diff --git a/include/proto/h1_htx.h b/include/proto/h1_htx.h index e77663b92..a8fd88675 100644 --- a/include/proto/h1_htx.h +++ b/include/proto/h1_htx.h @@ -34,6 +34,7 @@ int h1_parse_msg_data(struct h1m *h1m, struct htx **dsthtx, struct buffer *htxbuf); int h1_parse_msg_tlrs(struct h1m *h1m, struct htx *dsthtx, struct buffer *srcbuf, size_t ofs, size_t max); +int h1_parse_msg_eom(struct h1m *h1m, struct htx *dsthtx, size_t max); int h1_format_htx_reqline(const struct htx_sl *sl, struct buffer *chk); int h1_format_htx_stline(const struct htx_sl *sl, struct buffer *chk); diff --git a/src/h1_htx.c b/src/h1_htx.c index 48bfae544..af18a98b3 100644 --- a/src/h1_htx.c +++ b/src/h1_htx.c @@ -381,6 +381,11 @@ int h1_parse_msg_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx *dsthtx, ret = 0; } + /* Switch messages without any payload to DONE state */ + if (((h1m->flags & H1_MF_CLEN) && h1m->body_len == 0) || + ((h1m->flags & (H1_MF_XFER_LEN|H1_MF_CLEN|H1_MF_CHNK)) == H1_MF_XFER_LEN)) + h1m->state = H1_MSG_DONE; + end: return ret; error: @@ -465,11 +470,8 @@ int h1_parse_msg_data(struct h1m *h1m, struct htx **dsthtx, goto end; } - if (!h1m->curr_len) { - if (max < sizeof(struct htx_blk) + 1 || !htx_add_endof(*dsthtx, HTX_BLK_EOM)) - goto end; + if (!h1m->curr_len) h1m->state = H1_MSG_DONE; - } } else if (h1m->flags & H1_MF_CHNK) { /* te:chunked : parse chunks */ @@ -524,8 +526,6 @@ int h1_parse_msg_data(struct h1m *h1m, struct htx **dsthtx, /* XFER_LEN is set but not CLEN nor CHNK, it means there is no * body. Switch the message in DONE state */ - if (max < sizeof(struct htx_blk) + 1 || !htx_add_endof(*dsthtx, HTX_BLK_EOM)) - goto end; h1m->state = H1_MSG_DONE; } else { @@ -594,6 +594,8 @@ int h1_parse_msg_tlrs(struct h1m *h1m, struct htx *dsthtx, if (!htx_add_all_trailers(dsthtx, hdrs)) goto error; + h1m->state = H1_MSG_DONE; + end: return ret; error: @@ -603,6 +605,23 @@ int h1_parse_msg_tlrs(struct h1m *h1m, struct htx *dsthtx, return 0; } +/* Finish HTTP/1 parsing by adding the HTX EOM block. It returns 1 on success or + * 0 if it couldn't proceed. There is no parsing at this stage, but a parsing + * error is reported if the message state is not H1_MSG_DONE. */ +int h1_parse_msg_eom(struct h1m *h1m, struct htx *dsthtx, size_t max) +{ + if (h1m->state != H1_MSG_DONE) { + h1m->err_state = h1m->state; + h1m->err_pos = h1m->next; + dsthtx->flags |= HTX_FL_PARSING_ERROR; + return 0; + } + if (max < sizeof(struct htx_blk) + 1 || !htx_add_endof(dsthtx, HTX_BLK_EOM)) + return 0; + + return 1; +} + /* Appends the H1 representation of the request line to the chunk . It * returns 1 if data are successfully appended, otherwise it returns 0. diff --git a/src/mux_fcgi.c b/src/mux_fcgi.c index f8ee0b7e4..2290a48d5 100644 --- a/src/mux_fcgi.c +++ b/src/mux_fcgi.c @@ -145,8 +145,7 @@ enum fcgi_strm_st { #define FCGI_SF_KILL_CONN 0x00004000 /* kill the whole connection with this stream */ /* Other flags */ -#define FCGI_SF_HAVE_I_TLR 0x00010000 /* Set during input process to know the trailers were processed */ -#define FCGI_SF_APPEND_EOM 0x00020000 /* Send EOM to the HTX buffer */ +#define FCGI_SF_H1_PARSING_DONE 0x00010000 /* FCGI stream descriptor */ struct fcgi_strm { @@ -2736,7 +2735,7 @@ static int fcgi_recv(struct fcgi_conn *fconn) conn->xprt->subscribe(conn, conn->xprt_ctx, SUB_RETRY_RECV, &fconn->wait_event); } else - TRACE_DATA("send data", FCGI_EV_FCONN_RECV, conn,,, (size_t[]){ret}); + TRACE_DATA("recv data", FCGI_EV_FCONN_RECV, conn,,, (size_t[]){ret}); if (!b_data(buf)) { fcgi_release_buf(fconn, &fconn->dbuf); @@ -3168,7 +3167,7 @@ static size_t fcgi_strm_parse_data(struct fcgi_strm *fstrm, struct h1m *h1m, str TRACE_ENTER(FCGI_EV_RSP_DATA|FCGI_EV_RSP_BODY, fstrm->fconn->conn, fstrm,, (size_t[]){max}); ret = h1_parse_msg_data(h1m, htx, buf, *ofs, max, htxbuf); - if (ret <= 0) { + if (!ret) { TRACE_DEVEL("leaving on missing data or error", FCGI_EV_RSP_DATA|FCGI_EV_RSP_BODY, fstrm->fconn->conn, fstrm); if ((*htx)->flags & HTX_FL_PARSING_ERROR) { TRACE_USER("rejected H1 response", FCGI_EV_RSP_DATA|FCGI_EV_RSP_BODY|FCGI_EV_FSTRM_ERR, fstrm->fconn->conn, fstrm); @@ -3179,14 +3178,6 @@ static size_t fcgi_strm_parse_data(struct fcgi_strm *fstrm, struct h1m *h1m, str } *ofs += ret; end: - if (h1m->state == H1_MSG_DONE) { - fstrm->flags &= ~FCGI_SF_APPEND_EOM; - TRACE_STATE("end of message", FCGI_EV_RSP_DATA|FCGI_EV_RSP_EOM, fstrm->fconn->conn); - } - else if (h1m->state == H1_MSG_DATA && (h1m->flags & H1_MF_XFER_LEN) && h1m->curr_len == 0) { - fstrm->flags |= FCGI_SF_APPEND_EOM; - TRACE_STATE("add append_eom", FCGI_EV_RSP_DATA, fstrm->fconn->conn); - } TRACE_LEAVE(FCGI_EV_RSP_DATA|FCGI_EV_RSP_BODY, fstrm->fconn->conn, fstrm,, (size_t[]){ret}); return ret; } @@ -3198,7 +3189,7 @@ static size_t fcgi_strm_parse_trailers(struct fcgi_strm *fstrm, struct h1m *h1m, TRACE_ENTER(FCGI_EV_RSP_DATA|FCGI_EV_RSP_TLRS, fstrm->fconn->conn, fstrm,, (size_t[]){max}); ret = h1_parse_msg_tlrs(h1m, htx, buf, *ofs, max); - if (ret <= 0) { + if (!ret) { TRACE_DEVEL("leaving on missing data or error", FCGI_EV_RSP_DATA|FCGI_EV_RSP_TLRS, fstrm->fconn->conn, fstrm); if (htx->flags & HTX_FL_PARSING_ERROR) { TRACE_USER("rejected H1 response", FCGI_EV_RSP_DATA|FCGI_EV_RSP_TLRS|FCGI_EV_FSTRM_ERR, fstrm->fconn->conn, fstrm); @@ -3208,27 +3199,31 @@ static size_t fcgi_strm_parse_trailers(struct fcgi_strm *fstrm, struct h1m *h1m, goto end; } *ofs += ret; - fstrm->flags |= FCGI_SF_HAVE_I_TLR; end: TRACE_LEAVE(FCGI_EV_RSP_DATA|FCGI_EV_RSP_TLRS, fstrm->fconn->conn, fstrm,, (size_t[]){ret}); return ret; } static size_t fcgi_strm_add_eom(struct fcgi_strm *fstrm, struct h1m *h1m, struct htx *htx, - size_t max) + struct buffer *buf, size_t *ofs, size_t max) { - TRACE_ENTER(FCGI_EV_RSP_DATA, fstrm->fconn->conn, fstrm,, (size_t[]){max}); - if (max < sizeof(struct htx_blk) + 1 || !htx_add_endof(htx, HTX_BLK_EOM)) { - fstrm->flags |= FCGI_SF_APPEND_EOM; - TRACE_STATE("leaving on append_eom", FCGI_EV_RSP_DATA, fstrm->fconn->conn); - return 0; - } + int ret; - h1m->state = H1_MSG_DONE; - fstrm->flags &= ~FCGI_SF_APPEND_EOM; - TRACE_STATE("end of response", FCGI_EV_RSP_DATA|FCGI_EV_RSP_EOM, fstrm->fconn->conn, fstrm); - TRACE_LEAVE(FCGI_EV_RSP_DATA, fstrm->fconn->conn, fstrm); - return (sizeof(struct htx_blk) + 1); + TRACE_ENTER(FCGI_EV_RSP_DATA||FCGI_EV_RSP_EOM, fstrm->fconn->conn, fstrm,, (size_t[]){max}); + ret = h1_parse_msg_eom(h1m, htx, max); + if (!ret) { + TRACE_DEVEL("leaving on missing data or error", FCGI_EV_RSP_DATA|FCGI_EV_RSP_EOM, fstrm->fconn->conn, fstrm); + if (htx->flags & HTX_FL_PARSING_ERROR) { + TRACE_USER("rejected H1 response", FCGI_EV_RSP_DATA|FCGI_EV_RSP_EOM|FCGI_EV_FSTRM_ERR, fstrm->fconn->conn, fstrm); + fcgi_strm_error(fstrm); + fcgi_strm_capture_bad_message(fstrm->fconn, fstrm, h1m, buf); + } + goto end; + } + fstrm->flags |= FCGI_SF_H1_PARSING_DONE; + end: + TRACE_LEAVE(FCGI_EV_RSP_DATA|FCGI_EV_RSP_EOM, fstrm->fconn->conn, fstrm,, (size_t[]){ret}); + return ret; } static size_t fcgi_strm_parse_response(struct fcgi_strm *fstrm, struct buffer *buf, size_t count) @@ -3270,30 +3265,27 @@ static size_t fcgi_strm_parse_response(struct fcgi_strm *fstrm, struct buffer *b else if (h1m->state < H1_MSG_TRAILERS) { TRACE_PROTO("parsing response payload", FCGI_EV_RSP_DATA|FCGI_EV_RSP_BODY, fconn->conn, fstrm); ret = fcgi_strm_parse_data(fstrm, h1m, &htx, &fstrm->rxbuf, &total, count, buf); - if (!ret) + if (!ret && h1m->state != H1_MSG_DONE) break; TRACE_PROTO("rcvd response payload data", FCGI_EV_RSP_DATA|FCGI_EV_RSP_BODY, fconn->conn, fstrm, htx); - - if (h1m->state == H1_MSG_DONE) - TRACE_USER("H1 response fully rcvd", FCGI_EV_RSP_DATA|FCGI_EV_RSP_EOM, fconn->conn, fstrm, htx); } else if (h1m->state == H1_MSG_TRAILERS) { - if (!(fstrm->flags & FCGI_SF_HAVE_I_TLR)) { - TRACE_PROTO("parsing response trailers", FCGI_EV_RSP_DATA|FCGI_EV_RSP_TLRS, fconn->conn, fstrm); - ret = fcgi_strm_parse_trailers(fstrm, h1m, htx, &fstrm->rxbuf, &total, count); - if (!ret) - break; - - TRACE_PROTO("rcvd H1 response trailers", FCGI_EV_RSP_DATA|FCGI_EV_RSP_TLRS, fconn->conn, fstrm, htx); - } - else if (!fcgi_strm_add_eom(fstrm, h1m, htx, count)) + TRACE_PROTO("parsing response trailers", FCGI_EV_RSP_DATA|FCGI_EV_RSP_TLRS, fconn->conn, fstrm); + ret = fcgi_strm_parse_trailers(fstrm, h1m, htx, &fstrm->rxbuf, &total, count); + if (!ret && h1m->state != H1_MSG_DONE) break; - if (h1m->state == H1_MSG_DONE) - TRACE_USER("H1 response fully rcvd", FCGI_EV_RSP_DATA|FCGI_EV_RSP_EOM, fconn->conn, fstrm, htx); + TRACE_PROTO("rcvd H1 response trailers", FCGI_EV_RSP_DATA|FCGI_EV_RSP_TLRS, fconn->conn, fstrm, htx); } else if (h1m->state == H1_MSG_DONE) { + if (!(fstrm->flags & FCGI_SF_H1_PARSING_DONE)) { + if (!fcgi_strm_add_eom(fstrm, h1m, htx, &fstrm->rxbuf, &total, count)) + break; + + TRACE_USER("H1 response fully rcvd", FCGI_EV_RSP_DATA|FCGI_EV_RSP_EOM, fconn->conn, fstrm, htx); + } + if (b_data(&fstrm->rxbuf) > total) { htx->flags |= HTX_FL_PARSING_ERROR; TRACE_PROTO("too much data, parsing error", FCGI_EV_RSP_DATA, fconn->conn, fstrm); @@ -3304,21 +3296,16 @@ static size_t fcgi_strm_parse_response(struct fcgi_strm *fstrm, struct buffer *b else if (h1m->state == H1_MSG_TUNNEL) { TRACE_PROTO("parsing response tunneled data", FCGI_EV_RSP_DATA, fconn->conn, fstrm); ret = fcgi_strm_parse_data(fstrm, h1m, &htx, &fstrm->rxbuf, &total, count, buf); + if (fstrm->state != FCGI_SS_ERROR && (fstrm->flags & FCGI_SF_ES_RCVD) && b_data(&fstrm->rxbuf) == total) { TRACE_DEVEL("end of tunneled data", FCGI_EV_RSP_DATA, fconn->conn, fstrm); - if ((h1m->flags & (H1_MF_VER_11|H1_MF_XFER_LEN)) == H1_MF_VER_11) { - if (!fcgi_strm_add_eom(fstrm, h1m, htx, count)) - break; - TRACE_USER("H1 response fully rcvd", FCGI_EV_RSP_DATA|FCGI_EV_RSP_EOM, fconn->conn, fstrm, htx); - } - else { - h1m->state = H1_MSG_DONE; - TRACE_USER("H1 response fully rcvd", FCGI_EV_RSP_DATA|FCGI_EV_RSP_EOM, fconn->conn, fstrm, htx); - break; - } + if ((h1m->flags & (H1_MF_VER_11|H1_MF_XFER_LEN)) != H1_MF_VER_11) + fstrm->flags |= FCGI_SF_H1_PARSING_DONE; + h1m->state = H1_MSG_DONE; + TRACE_USER("H1 response fully rcvd", FCGI_EV_RSP_DATA|FCGI_EV_RSP_EOM, fconn->conn, fstrm, htx); } - if (!ret) + if (!ret && h1m->state != H1_MSG_DONE) break; TRACE_PROTO("rcvd H1 response tunneled data", FCGI_EV_RSP_DATA, fconn->conn, fstrm, htx); @@ -3793,11 +3780,11 @@ static size_t fcgi_rcv_buf(struct conn_stream *cs, struct buffer *buf, size_t co else TRACE_STATE("fstrm rxbuf not allocated", FCGI_EV_STRM_RECV|FCGI_EV_FSTRM_BLK, fconn->conn, fstrm); - if (b_data(&fstrm->rxbuf) || (fstrm->flags & FCGI_SF_APPEND_EOM)) + if (b_data(&fstrm->rxbuf) || (fstrm->h1m.state == H1_MSG_DONE && !(fstrm->flags & FCGI_SF_H1_PARSING_DONE))) cs->flags |= (CS_FL_RCV_MORE | CS_FL_WANT_ROOM); else { cs->flags &= ~(CS_FL_RCV_MORE | CS_FL_WANT_ROOM); - if (fstrm->state == FCGI_SS_ERROR || fstrm->h1m.state == H1_MSG_DONE) { + if (fstrm->state == FCGI_SS_ERROR || (fstrm->flags & FCGI_SF_H1_PARSING_DONE)) { cs->flags |= CS_FL_EOI; if (!(fstrm->h1m.flags & (H1_MF_VER_11|H1_MF_XFER_LEN))) cs->flags |= CS_FL_EOS; diff --git a/src/mux_h1.c b/src/mux_h1.c index b44204845..3a0af224f 100644 --- a/src/mux_h1.c +++ b/src/mux_h1.c @@ -72,9 +72,8 @@ #define H1S_F_NOT_FIRST 0x00000080 /* The H1 stream is not the first one */ #define H1S_F_BUF_FLUSH 0x00000100 /* Flush input buffer and don't read more data */ #define H1S_F_SPLICED_DATA 0x00000200 /* Set when the kernel splicing is in used */ -#define H1S_F_HAVE_I_TLR 0x00000800 /* Set during input process to know the trailers were processed */ -#define H1S_F_APPEND_EOM 0x00001000 /* Send EOM to the HTX buffer */ -/* 0x00002000 .. 0x00001000 unused */ +#define H1S_F_PARSING_DONE 0x00000400 /* Set when incoming message parsing is finished (EOM added) */ +/* 0x00000800 .. 0x00001000 unused */ #define H1S_F_HAVE_SRV_NAME 0x00002000 /* Set during output process if the server name header was added to the request */ #define H1S_F_HAVE_O_CONN 0x00004000 /* Set during output process to know connection mode was processed */ @@ -468,7 +467,7 @@ static inline size_t h1s_data_pending(const struct h1s *h1s) h1m = conn_is_back(h1s->h1c->conn) ? &h1s->res : &h1s->req; if (h1m->state == H1_MSG_DONE) - return 0; // data not for this stream (e.g. pipelining) + return !(h1s->flags & H1S_F_PARSING_DONE); return b_data(&h1s->h1c->ibuf); } @@ -612,7 +611,7 @@ static void h1s_destroy(struct h1s *h1s) if (!(h1c->flags & (H1C_F_CS_ERROR|H1C_F_CS_SHUTW_NOW|H1C_F_CS_SHUTDOWN)) && /* No error/shutdown on h1c */ !(h1c->conn->flags & (CO_FL_ERROR|CO_FL_SOCK_RD_SH|CO_FL_SOCK_WR_SH)) && /* No error/shutdown on conn */ - (h1s->flags & H1S_F_WANT_KAL) && /* K/A possible */ + (h1s->flags & (H1S_F_WANT_KAL|H1S_F_PARSING_DONE)) == (H1S_F_WANT_KAL|H1S_F_PARSING_DONE) && /* K/A possible */ h1s->req.state == H1_MSG_DONE && h1s->res.state == H1_MSG_DONE) { /* req/res in DONE state */ h1c->flags |= (H1C_F_CS_IDLE|H1C_F_WAIT_NEXT_REQ); TRACE_STATE("set idle mode on h1c, waiting for the next request", H1_EV_H1C_ERR, h1c->conn, h1s); @@ -1119,9 +1118,12 @@ static void h1_set_req_tunnel_mode(struct h1s *h1s) 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 (!conn_is_back(h1s->h1c->conn) && h1s->res.state < H1_MSG_DONE) { - h1s->h1c->flags |= H1C_F_IN_BUSY; - TRACE_STATE("switch h1c in busy mode", H1_EV_RX_DATA|H1_EV_H1C_BLK, h1s->h1c->conn, h1s); + if (!conn_is_back(h1s->h1c->conn)) { + h1s->flags &= ~H1S_F_PARSING_DONE; + if (h1s->res.state < H1_MSG_DONE) { + h1s->h1c->flags |= H1C_F_IN_BUSY; + TRACE_STATE("switch h1c in busy mode", H1_EV_RX_DATA|H1_EV_H1C_BLK, h1s->h1c->conn, h1s); + } } else if (h1s->h1c->flags & H1C_F_IN_BUSY) { h1s->h1c->flags &= ~H1C_F_IN_BUSY; @@ -1139,23 +1141,26 @@ static void h1_set_req_tunnel_mode(struct h1s *h1s) */ static void h1_set_res_tunnel_mode(struct h1s *h1s) { - /* On protocol switching, switch the request to tunnel mode if it is in - * DONE state. Otherwise we will wait the end of the request to switch - * it in tunnel mode. - */ - if (h1s->status == 101 && h1s->req.state == H1_MSG_DONE) { - 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); - } h1s->res.flags &= ~(H1_MF_XFER_LEN|H1_MF_CLEN|H1_MF_CHNK); h1s->res.state = H1_MSG_TUNNEL; TRACE_STATE("switch H1 response in tunnel mode", H1_EV_TX_DATA|H1_EV_TX_HDRS, h1s->h1c->conn, h1s); - if (conn_is_back(h1s->h1c->conn) && h1s->req.state < H1_MSG_DONE) { - h1s->h1c->flags |= H1C_F_IN_BUSY; - TRACE_STATE("switch h1c in busy mode", H1_EV_RX_DATA|H1_EV_H1C_BLK, h1s->h1c->conn, h1s); + if (conn_is_back(h1s->h1c->conn)) { + h1s->flags &= ~H1S_F_PARSING_DONE; + /* On protocol switching, switch the request to tunnel mode if it is in + * DONE state. Otherwise we will wait the end of the request to switch + * it in tunnel mode. + */ + if (h1s->req.state < H1_MSG_DONE) { + h1s->h1c->flags |= H1C_F_IN_BUSY; + TRACE_STATE("switch h1c in busy mode", H1_EV_RX_DATA|H1_EV_H1C_BLK, h1s->h1c->conn, h1s); + } + else if (h1s->status == 101 && h1s->req.state == H1_MSG_DONE) { + 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); + } } else if (h1s->h1c->flags & H1C_F_IN_BUSY) { h1s->h1c->flags &= ~H1C_F_IN_BUSY; @@ -1278,16 +1283,6 @@ static size_t h1_process_data(struct h1s *h1s, struct h1m *h1m, struct htx **htx *ofs += ret; end: - if (h1m->state == H1_MSG_DONE) { - h1s->flags &= ~H1S_F_APPEND_EOM; - h1s->cs->flags |= CS_FL_EOI; - TRACE_STATE("end of message", H1_EV_RX_DATA|H1_EV_RX_BODY|H1_EV_RX_EOI, h1s->h1c->conn); - } - else if (h1m->state == H1_MSG_DATA && (h1m->flags & H1_MF_XFER_LEN) && h1m->curr_len == 0) { - h1s->flags |= H1S_F_APPEND_EOM; - TRACE_STATE("add append_eom", H1_EV_RX_DATA, h1s->h1c->conn); - } - TRACE_LEAVE(H1_EV_RX_DATA|H1_EV_RX_BODY, h1s->h1c->conn, h1s,, (size_t[]){ret}); return ret; } @@ -1324,7 +1319,6 @@ static size_t h1_process_trailers(struct h1s *h1s, struct h1m *h1m, struct htx * } *ofs += ret; - h1s->flags |= H1S_F_HAVE_I_TLR; end: TRACE_LEAVE(H1_EV_RX_DATA|H1_EV_RX_TLRS, h1s->h1c->conn, h1s,, (size_t[]){ret}); @@ -1332,26 +1326,40 @@ static size_t h1_process_trailers(struct h1s *h1s, struct h1m *h1m, struct htx * } /* - * Add the EOM in the HTX message and switch the message to the DONE state. It - * returns the number of bytes parsed if > 0, or 0 if iet couldn't proceed. This - * functions is responsible to update the parser state . It also add the - * flag CS_FL_EOI on the CS. + * Add the EOM in the HTX message. It returns 1 on success or 0 if it couldn't + * proceed. This functions is responsible to update the parser state . It + * also add the flag CS_FL_EOI on the CS. */ -static size_t h1_process_eom(struct h1s *h1s, struct h1m *h1m, struct htx *htx, size_t max) +static size_t h1_process_eom(struct h1s *h1s, struct h1m *h1m, struct htx *htx, + struct buffer *buf, size_t *ofs, size_t max) { - TRACE_ENTER(H1_EV_RX_DATA, h1s->h1c->conn, h1s,, (size_t[]){max}); - if (max < sizeof(struct htx_blk) + 1 || !htx_add_endof(htx, HTX_BLK_EOM)) { - h1s->flags |= H1S_F_APPEND_EOM; - TRACE_STATE("leaving on append_eom", H1_EV_RX_DATA, h1s->h1c->conn); - return 0; + int ret; + + TRACE_ENTER(H1_EV_RX_DATA|H1_EV_RX_EOI, h1s->h1c->conn, h1s,, (size_t[]){max}); + ret = h1_parse_msg_eom(h1m, htx, max); + if (!ret) { + TRACE_DEVEL("leaving on missing data or error", H1_EV_RX_DATA|H1_EV_RX_EOI, h1s->h1c->conn, h1s); + if (htx->flags & HTX_FL_PARSING_ERROR) { + if (!(h1m->flags & H1_MF_RESP)) { + h1s->flags |= H1S_F_REQ_ERROR; + TRACE_USER("rejected H1 request", H1_EV_RX_DATA|H1_EV_RX_EOI|H1_EV_H1S_ERR, h1s->h1c->conn, h1s); + } + else { + h1s->flags |= H1S_F_RES_ERROR; + TRACE_USER("rejected H1 response", H1_EV_RX_DATA|H1_EV_RX_EOI|H1_EV_H1S_ERR, h1s->h1c->conn, h1s); + } + h1s->cs->flags |= CS_FL_EOI; + TRACE_STATE("parsing error", H1_EV_RX_DATA|H1_EV_RX_EOI|H1_EV_H1S_ERR, h1s->h1c->conn, h1s); + h1_capture_bad_message(h1s->h1c, h1s, h1m, buf); + } + goto end; } - h1s->flags &= ~H1S_F_APPEND_EOM; - h1m->state = H1_MSG_DONE; + h1s->flags |= H1S_F_PARSING_DONE; h1s->cs->flags |= CS_FL_EOI; - TRACE_STATE("end of message", H1_EV_RX_DATA|H1_EV_RX_EOI, h1s->h1c->conn, h1s); - TRACE_LEAVE(H1_EV_RX_DATA, h1s->h1c->conn, h1s); - return (sizeof(struct htx_blk) + 1); + end: + TRACE_LEAVE(H1_EV_RX_DATA|H1_EV_RX_EOI, h1s->h1c->conn, h1s,, (size_t[]){ret}); + return ret; } /* @@ -1406,33 +1414,30 @@ static size_t h1_process_input(struct h1c *h1c, struct buffer *buf, size_t count else if (h1m->state < H1_MSG_TRAILERS) { TRACE_PROTO("parsing message payload", H1_EV_RX_DATA|H1_EV_RX_BODY, h1c->conn, h1s); ret = h1_process_data(h1s, h1m, &htx, &h1c->ibuf, &total, count, buf); - if (!ret) + if (!ret && h1m->state != H1_MSG_DONE) break; TRACE_PROTO((!(h1m->flags & H1_MF_RESP) ? "rcvd H1 request payload data" : "rcvd H1 response payload data"), H1_EV_RX_DATA|H1_EV_RX_BODY, h1c->conn, h1s, htx, (size_t[]){ret}); - - if (h1m->state == H1_MSG_DONE) - TRACE_USER((!(h1m->flags & H1_MF_RESP) ? "H1 request fully rcvd" : "H1 response fully rcvd"), - H1_EV_RX_DATA, h1c->conn, h1s, htx); } else if (h1m->state == H1_MSG_TRAILERS) { - if (!(h1s->flags & H1S_F_HAVE_I_TLR)) { - TRACE_PROTO("parsing message trailers", H1_EV_RX_DATA|H1_EV_RX_TLRS, h1c->conn, h1s); - ret = h1_process_trailers(h1s, h1m, htx, &h1c->ibuf, &total, count); - if (!ret) - break; - - TRACE_PROTO((!(h1m->flags & H1_MF_RESP) ? "rcvd H1 request trailers" : "rcvd H1 response trailers"), - H1_EV_RX_DATA|H1_EV_RX_TLRS, h1c->conn, h1s, htx, (size_t[]){ret}); - } - else if (!h1_process_eom(h1s, h1m, htx, count)) + TRACE_PROTO("parsing message trailers", H1_EV_RX_DATA|H1_EV_RX_TLRS, h1c->conn, h1s); + ret = h1_process_trailers(h1s, h1m, htx, &h1c->ibuf, &total, count); + if (!ret && h1m->state != H1_MSG_DONE) break; - TRACE_USER((!(h1m->flags & H1_MF_RESP) ? "H1 request fully rcvd" : "H1 response fully rcvd"), - H1_EV_RX_DATA|H1_EV_RX_EOI, h1c->conn, h1s, htx); + TRACE_PROTO((!(h1m->flags & H1_MF_RESP) ? "rcvd H1 request trailers" : "rcvd H1 response trailers"), + H1_EV_RX_DATA|H1_EV_RX_TLRS, h1c->conn, h1s, htx, (size_t[]){ret}); } else if (h1m->state == H1_MSG_DONE) { + if (!(h1s->flags & H1S_F_PARSING_DONE)) { + if (!h1_process_eom(h1s, h1m, htx, &h1c->ibuf, &total, count)) + break; + + TRACE_USER((!(h1m->flags & H1_MF_RESP) ? "H1 request fully rcvd" : "H1 response fully rcvd"), + H1_EV_RX_DATA|H1_EV_RX_EOI, h1c->conn, h1s, htx); + } + if (!(h1m->flags & H1_MF_RESP) && h1s->status == 101) h1_set_req_tunnel_mode(h1s); else if (h1s->req.state < H1_MSG_DONE || h1s->res.state < H1_MSG_DONE) { @@ -1478,12 +1483,9 @@ static size_t h1_process_input(struct h1c *h1c, struct buffer *buf, size_t count if (!b_data(&h1c->ibuf)) h1_release_buf(h1c, &h1c->ibuf); - - if ((h1s_data_pending(h1s) && !htx_is_empty(htx)) || (h1s->flags & H1S_F_APPEND_EOM)) + if (h1s_data_pending(h1s)) h1s->cs->flags |= CS_FL_RCV_MORE | CS_FL_WANT_ROOM; - - if (((h1s->flags & (H1S_F_REOS|H1S_F_APPEND_EOM)) == H1S_F_REOS) && - (!h1s_data_pending(h1s) || htx_is_empty(htx))) { + else if (h1s->flags & H1S_F_REOS) { h1s->cs->flags |= CS_FL_EOS; if (h1m->state == H1_MSG_TUNNEL) h1s->cs->flags |= CS_FL_EOI;