From b75b5eaf26c944e3406040bc2ae5e8f8ce0edc60 Mon Sep 17 00:00:00 2001 From: Christopher Faulet Date: Fri, 17 May 2019 08:37:28 +0200 Subject: [PATCH] MEDIUM: htx: 1xx messages are now part of the final reponses 1xx informational messages (all except 101) are now part of the HTTP reponse, semantically speaking. These messages are not followed by an EOM anymore, because a final reponse is always expected. All these parts can also be transferred to the channel in same time, if possible. The HTX response analyzer has been update to forward them in loop, as the legacy one. --- src/http_fetch.c | 2 +- src/mux_h1.c | 80 ++++++++++++++++++++++-------------------------- src/mux_h2.c | 2 +- src/proto_htx.c | 35 +++++---------------- 4 files changed, 47 insertions(+), 72 deletions(-) diff --git a/src/http_fetch.c b/src/http_fetch.c index 395e665e9d..05074d6eb4 100644 --- a/src/http_fetch.c +++ b/src/http_fetch.c @@ -203,7 +203,7 @@ struct htx *smp_prefetch_htx(struct sample *smp, struct channel *chn, int vol) if (msg->msg_state < HTTP_MSG_BODY) { /* Analyse not yet started */ - if (htx_is_empty(htx) || htx_get_tail_type(htx) < HTX_BLK_EOH) { + if (htx_is_empty(htx) || htx->sl_pos == -1) { /* Parsing is done by the mux, just wait */ smp->flags |= SMP_F_MAY_CHANGE; return NULL; diff --git a/src/mux_h1.c b/src/mux_h1.c index 947e1930c8..881501eb0c 100644 --- a/src/mux_h1.c +++ b/src/mux_h1.c @@ -932,21 +932,6 @@ static size_t h1_eval_htx_res_size(struct h1m *h1m, union h1_sl *h1sl, struct ht return sz; } -/* - * Handle 100-Continue responses or any other informational 1xx responses which - * is non-final. In such case, this function reset the response parser. It is - * the caller responsibility to call this function when appropriate. - */ -static void h1_handle_1xx_response(struct h1s *h1s, struct h1m *h1m) -{ - if ((h1m->flags & H1_MF_RESP) && h1m->state == H1_MSG_DONE && - h1s->status < 200 && (h1s->status == 100 || h1s->status >= 102)) { - h1m_init_res(&h1s->res); - h1m->flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR); - h1s->h1c->flags &= ~H1C_F_IN_BUSY; - } -} - /* * Parse HTTP/1 headers. It returns the number of bytes parsed if > 0, or 0 if * it couldn't proceed. Parsing errors are reported by setting H1S_F_*_ERROR @@ -1004,10 +989,6 @@ static size_t h1_process_headers(struct h1s *h1s, struct h1m *h1m, struct htx *h /* Switch CONNECT requests to tunnel mode */ h1_set_req_tunnel_mode(h1s); } - else if (!(h1m->flags & H1_MF_CHNK) && !h1m->body_len) { - /* Switch requests with no body to done. */ - h1m->state = H1_MSG_DONE; - } if (!h1_process_req_vsn(h1s, h1m, h1sl)) { h1m->err_pos = h1sl.rq.v.ptr - b_head(buf); @@ -1027,18 +1008,14 @@ static size_t h1_process_headers(struct h1s *h1s, struct h1m *h1m, struct htx *h else if ((h1s->meth == HTTP_METH_HEAD) || (h1s->status >= 100 && h1s->status < 200) || (h1s->status == 204) || (h1s->status == 304)) { - /* Switch responses without body to done. */ + /* Responses known to have no body. */ h1m->flags &= ~(H1_MF_CLEN|H1_MF_CHNK); h1m->flags |= H1_MF_XFER_LEN; h1m->curr_len = h1m->body_len = 0; - h1m->state = H1_MSG_DONE; } else if (h1m->flags & (H1_MF_CLEN|H1_MF_CHNK)) { - /* Responses with a known body length. Switch requests - * with no body to done. */ + /* Responses with a known body length. */ h1m->flags |= H1_MF_XFER_LEN; - if ((h1m->flags & H1_MF_CLEN) && !h1m->body_len) - h1m->state = H1_MSG_DONE; } else { /* Responses with an unknown body length */ @@ -1061,16 +1038,25 @@ static size_t h1_process_headers(struct h1s *h1s, struct h1m *h1m, struct htx *h flags |= HTX_SL_F_XFER_LEN; if (h1m->flags & H1_MF_CHNK) flags |= HTX_SL_F_CHNK; - else if (h1m->flags & H1_MF_CLEN) + else if (h1m->flags & H1_MF_CLEN) { flags |= HTX_SL_F_CLEN; - if (h1m->state == H1_MSG_DONE) + if (h1m->body_len == 0) + flags |= HTX_SL_F_BODYLESS; + } + else flags |= HTX_SL_F_BODYLESS; } used = htx_used_space(htx); if (!(h1m->flags & H1_MF_RESP)) { - if (h1_eval_htx_req_size(h1m, &h1sl, hdrs) > max) - goto error; + if (h1_eval_htx_req_size(h1m, &h1sl, hdrs) > max) { + if (htx_is_empty(htx)) + goto error; + h1m_init_req(h1m); + h1m->flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR); + ret = 0; + goto end; + } sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, h1sl.rq.m, h1sl.rq.u, h1sl.rq.v); if (!sl || !htx_add_all_headers(htx, hdrs)) @@ -1078,8 +1064,14 @@ static size_t h1_process_headers(struct h1s *h1s, struct h1m *h1m, struct htx *h sl->info.req.meth = h1s->meth; } else { - if (h1_eval_htx_res_size(h1m, &h1sl, hdrs) > max) - goto error; + if (h1_eval_htx_res_size(h1m, &h1sl, hdrs) > max) { + if (htx_is_empty(htx)) + goto error; + h1m_init_res(h1m); + h1m->flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR); + ret = 0; + goto end; + } flags |= HTX_SL_F_IS_RESP; sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, h1sl.st.v, h1sl.st.c, h1sl.st.r); @@ -1091,13 +1083,6 @@ static size_t h1_process_headers(struct h1s *h1s, struct h1m *h1m, struct htx *h /* Set bytes used in the HTX mesage for the headers now */ sl->hdrs_bytes = htx_used_space(htx) - used; - - if (h1m->state == H1_MSG_DONE) { - if (!htx_add_endof(htx, HTX_BLK_EOM)) - goto error; - h1s->cs->flags |= CS_FL_EOI; - } - h1_process_input_conn_mode(h1s, h1m, htx); /* If body length cannot be determined, set htx->extra to @@ -1113,6 +1098,8 @@ static size_t h1_process_headers(struct h1s *h1s, struct h1m *h1m, struct htx *h h1m->err_pos = h1m->next; vsn_error: h1s->flags |= (!(h1m->flags & H1_MF_RESP) ? H1S_F_REQ_ERROR : H1S_F_RES_ERROR); + h1s->cs->flags |= CS_FL_EOI; + htx->flags |= HTX_FL_PARSING_ERROR; h1_capture_bad_message(h1s->h1c, h1s, h1m, buf); ret = 0; goto end; @@ -1315,6 +1302,8 @@ static size_t h1_process_data(struct h1s *h1s, struct h1m *h1m, struct htx *htx, end: if (ret < 0) { h1s->flags |= (!(h1m->flags & H1_MF_RESP) ? H1S_F_REQ_ERROR : H1S_F_RES_ERROR); + h1s->cs->flags |= CS_FL_EOI; + htx->flags |= HTX_FL_PARSING_ERROR; h1m->err_state = h1m->state; h1m->err_pos = *ofs + max + ret; h1_capture_bad_message(h1s->h1c, h1s, h1m, buf); @@ -1359,6 +1348,11 @@ static size_t h1_process_input(struct h1c *h1c, struct buffer *buf, size_t count ret = h1_process_headers(h1s, h1m, htx, &h1c->ibuf, &total, count); if (!ret) break; + if ((h1m->flags & H1_MF_RESP) && + h1s->status < 200 && (h1s->status == 100 || h1s->status >= 102)) { + h1m_init_res(&h1s->res); + h1m->flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR); + } } else if (h1m->state <= H1_MSG_TRAILERS) { ret = h1_process_data(h1s, h1m, htx, &h1c->ibuf, &total, count, buf); @@ -1415,9 +1409,7 @@ static size_t h1_process_input(struct h1c *h1c, struct buffer *buf, size_t count parsing_err: b_reset(&h1c->ibuf); - htx->flags |= HTX_FL_PARSING_ERROR; htx_to_buf(htx, buf); - h1s->cs->flags |= CS_FL_EOI; return 0; } @@ -1628,6 +1620,11 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun * to the client . Switch the response to tunnel mode. */ h1_set_res_tunnel_mode(h1s); } + else if ((h1m->flags & H1_MF_RESP) && + h1s->status < 200 && (h1s->status == 100 || h1s->status >= 102)) { + h1m_init_res(&h1s->res); + h1m->flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR); + } else h1m->state = H1_MSG_DATA; break; @@ -1689,9 +1686,6 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun } } - if (htx_is_empty(chn_htx)) - h1_handle_1xx_response(h1s, h1m); - copy: /* when the output buffer is empty, tmp shares the same area so that we * only have to update pointers and lengths. diff --git a/src/mux_h2.c b/src/mux_h2.c index 55c7619c51..6bec578b62 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -3670,7 +3670,7 @@ next_frame: if (!(msgf & H2_MSGF_RSP_1XX)) *flags |= H2_SF_HEADERS_RCVD; - if ((h2c->dff & H2_F_HEADERS_END_STREAM) || (msgf & H2_MSGF_RSP_1XX)) { + if ((h2c->dff & H2_F_HEADERS_END_STREAM)) { /* Mark the end of message, either using EOM in HTX or with the * trailing CRLF after the end of trailers. Note that DATA_CHNK * is not set during headers with END_STREAM. For HTX trailers, diff --git a/src/proto_htx.c b/src/proto_htx.c index dbbd937bdf..953b49f98d 100644 --- a/src/proto_htx.c +++ b/src/proto_htx.c @@ -132,19 +132,7 @@ int htx_wait_for_request(struct stream *s, struct channel *req, int an_bit) * a timeout or connection reset is not counted as an error. However * a bad request is. */ - if (unlikely(htx_is_empty(htx) || htx_get_tail_type(htx) < HTX_BLK_EOH)) { - /* - * First catch invalid request because only part of headers have - * been transfered. Multiplexers have the responsibility to emit - * all headers at once. - */ - if (htx_is_not_empty(htx) || (s->si[0].flags & SI_FL_RXBLK_ROOM)) { - stream_inc_http_req_ctr(s); - stream_inc_http_err_ctr(s); - proxy_inc_fe_req_ctr(sess->fe); - goto return_bad_req; - } - + if (unlikely(htx_is_empty(htx) || htx->sl_pos == -1)) { if (htx->flags & HTX_FL_UPGRADE) goto failed_keep_alive; @@ -1481,17 +1469,8 @@ int htx_wait_for_response(struct stream *s, struct channel *rep, int an_bit) * we should only check for HTTP status there, and check I/O * errors somewhere else. */ - if (unlikely(co_data(rep) || htx_is_empty(htx) || htx_get_tail_type(htx) < HTX_BLK_EOH)) { - /* - * First catch invalid response because of a parsing error or - * because only part of headers have been transfered. - * Multiplexers have the responsibility to emit all headers at - * once. We must be sure to have forwarded all outgoing data - * first. - */ - if (!co_data(rep) && (htx_is_not_empty(htx) || (s->si[1].flags & SI_FL_RXBLK_ROOM))) - goto return_bad_res; - + next_one: + if (unlikely(htx_is_empty(htx) || htx->sl_pos == -1)) { /* 1: have we encountered a read error ? */ if (rep->flags & CF_READ_ERROR) { struct connection *conn = NULL; @@ -1719,18 +1698,20 @@ int htx_wait_for_response(struct stream *s, struct channel *rep, int an_bit) int32_t pos; FLT_STRM_CB(s, flt_http_reset(s, msg)); - for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) { + for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) { struct htx_blk *blk = htx_get_blk(htx, pos); enum htx_blk_type type = htx_get_blk_type(blk); c_adv(rep, htx_get_blksz(blk)); - if (type == HTX_BLK_EOM) + if (type == HTX_BLK_EOH) { + htx->sl_pos = htx_get_next(htx, pos); break; + } } msg->msg_state = HTTP_MSG_RPBEFORE; txn->status = 0; s->logs.t_data = -1; /* was not a response yet */ - return 0; + goto next_one; } /* -- 2.39.5