mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-04-11 03:31:36 +00:00
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.
This commit is contained in:
parent
309c6418b0
commit
dbe34eb8cb
560
src/proto_http.c
560
src/proto_http.c
@ -275,6 +275,9 @@ fd_set http_encode_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set
|
|||||||
|
|
||||||
static int http_apply_redirect_rule(struct redirect_rule *rule, struct stream *s, struct http_txn *txn);
|
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 returns a reason associated with the HTTP status.
|
||||||
* This function never fails, a message is always returned.
|
* This function never fails, a message is always returned.
|
||||||
*/
|
*/
|
||||||
@ -5462,16 +5465,9 @@ int http_request_forward_body(struct stream *s, struct channel *req, int an_bit)
|
|||||||
* an "Expect: 100-continue" header.
|
* an "Expect: 100-continue" header.
|
||||||
*/
|
*/
|
||||||
if (msg->msg_state == HTTP_MSG_BODY) {
|
if (msg->msg_state == HTTP_MSG_BODY) {
|
||||||
/* The previous analysers guarantee that the state is somewhere
|
msg->msg_state = ((msg->flags & HTTP_MSGF_TE_CHNK)
|
||||||
* between MSG_BODY and the first MSG_DATA. So msg->sol and
|
? HTTP_MSG_CHUNK_SIZE
|
||||||
* msg->next are always correct.
|
: HTTP_MSG_DATA);
|
||||||
*/
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO/filters: when http-buffer-request option is set or if a
|
/* TODO/filters: when http-buffer-request option is set or if a
|
||||||
* rule on url_param exists, the first chunk size could be
|
* rule on url_param exists, the first chunk size could be
|
||||||
@ -5488,7 +5484,7 @@ int http_request_forward_body(struct stream *s, struct channel *req, int an_bit)
|
|||||||
if (!(s->res.flags & CF_READ_ATTACHED)) {
|
if (!(s->res.flags & CF_READ_ATTACHED)) {
|
||||||
channel_auto_connect(req);
|
channel_auto_connect(req);
|
||||||
req->flags |= CF_WAKE_CONNECT;
|
req->flags |= CF_WAKE_CONNECT;
|
||||||
goto missing_data;
|
goto missing_data_or_waiting;
|
||||||
}
|
}
|
||||||
msg->flags &= ~HTTP_MSGF_WAIT_CONN;
|
msg->flags &= ~HTTP_MSGF_WAIT_CONN;
|
||||||
}
|
}
|
||||||
@ -5499,127 +5495,32 @@ int http_request_forward_body(struct stream *s, struct channel *req, int an_bit)
|
|||||||
if (req->to_forward) {
|
if (req->to_forward) {
|
||||||
/* We can't process the buffer's contents yet */
|
/* We can't process the buffer's contents yet */
|
||||||
req->flags |= CF_WAKE_WRITE;
|
req->flags |= CF_WAKE_WRITE;
|
||||||
goto missing_data;
|
goto missing_data_or_waiting;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (1) {
|
if (msg->msg_state < HTTP_MSG_DONE) {
|
||||||
if (msg->msg_state == HTTP_MSG_DATA) {
|
ret = ((msg->flags & HTTP_MSGF_TE_CHNK)
|
||||||
/* must still forward */
|
? http_msg_forward_chunked_body(s, msg)
|
||||||
/* we may have some pending data starting at req->buf->p */
|
: http_msg_forward_body(s, msg));
|
||||||
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)
|
if (!ret)
|
||||||
goto missing_data;
|
goto missing_data_or_waiting;
|
||||||
msg->msg_state = HTTP_MSG_ENDING;
|
if (ret < 0)
|
||||||
|
goto return_bad_req;
|
||||||
}
|
}
|
||||||
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 */
|
/* other states, DONE...TUNNEL */
|
||||||
/* we don't want to forward closes on DONE except in
|
/* we don't want to forward closes on DONE except in tunnel mode. */
|
||||||
* tunnel mode.
|
|
||||||
*/
|
|
||||||
if ((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN)
|
if ((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN)
|
||||||
channel_dont_close(req);
|
channel_dont_close(req);
|
||||||
|
|
||||||
ret = msg->msg_state;
|
ret = msg->msg_state;
|
||||||
if (http_resync_states(s)) {
|
if (http_resync_states(s)) {
|
||||||
/* some state changes occurred, maybe the analyser
|
/* some state changes occurred, maybe the analyser
|
||||||
* was disabled too.
|
* was disabled too. */
|
||||||
*/
|
|
||||||
if (unlikely(msg->msg_state == HTTP_MSG_ERROR)) {
|
if (unlikely(msg->msg_state == HTTP_MSG_ERROR)) {
|
||||||
if (req->flags & CF_SHUTW) {
|
if (req->flags & CF_SHUTW) {
|
||||||
/* request errors are most likely due to
|
/* request errors are most likely due to the
|
||||||
* the server aborting the transfer.
|
* server aborting the transfer. */
|
||||||
*/
|
|
||||||
goto aborted_xfer;
|
goto aborted_xfer;
|
||||||
}
|
}
|
||||||
if (msg->err_pos >= 0)
|
if (msg->err_pos >= 0)
|
||||||
@ -5629,15 +5530,11 @@ int http_request_forward_body(struct stream *s, struct channel *req, int an_bit)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
skip_resync_states:
|
/* If "option abortonclose" is set on the backend, we want to monitor
|
||||||
/* If "option abortonclose" is set on the backend, we
|
* the client's connection and forward any shutdown notification to the
|
||||||
* want to monitor the client's connection and forward
|
* server, which will decide whether to close or to go on processing the
|
||||||
* any shutdown notification to the server, which will
|
* request. We only do that in tunnel mode, and not in other modes since
|
||||||
* decide whether to close or to go on processing the
|
* it can be abused to exhaust source ports. */
|
||||||
* 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) {
|
if (s->be->options & PR_O_ABRT_CLOSE) {
|
||||||
channel_auto_read(req);
|
channel_auto_read(req);
|
||||||
if ((req->flags & (CF_SHUTR|CF_READ_NULL)) &&
|
if ((req->flags & (CF_SHUTR|CF_READ_NULL)) &&
|
||||||
@ -5646,30 +5543,14 @@ int http_request_forward_body(struct stream *s, struct channel *req, int an_bit)
|
|||||||
channel_auto_close(req);
|
channel_auto_close(req);
|
||||||
}
|
}
|
||||||
else if (s->txn->meth == HTTP_METH_POST) {
|
else if (s->txn->meth == HTTP_METH_POST) {
|
||||||
/* POST requests may require to read extra CRLF
|
/* POST requests may require to read extra CRLF sent by broken
|
||||||
* sent by broken browsers and which could cause
|
* browsers and which could cause an RST to be sent upon close
|
||||||
* an RST to be sent upon close on some systems
|
* on some systems (eg: Linux). */
|
||||||
* (eg: Linux).
|
|
||||||
*/
|
|
||||||
channel_auto_read(req);
|
channel_auto_read(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
|
missing_data_or_waiting:
|
||||||
/* stop waiting for data if the input is closed before the end */
|
/* stop waiting for data if the input is closed before the end */
|
||||||
if (req->flags & CF_SHUTR) {
|
if (req->flags & CF_SHUTR) {
|
||||||
if (!(s->flags & SF_ERR_MASK))
|
if (!(s->flags & SF_ERR_MASK))
|
||||||
@ -6738,7 +6619,6 @@ int http_process_res_common(struct stream *s, struct channel *rep, int an_bit, s
|
|||||||
* is performed at once on final states for all bytes parsed, or when leaving
|
* is performed at once on final states for all bytes parsed, or when leaving
|
||||||
* on missing data.
|
* on missing data.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int http_response_forward_body(struct stream *s, struct channel *res, int an_bit)
|
int http_response_forward_body(struct stream *s, struct channel *res, int an_bit)
|
||||||
{
|
{
|
||||||
struct session *sess = s->sess;
|
struct session *sess = s->sess;
|
||||||
@ -6764,146 +6644,28 @@ int http_response_forward_body(struct stream *s, struct channel *res, int an_bit
|
|||||||
channel_auto_close(res);
|
channel_auto_close(res);
|
||||||
|
|
||||||
if (msg->msg_state == HTTP_MSG_BODY) {
|
if (msg->msg_state == HTTP_MSG_BODY) {
|
||||||
/* The previous analysers guarantee that the state is somewhere
|
msg->msg_state = ((msg->flags & HTTP_MSGF_TE_CHNK)
|
||||||
* between MSG_BODY and the first MSG_DATA. So msg->sol and
|
? HTTP_MSG_CHUNK_SIZE
|
||||||
* msg->next are always correct.
|
: HTTP_MSG_DATA);
|
||||||
*/
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res->to_forward) {
|
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;
|
res->flags |= CF_WAKE_WRITE;
|
||||||
goto missing_data;
|
goto missing_data_or_waiting;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (1) {
|
if (msg->msg_state < HTTP_MSG_DONE) {
|
||||||
switch (msg->msg_state - HTTP_MSG_DATA) {
|
ret = ((msg->flags & HTTP_MSGF_TE_CHNK)
|
||||||
case HTTP_MSG_DATA - HTTP_MSG_DATA: /* must still forward */
|
? http_msg_forward_chunked_body(s, msg)
|
||||||
/* we may have some pending data starting at res->buf->p */
|
: http_msg_forward_body(s, msg));
|
||||||
|
|
||||||
/* 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)
|
if (!ret)
|
||||||
goto missing_data;
|
goto missing_data_or_waiting;
|
||||||
msg->msg_state = HTTP_MSG_ENDING;
|
if (ret < 0)
|
||||||
/* fall through */
|
goto return_bad_res;
|
||||||
|
}
|
||||||
|
|
||||||
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 */
|
/* other states, DONE...TUNNEL */
|
||||||
|
|
||||||
/* for keep-alive we don't want to forward closes on DONE */
|
/* for keep-alive we don't want to forward closes on DONE */
|
||||||
if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL ||
|
if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL ||
|
||||||
(txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL)
|
(txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL)
|
||||||
@ -6911,40 +6673,22 @@ int http_response_forward_body(struct stream *s, struct channel *res, int an_bit
|
|||||||
|
|
||||||
ret = msg->msg_state;
|
ret = msg->msg_state;
|
||||||
if (http_resync_states(s)) {
|
if (http_resync_states(s)) {
|
||||||
/* some state changes occurred, maybe the analyser
|
/* some state changes occurred, maybe the analyser was disabled
|
||||||
* was disabled too.
|
* too. */
|
||||||
*/
|
|
||||||
if (unlikely(msg->msg_state == HTTP_MSG_ERROR)) {
|
if (unlikely(msg->msg_state == HTTP_MSG_ERROR)) {
|
||||||
if (res->flags & CF_SHUTW) {
|
if (res->flags & CF_SHUTW) {
|
||||||
/* response errors are most likely due to
|
/* response errors are most likely due to the
|
||||||
* the client aborting the transfer.
|
* client aborting the transfer. */
|
||||||
*/
|
|
||||||
goto aborted_xfer;
|
goto aborted_xfer;
|
||||||
}
|
}
|
||||||
if (msg->err_pos >= 0)
|
if (msg->err_pos >= 0)
|
||||||
http_capture_bad_message(&s->be->invalid_rep, s, msg, ret, sess->fe);
|
http_capture_bad_message(&s->be->invalid_rep, s, msg, ret, strm_fe(s));
|
||||||
goto return_bad_res;
|
goto return_bad_res;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
skip_resync_states:
|
missing_data_or_waiting:
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (res->flags & CF_SHUTW)
|
if (res->flags & CF_SHUTW)
|
||||||
goto aborted_xfer;
|
goto aborted_xfer;
|
||||||
|
|
||||||
@ -6971,13 +6715,14 @@ int http_response_forward_body(struct stream *s, struct channel *res, int an_bit
|
|||||||
if (!s->req.analysers)
|
if (!s->req.analysers)
|
||||||
goto return_bad_res;
|
goto return_bad_res;
|
||||||
|
|
||||||
/* When TE: chunked is used, we need to get there again to parse remaining
|
/* When TE: chunked is used, we need to get there again to parse
|
||||||
* chunks even if the server has closed, so we don't want to set CF_DONTCLOSE.
|
* remaining chunks even if the server has closed, so we don't want to
|
||||||
* Similarly, with keep-alive on the client side, we don't want to forward a
|
* set CF_DONTCLOSE. Similarly, if the body length is undefined, if
|
||||||
* close.
|
* 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 ||
|
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_KAL ||
|
||||||
(txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL)
|
(txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL)
|
||||||
channel_dont_close(res);
|
channel_dont_close(res);
|
||||||
@ -7035,6 +6780,207 @@ int http_response_forward_body(struct stream *s, struct channel *res, int an_bit
|
|||||||
return 0;
|
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.
|
/* Iterate the same filter through all request headers.
|
||||||
* Returns 1 if this filter can be stopped upon return, otherwise 0.
|
* 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
|
* Since it can manage the switch to another backend, it updates the per-proxy
|
||||||
|
Loading…
Reference in New Issue
Block a user