MEDIUM: filters/http: Move body parsing of HTTP messages in dedicated functions
Now body parsing is done in http_msg_forward_body and
http_msg_forward_chunked_body functions, regardless of whether we parse a
request or a response.
Parsing result is still handled in http_request_forward_body and
http_response_forward_body functions.
This patch will ease futur optimizations, mainly on filters.
diff --git a/src/proto_http.c b/src/proto_http.c
index fdb17b6..b25d18a 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -275,6 +275,9 @@
static int http_apply_redirect_rule(struct redirect_rule *rule, struct stream *s, struct http_txn *txn);
+static int http_msg_forward_body(struct stream *s, struct http_msg *msg);
+static int http_msg_forward_chunked_body(struct stream *s, struct http_msg *msg);
+
/* This function returns a reason associated with the HTTP status.
* This function never fails, a message is always returned.
*/
@@ -5462,16 +5465,9 @@
* an "Expect: 100-continue" header.
*/
if (msg->msg_state == HTTP_MSG_BODY) {
- /* The previous analysers guarantee that the state is somewhere
- * between MSG_BODY and the first MSG_DATA. So msg->sol and
- * msg->next are always correct.
- */
- if (msg->msg_state < HTTP_MSG_CHUNK_SIZE) {
- if (msg->flags & HTTP_MSGF_TE_CHNK)
- msg->msg_state = HTTP_MSG_CHUNK_SIZE;
- else
- msg->msg_state = HTTP_MSG_DATA;
- }
+ msg->msg_state = ((msg->flags & HTTP_MSGF_TE_CHNK)
+ ? HTTP_MSG_CHUNK_SIZE
+ : HTTP_MSG_DATA);
/* TODO/filters: when http-buffer-request option is set or if a
* rule on url_param exists, the first chunk size could be
@@ -5488,7 +5484,7 @@
if (!(s->res.flags & CF_READ_ATTACHED)) {
channel_auto_connect(req);
req->flags |= CF_WAKE_CONNECT;
- goto missing_data;
+ goto missing_data_or_waiting;
}
msg->flags &= ~HTTP_MSGF_WAIT_CONN;
}
@@ -5499,177 +5495,62 @@
if (req->to_forward) {
/* We can't process the buffer's contents yet */
req->flags |= CF_WAKE_WRITE;
- goto missing_data;
+ goto missing_data_or_waiting;
}
- while (1) {
- if (msg->msg_state == HTTP_MSG_DATA) {
- /* must still forward */
- /* we may have some pending data starting at req->buf->p */
- ret = FLT_STRM_CB(s, flt_http_data(s, msg),
- /* default_ret */ MIN(msg->chunk_len, req->buf->i - msg->next),
- /* on_error */ goto aborted_xfer);
- msg->next += ret;
- msg->chunk_len -= ret;
-
- if (msg->chunk_len) {
- /* input empty or output full */
- if (req->buf->i > msg->next)
- req->flags |= CF_WAKE_WRITE;
- goto missing_data;
- }
-
- /* nothing left to forward */
- if (msg->flags & HTTP_MSGF_TE_CHNK)
- msg->msg_state = HTTP_MSG_CHUNK_CRLF;
- else
- msg->msg_state = HTTP_MSG_ENDING;
- }
- else if (msg->msg_state == HTTP_MSG_CHUNK_SIZE) {
- /* read the chunk size and assign it to ->chunk_len, then
- * set ->next to point to the body and switch to DATA or
- * TRAILERS state.
- */
- ret = http_parse_chunk_size(msg);
- if (ret == 0)
- goto missing_data;
- else if (ret < 0) {
- stream_inc_http_err_ctr(s);
- if (msg->err_pos >= 0)
- http_capture_bad_message(&sess->fe->invalid_req, s, msg, HTTP_MSG_CHUNK_SIZE, s->be);
- goto return_bad_req;
- }
- msg->next += msg->sol;
- msg->sol = 0;
- msg->msg_state = msg->chunk_len ? HTTP_MSG_DATA : HTTP_MSG_TRAILERS;
- /* otherwise we're in HTTP_MSG_DATA or HTTP_MSG_TRAILERS state */
- }
- else if (msg->msg_state == HTTP_MSG_CHUNK_CRLF) {
- /* we want the CRLF after the data */
- ret = http_skip_chunk_crlf(msg);
- if (ret == 0)
- goto missing_data;
- else if (ret < 0) {
- stream_inc_http_err_ctr(s);
- if (msg->err_pos >= 0)
- http_capture_bad_message(&sess->fe->invalid_req, s, msg, HTTP_MSG_CHUNK_CRLF, s->be);
- goto return_bad_req;
- }
- msg->next += msg->sol;
- msg->sol = 0;
- msg->msg_state = HTTP_MSG_CHUNK_SIZE;
- /* we're in MSG_CHUNK_SIZE now */
- }
- else if (msg->msg_state == HTTP_MSG_TRAILERS) {
- ret = http_forward_trailers(msg);
- if (ret < 0) {
- stream_inc_http_err_ctr(s);
- if (msg->err_pos >= 0)
- http_capture_bad_message(&sess->fe->invalid_req, s, msg, HTTP_MSG_TRAILERS, s->be);
- goto return_bad_req;
- }
- FLT_STRM_CB(s, flt_http_chunk_trailers(s, msg),
- /* default_ret */ 1,
- /* on_error */ goto return_bad_req);
- msg->next += msg->sol;
- msg->sol = 0;
- if (!ret)
- goto missing_data;
- msg->msg_state = HTTP_MSG_ENDING;
- }
- else if (msg->msg_state == HTTP_MSG_ENDING) {
- /* we don't want to forward closes on DONE except in
- * tunnel mode.
- */
- if ((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN)
- channel_dont_close(req);
- /* we may have some pending data starting at req->buf->p
- * such as last chunk of data or trailers.
- */
- ret = FLT_STRM_CB(s, flt_http_forward_data(s, msg, msg->next),
- /* default_ret */ msg->next,
- /* on_error */ goto return_bad_req);
- b_adv(req->buf, ret);
- msg->next -= ret;
- if (unlikely(!(s->req.flags & CF_WROTE_DATA) || msg->sov > 0))
- msg->sov -= ret;
- if (msg->next)
- goto skip_resync_states;
-
- FLT_STRM_CB(s, flt_http_end(s, msg),
- /* default_ret */ 1,
- /* on_error */ goto return_bad_req,
- /* on_wait */ goto skip_resync_states);
- msg->msg_state = HTTP_MSG_DONE;
- }
- else {
- /* other states, DONE...TUNNEL */
- /* we don't want to forward closes on DONE except in
- * tunnel mode.
- */
- if ((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN)
- channel_dont_close(req);
+ if (msg->msg_state < HTTP_MSG_DONE) {
+ ret = ((msg->flags & HTTP_MSGF_TE_CHNK)
+ ? http_msg_forward_chunked_body(s, msg)
+ : http_msg_forward_body(s, msg));
+ if (!ret)
+ goto missing_data_or_waiting;
+ if (ret < 0)
+ goto return_bad_req;
+ }
- ret = msg->msg_state;
- if (http_resync_states(s)) {
- /* some state changes occurred, maybe the analyser
- * was disabled too.
- */
- if (unlikely(msg->msg_state == HTTP_MSG_ERROR)) {
- if (req->flags & CF_SHUTW) {
- /* request errors are most likely due to
- * the server aborting the transfer.
- */
- goto aborted_xfer;
- }
- if (msg->err_pos >= 0)
- http_capture_bad_message(&sess->fe->invalid_req, s, msg, ret, s->be);
- goto return_bad_req;
- }
- return 1;
- }
+ /* other states, DONE...TUNNEL */
+ /* we don't want to forward closes on DONE except in tunnel mode. */
+ if ((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN)
+ channel_dont_close(req);
- skip_resync_states:
- /* If "option abortonclose" is set on the backend, we
- * want to monitor the client's connection and forward
- * any shutdown notification to the server, which will
- * decide whether to close or to go on processing the
- * request. We only do that in tunnel mode, and not in
- * other modes since it can be abused to exhaust source
- * ports.
- */
- if (s->be->options & PR_O_ABRT_CLOSE) {
- channel_auto_read(req);
- if ((req->flags & (CF_SHUTR|CF_READ_NULL)) &&
- ((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN))
- s->si[1].flags |= SI_FL_NOLINGER;
- channel_auto_close(req);
- }
- else if (s->txn->meth == HTTP_METH_POST) {
- /* POST requests may require to read extra CRLF
- * sent by broken browsers and which could cause
- * an RST to be sent upon close on some systems
- * (eg: Linux).
- */
- channel_auto_read(req);
+ ret = msg->msg_state;
+ if (http_resync_states(s)) {
+ /* some state changes occurred, maybe the analyser
+ * was disabled too. */
+ if (unlikely(msg->msg_state == HTTP_MSG_ERROR)) {
+ if (req->flags & CF_SHUTW) {
+ /* request errors are most likely due to the
+ * server aborting the transfer. */
+ goto aborted_xfer;
}
-
- return 0;
+ if (msg->err_pos >= 0)
+ http_capture_bad_message(&sess->fe->invalid_req, s, msg, ret, s->be);
+ goto return_bad_req;
}
+ return 1;
}
- missing_data:
- /* we may have some pending data starting at req->buf->p */
- ret = FLT_STRM_CB(s, flt_http_forward_data(s, msg, msg->next),
- /* default_ret */ msg->next,
- /* on_error */ goto return_bad_req);
- b_adv(req->buf, ret);
- msg->next -= ret;
- if (unlikely(!(s->req.flags & CF_WROTE_DATA) || msg->sov > 0))
- msg->sov -= ret;
- if (!HAS_FILTERS(s))
- msg->chunk_len -= channel_forward(req, msg->chunk_len);
+ /* If "option abortonclose" is set on the backend, we want to monitor
+ * the client's connection and forward any shutdown notification to the
+ * server, which will decide whether to close or to go on processing the
+ * request. We only do that in tunnel mode, and not in other modes since
+ * it can be abused to exhaust source ports. */
+ if (s->be->options & PR_O_ABRT_CLOSE) {
+ channel_auto_read(req);
+ if ((req->flags & (CF_SHUTR|CF_READ_NULL)) &&
+ ((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN))
+ s->si[1].flags |= SI_FL_NOLINGER;
+ channel_auto_close(req);
+ }
+ else if (s->txn->meth == HTTP_METH_POST) {
+ /* POST requests may require to read extra CRLF sent by broken
+ * browsers and which could cause an RST to be sent upon close
+ * on some systems (eg: Linux). */
+ channel_auto_read(req);
+ }
+ return 0;
+ missing_data_or_waiting:
/* stop waiting for data if the input is closed before the end */
if (req->flags & CF_SHUTR) {
if (!(s->flags & SF_ERR_MASK))
@@ -6738,7 +6619,6 @@
* is performed at once on final states for all bytes parsed, or when leaving
* on missing data.
*/
-
int http_response_forward_body(struct stream *s, struct channel *res, int an_bit)
{
struct session *sess = s->sess;
@@ -6764,187 +6644,51 @@
channel_auto_close(res);
if (msg->msg_state == HTTP_MSG_BODY) {
- /* The previous analysers guarantee that the state is somewhere
- * between MSG_BODY and the first MSG_DATA. So msg->sol and
- * msg->next are always correct.
- */
- if (msg->msg_state < HTTP_MSG_CHUNK_SIZE) {
- if (msg->flags & HTTP_MSGF_TE_CHNK)
- msg->msg_state = HTTP_MSG_CHUNK_SIZE;
- else
- msg->msg_state = HTTP_MSG_DATA;
- }
+ msg->msg_state = ((msg->flags & HTTP_MSGF_TE_CHNK)
+ ? HTTP_MSG_CHUNK_SIZE
+ : HTTP_MSG_DATA);
}
if (res->to_forward) {
- /* We can't process the buffer's contents yet */
+ /* We can't process the buffer's contents yet */
res->flags |= CF_WAKE_WRITE;
- goto missing_data;
+ goto missing_data_or_waiting;
}
- while (1) {
- switch (msg->msg_state - HTTP_MSG_DATA) {
- case HTTP_MSG_DATA - HTTP_MSG_DATA: /* must still forward */
- /* we may have some pending data starting at res->buf->p */
-
- /* Neither content-length, nor transfer-encoding was
- * found, so we must read the body until the server
- * connection is closed. In that case, we eat data as
- * they come. */
- if (!(msg->flags & HTTP_MSGF_XFER_LEN)) {
- unsigned long long len = (res->buf->i - msg->next);
- msg->chunk_len += len;
- msg->body_len += len;
- }
- ret = FLT_STRM_CB(s, flt_http_data(s, msg),
- /* default_ret */ MIN(msg->chunk_len, res->buf->i - msg->next),
- /* on_error */ goto aborted_xfer);
- msg->next += ret;
- msg->chunk_len -= ret;
- if (msg->chunk_len) {
- /* input empty or output full */
- if (res->buf->i > msg->next)
- res->flags |= CF_WAKE_WRITE;
- goto missing_data;
- }
-
- /* nothing left to forward */
- if (msg->flags & HTTP_MSGF_TE_CHNK) {
- msg->msg_state = HTTP_MSG_CHUNK_CRLF;
- } else if (!(msg->flags & HTTP_MSGF_XFER_LEN) &&
- !(res->flags & CF_SHUTR)) {
- /* The server still sending data */
- goto missing_data;
- } else {
- msg->msg_state = HTTP_MSG_ENDING;
- break;
- }
- /* fall through for HTTP_MSG_CHUNK_CRLF */
-
- case HTTP_MSG_CHUNK_CRLF - HTTP_MSG_DATA:
- /* we want the CRLF after the data */
- ret = http_skip_chunk_crlf(msg);
- if (ret == 0)
- goto missing_data;
- else if (ret < 0) {
- if (msg->err_pos >= 0)
- http_capture_bad_message(&s->be->invalid_rep, s, msg, HTTP_MSG_CHUNK_CRLF, sess->fe);
- goto return_bad_res;
- }
- msg->next += msg->sol;
- msg->sol = 0;
- msg->msg_state = HTTP_MSG_CHUNK_SIZE;
- /* we're in MSG_CHUNK_SIZE now, fall through */
-
- case HTTP_MSG_CHUNK_SIZE - HTTP_MSG_DATA:
- /* read the chunk size and assign it to ->chunk_len, then
- * set ->next to point to the body and switch to DATA or
- * TRAILERS state.
- */
- ret = http_parse_chunk_size(msg);
- if (ret == 0)
- goto missing_data;
- else if (ret < 0) {
- if (msg->err_pos >= 0)
- http_capture_bad_message(&s->be->invalid_rep, s, msg, HTTP_MSG_CHUNK_SIZE, sess->fe);
- goto return_bad_res;
- }
- msg->next += msg->sol;
- msg->sol = 0;
- if (msg->chunk_len) {
- msg->msg_state = HTTP_MSG_DATA;
- break;
- }
- msg->msg_state = HTTP_MSG_TRAILERS;
- /* fall through */
-
- case HTTP_MSG_TRAILERS - HTTP_MSG_DATA:
- ret = http_forward_trailers(msg);
- if (ret < 0) {
- if (msg->err_pos >= 0)
- http_capture_bad_message(&s->be->invalid_rep, s, msg, HTTP_MSG_TRAILERS, sess->fe);
- goto return_bad_res;
- }
- FLT_STRM_CB(s, flt_http_chunk_trailers(s, msg),
- /* default_ret */ 1,
- /* on_error */ goto return_bad_res);
- msg->next += msg->sol;
- msg->sol = 0;
- if (!ret)
- goto missing_data;
- msg->msg_state = HTTP_MSG_ENDING;
- /* fall through */
-
- case HTTP_MSG_ENDING - HTTP_MSG_DATA:
- /* for keep-alive we don't want to forward closes on ENDING */
- if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL ||
- (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL)
- channel_dont_close(res);
-
- /* we may have some pending data starting at res->buf->p
- * such as a last chunk of data or trailers.
- */
- ret = FLT_STRM_CB(s, flt_http_forward_data(s, msg, msg->next),
- /* default_ret */ msg->next,
- /* on_error */ goto return_bad_res);
- b_adv(res->buf, ret);
- msg->next -= ret;
- if (msg->sov > 0)
- msg->sov -= ret;
- if (msg->next)
- goto skip_resync_states;
-
- FLT_STRM_CB(s, flt_http_end(s, msg),
- /* default_ret */ 1,
- /* on_error */ goto return_bad_res,
- /* on_wait */ goto skip_resync_states);
- msg->msg_state = HTTP_MSG_DONE;
- /* fall through */
-
- default:
- /* other states, DONE...TUNNEL */
+ if (msg->msg_state < HTTP_MSG_DONE) {
+ ret = ((msg->flags & HTTP_MSGF_TE_CHNK)
+ ? http_msg_forward_chunked_body(s, msg)
+ : http_msg_forward_body(s, msg));
+ if (!ret)
+ goto missing_data_or_waiting;
+ if (ret < 0)
+ goto return_bad_res;
+ }
- /* for keep-alive we don't want to forward closes on DONE */
- if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL ||
- (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL)
- channel_dont_close(res);
+ /* other states, DONE...TUNNEL */
+ /* for keep-alive we don't want to forward closes on DONE */
+ if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL ||
+ (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL)
+ channel_dont_close(res);
- ret = msg->msg_state;
- if (http_resync_states(s)) {
- /* some state changes occurred, maybe the analyser
- * was disabled too.
- */
- if (unlikely(msg->msg_state == HTTP_MSG_ERROR)) {
- if (res->flags & CF_SHUTW) {
- /* response errors are most likely due to
- * the client aborting the transfer.
- */
- goto aborted_xfer;
- }
- if (msg->err_pos >= 0)
- http_capture_bad_message(&s->be->invalid_rep, s, msg, ret, sess->fe);
- goto return_bad_res;
- }
- return 1;
+ ret = msg->msg_state;
+ if (http_resync_states(s)) {
+ /* some state changes occurred, maybe the analyser was disabled
+ * too. */
+ if (unlikely(msg->msg_state == HTTP_MSG_ERROR)) {
+ if (res->flags & CF_SHUTW) {
+ /* response errors are most likely due to the
+ * client aborting the transfer. */
+ goto aborted_xfer;
}
-
- skip_resync_states:
- return 0;
+ if (msg->err_pos >= 0)
+ http_capture_bad_message(&s->be->invalid_rep, s, msg, ret, strm_fe(s));
+ goto return_bad_res;
}
+ return 1;
}
- missing_data:
- /* we may have some pending data starting at res->buf->p */
- ret = FLT_STRM_CB(s, flt_http_forward_data(s, msg, msg->next),
- /* default_ret */ msg->next,
- /* on_error */ goto return_bad_res);
- b_adv(res->buf, ret);
- msg->next -= ret;
- if (msg->sov > 0)
- msg->sov -= ret;
- if (!HAS_FILTERS(s))
- msg->chunk_len -= channel_forward(res, msg->chunk_len);
-
+ missing_data_or_waiting:
if (res->flags & CF_SHUTW)
goto aborted_xfer;
@@ -6971,13 +6715,14 @@
if (!s->req.analysers)
goto return_bad_res;
- /* When TE: chunked is used, we need to get there again to parse remaining
- * chunks even if the server has closed, so we don't want to set CF_DONTCLOSE.
- * Similarly, with keep-alive on the client side, we don't want to forward a
- * close.
+ /* When TE: chunked is used, we need to get there again to parse
+ * remaining chunks even if the server has closed, so we don't want to
+ * set CF_DONTCLOSE. Similarly, if the body length is undefined, if
+ * keep-alive is set on the client side or if there are filters
+ * registered on the stream, we don't want to forward a close
*/
if ((msg->flags & HTTP_MSGF_TE_CHNK) || !msg->body_len ||
- (msg->flags & HTTP_MSGF_COMPRESSING) ||
+ HAS_FILTERS(s) ||
(txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL ||
(txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL)
channel_dont_close(res);
@@ -7033,8 +6778,209 @@
if (!(s->flags & SF_FINST_MASK))
s->flags |= SF_FINST_D;
return 0;
+}
+
+
+static inline int
+http_msg_forward_body(struct stream *s, struct http_msg *msg)
+{
+ struct channel *chn = msg->chn;
+ int ret;
+
+ /* Here we have the guarantee to be in HTTP_MSG_DATA or HTTP_MSG_ENDING state */
+
+ if (msg->msg_state == HTTP_MSG_ENDING)
+ goto ending;
+
+ /* Neither content-length, nor transfer-encoding was found, so we must
+ * read the body until the server connection is closed. In that case, we
+ * eat data as they come. Of course, this happens for response only. */
+ if (!(msg->flags & HTTP_MSGF_XFER_LEN)) {
+ unsigned long long len = (chn->buf->i - msg->next);
+ msg->chunk_len += len;
+ msg->body_len += len;
+ }
+ ret = FLT_STRM_CB(s, flt_http_data(s, msg),
+ /* default_ret */ MIN(msg->chunk_len, chn->buf->i - msg->next),
+ /* on_error */ goto error);
+ msg->next += ret;
+ msg->chunk_len -= ret;
+ if (msg->chunk_len) {
+ /* input empty or output full */
+ if (chn->buf->i > msg->next)
+ chn->flags |= CF_WAKE_WRITE;
+ goto missing_data_or_waiting;
+ }
+
+ if (!(msg->flags & HTTP_MSGF_XFER_LEN) && !(chn->flags & CF_SHUTR)) {
+ /* The server still sending data */
+ goto missing_data_or_waiting;
+ }
+ msg->msg_state = HTTP_MSG_ENDING;
+
+ ending:
+ /* we may have some pending data starting at res->buf->p such as a last
+ * chunk of data or trailers. */
+ ret = FLT_STRM_CB(s, flt_http_forward_data(s, msg, msg->next),
+ /* default_ret */ msg->next,
+ /* on_error */ goto error);
+ b_adv(chn->buf, ret);
+ msg->next -= ret;
+ if (msg->next)
+ goto missing_data_or_waiting;
+
+ FLT_STRM_CB(s, flt_http_end(s, msg),
+ /* default_ret */ 1,
+ /* on_error */ goto error,
+ /* on_wait */ goto waiting);
+ msg->msg_state = HTTP_MSG_DONE;
+ return 1;
+
+ missing_data_or_waiting:
+ /* we may have some pending data starting at chn->buf->p */
+ ret = FLT_STRM_CB(s, flt_http_forward_data(s, msg, msg->next),
+ /* default_ret */ msg->next,
+ /* on_error */ goto error);
+ b_adv(chn->buf, ret);
+ msg->next -= ret;
+ if (!(chn->flags & CF_WROTE_DATA) || msg->sov > 0)
+ msg->sov -= ret;
+ if (!HAS_FILTERS(s))
+ msg->chunk_len -= channel_forward(chn, msg->chunk_len);
+ waiting:
+ return 0;
+ error:
+ return -1;
+}
+
+static inline int
+http_msg_forward_chunked_body(struct stream *s, struct http_msg *msg)
+{
+ struct channel *chn = msg->chn;
+ int ret;
+
+ /* Here we have the guarantee to be in one of the following state:
+ * HTTP_MSG_DATA, HTTP_MSG_CHUNK_SIZE, HTTP_MSG_CHUNK_CRLF,
+ * HTTP_MSG_TRAILERS or HTTP_MSG_ENDING. */
+
+ switch_states:
+ switch (msg->msg_state) {
+ case HTTP_MSG_DATA:
+ ret = FLT_STRM_CB(s, flt_http_data(s, msg),
+ /* default_ret */ MIN(msg->chunk_len, chn->buf->i - msg->next),
+ /* on_error */ goto error);
+ msg->next += ret;
+ msg->chunk_len -= ret;
+ if (msg->chunk_len) {
+ /* input empty or output full */
+ if (chn->buf->i > msg->next)
+ chn->flags |= CF_WAKE_WRITE;
+ goto missing_data_or_waiting;
+ }
+
+ /* nothing left to forward for this chunk*/
+ msg->msg_state = HTTP_MSG_CHUNK_CRLF;
+ /* fall through for HTTP_MSG_CHUNK_CRLF */
+
+ case HTTP_MSG_CHUNK_CRLF:
+ /* we want the CRLF after the data */
+ ret = http_skip_chunk_crlf(msg);
+ if (ret == 0)
+ goto missing_data_or_waiting;
+ if (ret < 0)
+ goto chunk_parsing_error;
+ msg->next += msg->sol;
+ msg->sol = 0;
+ msg->msg_state = HTTP_MSG_CHUNK_SIZE;
+ /* fall through for HTTP_MSG_CHUNK_SIZE */
+
+ case HTTP_MSG_CHUNK_SIZE:
+ /* read the chunk size and assign it to ->chunk_len,
+ * then set ->next to point to the body and switch to
+ * DATA or TRAILERS state.
+ */
+ ret = http_parse_chunk_size(msg);
+ if (ret == 0)
+ goto missing_data_or_waiting;
+ if (ret < 0)
+ goto chunk_parsing_error;
+ msg->next += msg->sol;
+ msg->sol = 0;
+ if (msg->chunk_len) {
+ msg->msg_state = HTTP_MSG_DATA;
+ goto switch_states;
+ }
+ msg->msg_state = HTTP_MSG_TRAILERS;
+ /* fall through for HTTP_MSG_TRAILERS */
+
+ case HTTP_MSG_TRAILERS:
+ ret = http_forward_trailers(msg);
+ if (ret < 0)
+ goto chunk_parsing_error;
+ FLT_STRM_CB(s, flt_http_chunk_trailers(s, msg),
+ /* default_ret */ 1,
+ /* on_error */ goto error);
+ msg->next += msg->sol;
+ msg->sol = 0;
+ if (!ret)
+ goto missing_data_or_waiting;
+ break;
+
+ case HTTP_MSG_ENDING:
+ goto ending;
+
+ default:
+ /* This should no happen in this function */
+ goto error;
+ }
+
+ msg->msg_state = HTTP_MSG_ENDING;
+ ending:
+ /* we may have some pending data starting at res->buf->p such as a last
+ * chunk of data or trailers. */
+ ret = FLT_STRM_CB(s, flt_http_forward_data(s, msg, msg->next),
+ /* default_ret */ msg->next,
+ /* on_error */ goto error);
+ b_adv(chn->buf, ret);
+ msg->next -= ret;
+ if (msg->next)
+ goto missing_data_or_waiting;
+
+ FLT_STRM_CB(s, flt_http_end(s, msg),
+ /* default_ret */ 1,
+ /* on_error */ goto error,
+ /* on_wait */ goto waiting);
+ msg->msg_state = HTTP_MSG_DONE;
+ return 1;
+
+ missing_data_or_waiting:
+ /* we may have some pending data starting at chn->buf->p */
+ ret = FLT_STRM_CB(s, flt_http_forward_data(s, msg, msg->next),
+ /* default_ret */ msg->next,
+ /* on_error */ goto error);
+ b_adv(chn->buf, ret);
+ msg->next -= ret;
+ if (!(chn->flags & CF_WROTE_DATA) || msg->sov > 0)
+ msg->sov -= ret;
+ if (!HAS_FILTERS(s))
+ msg->chunk_len -= channel_forward(chn, msg->chunk_len);
+ waiting:
+ return 0;
+
+ chunk_parsing_error:
+ if (msg->err_pos >= 0) {
+ if (chn->flags & CF_ISRESP)
+ http_capture_bad_message(&s->be->invalid_rep, s, msg,
+ msg->msg_state, strm_fe(s));
+ else
+ http_capture_bad_message(&strm_fe(s)->invalid_req, s,
+ msg, msg->msg_state, s->be);
+ }
+ error:
+ return -1;
}
+
/* Iterate the same filter through all request headers.
* Returns 1 if this filter can be stopped upon return, otherwise 0.
* Since it can manage the switch to another backend, it updates the per-proxy