MEDIUM: mux-h1: Properly handle tunnel establishments and aborts
In the same way than the H2 mux, we now bloc data sending on the server side if a tunnel is not fully established. In addition, if some data are still pending for a aborted tunnel, an error is triggered and the server connection is closed. To do so, we rely on the H1C_F_WAIT_INPUT flag to bloc the output processing. This patch contributes to fix the tunnel mode between the H1 and the H2 muxes.
This commit is contained in:
parent
91b21dc8d8
commit
dea2474991
88
src/mux_h1.c
88
src/mux_h1.c
@ -1318,16 +1318,30 @@ static void h1_set_res_tunnel_mode(struct h1s *h1s)
|
||||
h1s->h1c->flags |= H1C_F_WAIT_OUTPUT;
|
||||
TRACE_STATE("Disable read on h1c (wait_output)", H1_EV_RX_DATA|H1_EV_H1C_BLK, h1s->h1c->conn, h1s);
|
||||
}
|
||||
else if (h1s->status == 101 && h1s->req.state == H1_MSG_DONE) {
|
||||
else {
|
||||
h1s->req.flags &= ~(H1_MF_XFER_LEN|H1_MF_CLEN|H1_MF_CHNK);
|
||||
h1s->req.state = H1_MSG_TUNNEL;
|
||||
TRACE_STATE("switch H1 request in tunnel mode", H1_EV_TX_DATA|H1_EV_TX_HDRS, h1s->h1c->conn, h1s);
|
||||
|
||||
if (h1s->h1c->flags & H1C_F_WAIT_INPUT) {
|
||||
h1s->h1c->flags &= ~H1C_F_WAIT_INPUT;
|
||||
h1_wake_stream_for_send(h1s);
|
||||
if (b_data(&h1s->h1c->obuf))
|
||||
tasklet_wakeup(h1s->h1c->wait_event.tasklet);
|
||||
TRACE_STATE("Re-enable send on h1c", H1_EV_TX_DATA|H1_EV_H1C_BLK|H1_EV_H1C_WAKE, h1s->h1c->conn, h1s);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (h1s->h1c->flags & H1C_F_WAIT_OUTPUT) {
|
||||
h1s->h1c->flags &= ~H1C_F_WAIT_OUTPUT;
|
||||
tasklet_wakeup(h1s->h1c->wait_event.tasklet);
|
||||
TRACE_STATE("Re-enable read on h1c", H1_EV_RX_DATA|H1_EV_H1C_BLK|H1_EV_H1C_WAKE, h1s->h1c->conn, h1s);
|
||||
else {
|
||||
h1s->req.flags &= ~(H1_MF_XFER_LEN|H1_MF_CLEN|H1_MF_CHNK);
|
||||
h1s->req.state = H1_MSG_TUNNEL;
|
||||
TRACE_STATE("switch H1 request in tunnel mode", H1_EV_TX_DATA|H1_EV_TX_HDRS, h1s->h1c->conn, h1s);
|
||||
|
||||
if (h1s->h1c->flags & H1C_F_WAIT_OUTPUT) {
|
||||
h1s->h1c->flags &= ~H1C_F_WAIT_OUTPUT;
|
||||
tasklet_wakeup(h1s->h1c->wait_event.tasklet);
|
||||
TRACE_STATE("Re-enable read on h1c", H1_EV_RX_DATA|H1_EV_H1C_BLK|H1_EV_H1C_WAKE, h1s->h1c->conn, h1s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1713,6 +1727,9 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun
|
||||
if (h1s->flags & H1S_F_PROCESSING_ERROR)
|
||||
goto end;
|
||||
|
||||
if (h1c->flags & H1C_F_WAIT_INPUT)
|
||||
goto end;
|
||||
|
||||
if (!h1_get_buf(h1c, &h1c->obuf)) {
|
||||
h1c->flags |= H1C_F_OUT_ALLOC;
|
||||
TRACE_STATE("waiting for h1c obuf allocation", H1_EV_TX_DATA|H1_EV_H1S_BLK, h1c->conn, h1s);
|
||||
@ -1741,7 +1758,8 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun
|
||||
* the HTX blocks.
|
||||
*/
|
||||
if (!b_data(&h1c->obuf)) {
|
||||
if (htx_nbblks(chn_htx) == 1 &&
|
||||
if ((h1m->state == H1_MSG_DATA || h1m->state == H1_MSG_TUNNEL) &&
|
||||
htx_nbblks(chn_htx) == 1 &&
|
||||
htx_get_blk_type(blk) == HTX_BLK_DATA &&
|
||||
htx_get_blk_value(chn_htx, blk).len == count) {
|
||||
void *old_area = h1c->obuf.area;
|
||||
@ -1782,7 +1800,7 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun
|
||||
|
||||
tmp.data = 0;
|
||||
tmp.size = b_room(&h1c->obuf);
|
||||
while (count && !(h1s->flags & H1S_F_PROCESSING_ERROR) && blk) {
|
||||
while (count && !(h1s->flags & H1S_F_PROCESSING_ERROR) && !(h1c->flags & H1C_F_WAIT_INPUT) && blk) {
|
||||
struct htx_sl *sl;
|
||||
struct ist n, v;
|
||||
enum htx_blk_type type = htx_get_blk_type(blk);
|
||||
@ -1948,16 +1966,11 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun
|
||||
H1_EV_TX_DATA|H1_EV_TX_HDRS, h1c->conn, h1s);
|
||||
|
||||
if (!(h1m->flags & H1_MF_RESP) && h1s->meth == HTTP_METH_CONNECT) {
|
||||
/* a CONNECT request is sent to the server. Switch it to tunnel mode. */
|
||||
h1_set_req_tunnel_mode(h1s);
|
||||
goto done;
|
||||
}
|
||||
else if ((h1m->flags & H1_MF_RESP) &&
|
||||
((h1s->meth == HTTP_METH_CONNECT && h1s->status >= 200 && h1s->status < 300) || h1s->status == 101)) {
|
||||
/* a successful reply to a CONNECT or a protocol switching is sent
|
||||
* to the client. Switch the response to tunnel mode.
|
||||
*/
|
||||
h1_set_res_tunnel_mode(h1s);
|
||||
TRACE_STATE("switch H1 response in tunnel mode", H1_EV_TX_DATA|H1_EV_TX_HDRS, h1c->conn, h1s);
|
||||
goto done;
|
||||
}
|
||||
else if ((h1m->flags & H1_MF_RESP) &&
|
||||
h1s->status < 200 && (h1s->status == 100 || h1s->status >= 102)) {
|
||||
@ -2066,9 +2079,17 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun
|
||||
goto error;
|
||||
done:
|
||||
h1m->state = H1_MSG_DONE;
|
||||
if (!(h1m->flags & H1_MF_RESP) && h1s->status == 101) {
|
||||
h1_set_req_tunnel_mode(h1s);
|
||||
TRACE_STATE("switch H1 request in tunnel mode", H1_EV_TX_DATA|H1_EV_TX_HDRS, h1c->conn, h1s);
|
||||
if (!(h1m->flags & H1_MF_RESP) && h1s->meth == HTTP_METH_CONNECT) {
|
||||
h1c->flags |= H1C_F_WAIT_INPUT;
|
||||
TRACE_STATE("Disable send on h1c (wait_input)", H1_EV_TX_DATA|H1_EV_H1C_BLK, h1c->conn, h1s);
|
||||
}
|
||||
else if ((h1m->flags & H1_MF_RESP) &&
|
||||
((h1s->meth == HTTP_METH_CONNECT && h1s->status >= 200 && h1s->status < 300) || h1s->status == 101)) {
|
||||
/* a successful reply to a CONNECT or a protocol switching is sent
|
||||
* to the client. Switch the response to tunnel mode.
|
||||
*/
|
||||
h1_set_res_tunnel_mode(h1s);
|
||||
TRACE_STATE("switch H1 response in tunnel mode", H1_EV_TX_DATA|H1_EV_TX_HDRS, h1c->conn, h1s);
|
||||
}
|
||||
else if (h1s->h1c->flags & H1C_F_WAIT_OUTPUT) {
|
||||
h1s->h1c->flags &= ~H1C_F_WAIT_OUTPUT;
|
||||
@ -2086,6 +2107,7 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun
|
||||
/* Unexpected error during output processing */
|
||||
chn_htx->flags |= HTX_FL_PROCESSING_ERROR;
|
||||
h1s->flags |= H1S_F_PROCESSING_ERROR;
|
||||
h1c->flags |= H1C_F_ST_ERROR;
|
||||
TRACE_STATE("processing error, set error on h1c/h1s", H1_EV_H1C_ERR|H1_EV_H1S_ERR, h1c->conn, h1s);
|
||||
TRACE_DEVEL("unexpected error", H1_EV_TX_DATA|H1_EV_STRM_ERR, h1c->conn, h1s);
|
||||
break;
|
||||
@ -2113,18 +2135,24 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun
|
||||
|
||||
htx_to_buf(chn_htx, buf);
|
||||
out:
|
||||
/* Both the request and the response reached the DONE state. So set EOI
|
||||
* flag on the conn-stream. Most of time, the flag will already be set,
|
||||
* except for protocol upgrades.
|
||||
*/
|
||||
if (h1s->cs && h1s->req.state == H1_MSG_DONE && h1s->res.state == H1_MSG_DONE)
|
||||
h1s->cs->flags |= CS_FL_EOI;
|
||||
|
||||
if (!buf_room_for_htx_data(&h1c->obuf)) {
|
||||
TRACE_STATE("h1c obuf full", H1_EV_TX_DATA|H1_EV_H1S_BLK, h1c->conn, h1s);
|
||||
h1c->flags |= H1C_F_OUT_FULL;
|
||||
}
|
||||
end:
|
||||
/* Both the request and the response reached the DONE state. So set EOI
|
||||
* flag on the conn-stream. Most of time, the flag will already be set,
|
||||
* except for protocol upgrades. Report an error if data remains blocked
|
||||
* in the output buffer.
|
||||
*/
|
||||
if (h1s->req.state == H1_MSG_DONE && h1s->res.state == H1_MSG_DONE) {
|
||||
if (!htx_is_empty(chn_htx)) {
|
||||
h1c->flags |= H1C_F_ST_ERROR;
|
||||
TRACE_STATE("txn done but data waiting to be sent, set error on h1c", H1_EV_H1C_ERR, h1c->conn, h1s);
|
||||
}
|
||||
h1s->cs->flags |= CS_FL_EOI;
|
||||
}
|
||||
|
||||
TRACE_LEAVE(H1_EV_TX_DATA, h1c->conn, h1s, chn_htx, (size_t[]){total});
|
||||
return total;
|
||||
|
||||
@ -3180,6 +3208,12 @@ static size_t h1_snd_buf(struct conn_stream *cs, struct buffer *buf, size_t coun
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (h1c->flags & H1C_F_ST_ERROR) {
|
||||
cs->flags |= CS_FL_ERROR;
|
||||
TRACE_DEVEL("H1 connection is in error, leaving in error", H1_EV_STRM_SEND|H1_EV_H1C_ERR|H1_EV_H1S_ERR|H1_EV_STRM_ERR, h1c->conn, h1s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Inherit some flags from the upper layer */
|
||||
h1c->flags &= ~(H1C_F_CO_MSG_MORE|H1C_F_CO_STREAMER);
|
||||
if (flags & CO_SFL_MSG_MORE)
|
||||
@ -3205,6 +3239,12 @@ static size_t h1_snd_buf(struct conn_stream *cs, struct buffer *buf, size_t coun
|
||||
if ((h1c->wait_event.events & SUB_RETRY_SEND) || !h1_send(h1c))
|
||||
break;
|
||||
}
|
||||
|
||||
if (h1c->flags & H1C_F_ST_ERROR) {
|
||||
TRACE_DEVEL("reporting error to the app-layer stream", H1_EV_STRM_SEND|H1_EV_H1S_ERR|H1_EV_STRM_ERR, h1c->conn, h1s);
|
||||
cs->flags |= CS_FL_ERROR;
|
||||
}
|
||||
|
||||
h1_refresh_timeout(h1c);
|
||||
TRACE_LEAVE(H1_EV_STRM_SEND, h1c->conn, h1s, 0, (size_t[]){total});
|
||||
return total;
|
||||
|
Loading…
x
Reference in New Issue
Block a user