[MAJOR] http: fix again the forward analysers

There were still several situations leading to CLOSE_WAIT sockets
remaining there forever because some complex transitions were
obviously not caught due to the impossibility to resync changes
between the request and response FSMs.

This patch now centralizes the global transaction state and feeds
it from both request and response transitions. That way, whoever
finishes first, there will be no issue for converging to the correct
state.

Some heavy use of the new debugging function has helped a lot. Maybe
those calls could be removed after some time. First tests are very
positive.
This commit is contained in:
Willy Tarreau 2010-01-04 21:15:02 +01:00
parent e988a79c74
commit 610ecceef9
2 changed files with 444 additions and 266 deletions

View File

@ -193,6 +193,7 @@
#define HTTP_MSG_DONE 33 // message end received, waiting for resync or close
#define HTTP_MSG_CLOSING 34 // shutdown_w done, not all bytes sent yet
#define HTTP_MSG_CLOSED 35 // shutdown_w done, all bytes sent
#define HTTP_MSG_TUNNEL 36 // tunneled data after DONE
/* various data sources for the responses */
#define DATA_SRC_NONE 0

View File

@ -3297,192 +3297,24 @@ int http_process_request_body(struct session *s, struct buffer *req, int an_bit)
return 0;
}
/* This function is an analyser which forwards request body (including chunk
* sizes if any). It is called as soon as we must forward, even if we forward
* zero byte. The only situation where it must not be called is when we're in
* tunnel mode and we want to forward till the close. It's used both to forward
* remaining data and to resync after end of body. It expects the msg_state to
* be between MSG_BODY and MSG_DONE (inclusive). It returns zero if it needs to
* read more data, or 1 once we can go on with next request or end the session.
* When in MSG_DATA or MSG_TRAILERS, it will automatically forward hdr_content_len
* bytes of pending data + the headers if not already done (between som and sov).
* It eventually adjusts som to match sov after the data in between have been sent.
/* Terminate current transaction and prepare a new one. This is very tricky
* right now but it works.
*/
int http_request_forward_body(struct session *s, struct buffer *req, int an_bit)
void http_end_txn_clean_session(struct session *s)
{
struct http_txn *txn = &s->txn;
struct http_msg *msg = &s->txn.req;
if (unlikely(msg->msg_state < HTTP_MSG_BODY))
return 0;
if ((req->flags & (BF_READ_ERROR|BF_READ_TIMEOUT|BF_WRITE_ERROR|BF_WRITE_TIMEOUT)) ||
((req->flags & BF_SHUTW) && (req->to_forward || req->send_max))) {
/* Output closed while we were sending data. We must abort. */
buffer_ignore(req, req->l - req->send_max);
req->analysers &= ~an_bit;
return 1;
}
buffer_dont_close(req);
/* Note that we don't have to send 100-continue back because we don't
* need the data to complete our job, and it's up to the server to
* decide whether to return 100, 417 or anything else in return of
* an "Expect: 100-continue" header.
/* FIXME: We need a more portable way of releasing a backend's and a
* server's connections. We need a safer way to reinitialize buffer
* flags. We also need a more accurate method for computing per-request
* data.
*/
http_silent_debug(__LINE__, s);
if (msg->msg_state < HTTP_MSG_CHUNK_SIZE) {
/* we have msg->col and msg->sov which both point to the first
* byte of message body. msg->som still points to the beginning
* of the message. We must save the body in req->lr because it
* survives buffer re-alignments.
*/
req->lr = req->data + msg->sov;
if (txn->flags & TX_REQ_TE_CHNK)
msg->msg_state = HTTP_MSG_CHUNK_SIZE;
else {
msg->msg_state = HTTP_MSG_DATA;
}
}
while (1) {
/* we may have some data pending */
if (msg->hdr_content_len || msg->som != msg->sov) {
int bytes = msg->sov - msg->som;
if (bytes < 0) /* sov may have wrapped at the end */
bytes += req->size;
buffer_forward(req, bytes + msg->hdr_content_len);
msg->hdr_content_len = 0; /* don't forward that again */
msg->som = msg->sov;
}
if (msg->msg_state == HTTP_MSG_DATA) {
/* must still forward */
if (req->to_forward)
goto missing_data;
/* nothing left to forward */
if (txn->flags & TX_REQ_TE_CHNK)
msg->msg_state = HTTP_MSG_DATA_CRLF;
else {
msg->msg_state = HTTP_MSG_DONE;
goto http_msg_done;
}
}
else if (msg->msg_state == HTTP_MSG_CHUNK_SIZE) {
/* read the chunk size and assign it to ->hdr_content_len, then
* set ->sov and ->lr to point to the body and switch to DATA or
* TRAILERS state.
*/
int ret = http_parse_chunk_size(req, msg);
if (!ret)
goto missing_data;
else if (ret < 0)
goto return_bad_req;
/* otherwise we're in HTTP_MSG_DATA or HTTP_MSG_TRAILERS state */
}
else if (msg->msg_state == HTTP_MSG_DATA_CRLF) {
/* we want the CRLF after the data */
int ret;
req->lr = req->w + req->send_max;
if (req->lr >= req->data + req->size)
req->lr -= req->size;
ret = http_skip_chunk_crlf(req, msg);
if (ret == 0)
goto missing_data;
else if (ret < 0)
goto return_bad_req;
/* we're in MSG_CHUNK_SIZE now */
}
else if (msg->msg_state == HTTP_MSG_TRAILERS) {
int ret = http_forward_trailers(req, msg);
if (ret == 0)
goto missing_data;
else if (ret < 0)
goto return_bad_req;
/* we're in HTTP_MSG_DONE now */
}
else if (msg->msg_state == HTTP_MSG_DONE) {
http_msg_done:
/* No need to read anymore, the request was completely parsed */
req->flags |= BF_DONT_READ;
if (txn->rsp.msg_state < HTTP_MSG_DONE && txn->rsp.msg_state != HTTP_MSG_ERROR) {
/* The server has not finished to respond, so we
* don't want to move in order not to upset it.
*/
goto wait_other_side;
}
/* when we support keep-alive or server-close modes, we'll have
* to reset the transaction here.
*/
if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL) {
/* initiate a connection close to the server */
req->cons->flags |= SI_FL_NOLINGER;
buffer_shutw_now(req);
}
else if ((s->fe->options | s->be->options) & PR_O_FORCE_CLO) {
/* Option forceclose is set, let's enforce it now
* that the transfer is complete. We can safely speed
* up the close because we know the server has received
* everything we wanted it to receive.
*/
req->cons->flags |= SI_FL_NOLINGER;
buffer_abort(req);
}
if (req->flags & (BF_SHUTW|BF_SHUTW_NOW)) {
if (req->flags & BF_OUT_EMPTY) {
msg->msg_state = HTTP_MSG_CLOSED;
goto http_msg_closed;
}
else {
msg->msg_state = HTTP_MSG_CLOSING;
goto http_msg_closing;
}
}
else {
/* for other modes, we let further requests pass for now */
req->flags &= ~BF_DONT_READ;
/* FIXME: we're still forced to do that here */
s->rep->flags &= ~BF_DONT_READ;
break;
}
}
else if (msg->msg_state == HTTP_MSG_CLOSING) {
http_msg_closing:
/* nothing else to forward, just waiting for the buffer to be empty */
if (!(req->flags & BF_OUT_EMPTY))
goto wait_empty;
msg->msg_state = HTTP_MSG_CLOSED;
}
else if (msg->msg_state == HTTP_MSG_CLOSED) {
http_msg_closed:
req->flags &= ~BF_DONT_READ;
if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL) {
/* FIXME : this part is 1) awful, 2) tricky, 3) duplicated
* ... but it works.
* We need a better way to force a connection close without
* any risk of propagation to the other side. We need a more
* portable way of releasing a backend's and a server's
* connections. We need a safer way to reinitialize buffer
* flags. We also need a more accurate method for computing
* per-request data.
*/
s->req->cons->flags |= SI_FL_NOLINGER;
s->req->cons->shutr(s->req->cons);
s->req->cons->shutw(s->req->cons);
http_silent_debug(__LINE__, s);
if (s->flags & SN_BE_ASSIGNED)
s->be->beconn--;
@ -3556,7 +3388,7 @@ int http_request_forward_body(struct session *s, struct buffer *req, int an_bit)
s->flags &= ~(SN_CURR_SESS|SN_REDIRECTABLE);
s->txn.meth = 0;
http_reset_txn(s);
txn->flags |= TX_NOT_FIRST;
s->txn.flags |= TX_NOT_FIRST;
if (s->be->options2 & PR_O2_INDEPSTR)
s->req->cons->flags |= SI_FL_INDEP_STR;
@ -3566,6 +3398,7 @@ int http_request_forward_body(struct session *s, struct buffer *req, int an_bit)
* Also, let's not start reading a small request packet,
* we may prefer to read a larger one later.
*/
s->req->flags &= ~BF_DONT_READ;
if (s->req->l > s->req->send_max) {
s->rep->flags |= BF_EXPECT_MORE;
s->req->flags |= BF_DONT_READ;
@ -3581,39 +3414,436 @@ int http_request_forward_body(struct session *s, struct buffer *req, int an_bit)
s->req->analysers |= s->fe->fe_req_ana;
s->rep->analysers = 0;
http_silent_debug(__LINE__, s);
}
/* FIXME: we're still forced to do that here */
s->rep->flags &= ~BF_DONT_READ;
/* This function updates the request state machine according to the response
* state machine and buffer flags. It returns 1 if it changes anything (flag
* or state), otherwise zero. It ignores any state before HTTP_MSG_DONE, as
* it is only used to find when a request/response couple is complete. Both
* this function and its equivalent should loop until both return zero. It
* can set its own state to DONE, CLOSING, CLOSED, TUNNEL, ERROR.
*/
int http_sync_req_state(struct session *s)
{
struct buffer *buf = s->req;
struct http_txn *txn = &s->txn;
unsigned int old_flags = buf->flags;
unsigned int old_state = txn->req.msg_state;
http_silent_debug(__LINE__, s);
if (unlikely(txn->req.msg_state < HTTP_MSG_BODY))
return 0;
if (txn->req.msg_state == HTTP_MSG_DONE) {
/* No need to read anymore, the request was completely parsed */
buf->flags |= BF_DONT_READ;
if (txn->rsp.msg_state == HTTP_MSG_ERROR)
goto wait_other_side;
if (txn->rsp.msg_state < HTTP_MSG_DONE) {
/* The server has not finished to respond, so we
* don't want to move in order not to upset it.
*/
goto wait_other_side;
}
if (txn->rsp.msg_state == HTTP_MSG_TUNNEL) {
/* if any side switches to tunnel mode, the other one does too */
buf->flags &= ~BF_DONT_READ;
txn->req.msg_state = HTTP_MSG_TUNNEL;
goto wait_other_side;
}
/* When we get here, it means that both the request and the
* response have finished receiving. Depending on the connection
* mode, we'll have to wait for the last bytes to leave in either
* direction, and sometimes for a close to be effective.
*/
if (!(buf->flags & (BF_SHUTW|BF_SHUTW_NOW))) {
if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL) {
/* Server-close mode : queue a connection close to the server */
buffer_shutw_now(buf);
buf->cons->flags |= SI_FL_NOLINGER;
}
else if ((s->fe->options | s->be->options) & PR_O_FORCE_CLO) {
/* Option forceclose is set, let's enforce it now
* that we're not expecting any new data to come.
*/
buffer_shutr_now(buf);
buffer_shutw_now(buf);
buf->cons->flags |= SI_FL_NOLINGER;
}
/* other modes include httpclose (no action) and keepalive (not implemented) */
}
if (buf->flags & (BF_SHUTW|BF_SHUTW_NOW)) {
/* if we've just closed an output, let's switch */
if (!(buf->flags & BF_OUT_EMPTY)) {
txn->req.msg_state = HTTP_MSG_CLOSING;
goto http_msg_closing;
}
else {
txn->req.msg_state = HTTP_MSG_CLOSED;
goto http_msg_closed;
}
}
else {
/* other modes are used as a tunnel right now */
buf->flags &= ~BF_DONT_READ;
txn->req.msg_state = HTTP_MSG_TUNNEL;
goto wait_other_side;
}
}
if (txn->req.msg_state == HTTP_MSG_CLOSING) {
http_msg_closing:
/* nothing else to forward, just waiting for the output buffer
* to be empty and for the shutw_now to take effect.
*/
if (buf->flags & BF_OUT_EMPTY) {
txn->req.msg_state = HTTP_MSG_CLOSED;
goto http_msg_closed;
}
else if (buf->flags & BF_SHUTW) {
txn->req.msg_state = HTTP_MSG_ERROR;
goto wait_other_side;
}
}
if (txn->req.msg_state == HTTP_MSG_CLOSED) {
http_msg_closed:
goto wait_other_side;
}
wait_other_side:
http_silent_debug(__LINE__, s);
return txn->req.msg_state != old_state || buf->flags != old_flags;
}
/* This function updates the response state machine according to the request
* state machine and buffer flags. It returns 1 if it changes anything (flag
* or state), otherwise zero. It ignores any state before HTTP_MSG_DONE, as
* it is only used to find when a request/response couple is complete. Both
* this function and its equivalent should loop until both return zero. It
* can set its own state to DONE, CLOSING, CLOSED, TUNNEL, ERROR.
*/
int http_sync_res_state(struct session *s)
{
struct buffer *buf = s->rep;
struct http_txn *txn = &s->txn;
unsigned int old_flags = buf->flags;
unsigned int old_state = txn->rsp.msg_state;
http_silent_debug(__LINE__, s);
if (unlikely(txn->rsp.msg_state < HTTP_MSG_BODY))
return 0;
if (txn->rsp.msg_state == HTTP_MSG_DONE) {
/* In theory, we don't need to read anymore, but we must
* still monitor the server connection for a possible close,
* so we don't set the BF_DONT_READ flag here.
*/
/* buf->flags |= BF_DONT_READ; */
if (txn->req.msg_state == HTTP_MSG_ERROR)
goto wait_other_side;
if (txn->req.msg_state < HTTP_MSG_DONE) {
/* The client seems to still be sending data, probably
* because we got an error response during an upload.
* We have the choice of either breaking the connection
* or letting it pass through. Let's do the later.
*/
goto wait_other_side;
}
if (txn->req.msg_state == HTTP_MSG_TUNNEL) {
/* if any side switches to tunnel mode, the other one does too */
buf->flags &= ~BF_DONT_READ;
txn->rsp.msg_state = HTTP_MSG_TUNNEL;
goto wait_other_side;
}
/* When we get here, it means that both the request and the
* response have finished receiving. Depending on the connection
* mode, we'll have to wait for the last bytes to leave in either
* direction, and sometimes for a close to be effective.
*/
if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL) {
/* Server-close mode : shut read and wait for the request
* side to close its output buffer. The caller will detect
* when we're in DONE and the other is in CLOSED and will
* catch that for the final cleanup.
*/
if (!(buf->flags & (BF_SHUTR|BF_SHUTR_NOW)))
buffer_shutr_now(buf);
goto wait_other_side;
}
else if (!(buf->flags & (BF_SHUTW|BF_SHUTW_NOW)) &&
((s->fe->options | s->be->options) & PR_O_FORCE_CLO)) {
/* Option forceclose is set, let's enforce it now
* that we're not expecting any new data to come.
* The caller knows the session is complete once
* both states are CLOSED.
*/
buffer_shutr_now(buf);
buffer_shutw_now(buf);
buf->cons->flags |= SI_FL_NOLINGER;
}
else {
/* other modes include httpclose (no action) and keepalive
* (not implemented). These modes are used as a tunnel right
* now.
*/
buf->flags &= ~BF_DONT_READ;
txn->rsp.msg_state = HTTP_MSG_TUNNEL;
goto wait_other_side;
}
if (buf->flags & (BF_SHUTW|BF_SHUTW_NOW)) {
/* if we've just closed an output, let's switch */
if (!(buf->flags & BF_OUT_EMPTY)) {
txn->rsp.msg_state = HTTP_MSG_CLOSING;
goto http_msg_closing;
}
else {
txn->rsp.msg_state = HTTP_MSG_CLOSED;
goto http_msg_closed;
}
}
goto wait_other_side;
}
if (txn->rsp.msg_state == HTTP_MSG_CLOSING) {
http_msg_closing:
/* nothing else to forward, just waiting for the output buffer
* to be empty and for the shutw_now to take effect.
*/
if (buf->flags & BF_OUT_EMPTY) {
txn->rsp.msg_state = HTTP_MSG_CLOSED;
goto http_msg_closed;
}
else if (buf->flags & BF_SHUTW) {
txn->rsp.msg_state = HTTP_MSG_ERROR;
goto wait_other_side;
}
}
if (txn->rsp.msg_state == HTTP_MSG_CLOSED) {
http_msg_closed:
/* drop any pending data */
buffer_ignore(buf, buf->l - buf->send_max);
buffer_auto_close(buf);
goto wait_other_side;
}
wait_other_side:
http_silent_debug(__LINE__, s);
return txn->rsp.msg_state != old_state || buf->flags != old_flags;
}
/* Resync the request and response state machines. Return 1 if either state
* changes.
*/
int http_resync_states(struct session *s)
{
struct http_txn *txn = &s->txn;
int old_req_state = txn->req.msg_state;
int old_res_state = txn->rsp.msg_state;
http_silent_debug(__LINE__, s);
http_sync_req_state(s);
while (1) {
http_silent_debug(__LINE__, s);
if (!http_sync_res_state(s))
break;
http_silent_debug(__LINE__, s);
if (!http_sync_req_state(s))
break;
}
http_silent_debug(__LINE__, s);
/* OK, both state machines agree on a compatible state.
* There are a few cases we're interested in :
* - HTTP_MSG_TUNNEL on either means we have to disable both analysers
* - HTTP_MSG_CLOSED on both sides means we've reached the end in both
* directions, so let's simply disable both analysers.
* - HTTP_MSG_CLOSED on the response only means we must abort the
* request.
* - HTTP_MSG_CLOSED on the request and HTTP_MSG_DONE on the response
* with server-close mode means we've completed one request and we
* must re-initialize the server connection.
*/
if (txn->req.msg_state == HTTP_MSG_TUNNEL ||
txn->rsp.msg_state == HTTP_MSG_TUNNEL ||
(txn->req.msg_state == HTTP_MSG_CLOSED &&
txn->rsp.msg_state == HTTP_MSG_CLOSED)) {
s->req->analysers = 0;
s->rep->analysers = 0;
}
else if (txn->rsp.msg_state == HTTP_MSG_CLOSED) {
buffer_abort(s->req);
buffer_auto_close(s->req);
buffer_ignore(s->req, s->req->l - s->req->send_max);
s->req->analysers = 0;
s->rep->analysers = 0;
}
else if (txn->req.msg_state == HTTP_MSG_CLOSED &&
txn->rsp.msg_state == HTTP_MSG_DONE &&
((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL)) {
/* server-close: terminate this server connection and
* reinitialize a fresh-new transaction.
*/
http_end_txn_clean_session(s);
}
/* OK we're done with the data phase */
buffer_auto_close(req);
http_silent_debug(__LINE__, s);
return txn->req.msg_state != old_req_state ||
txn->rsp.msg_state != old_res_state;
}
/* This function is an analyser which forwards request body (including chunk
* sizes if any). It is called as soon as we must forward, even if we forward
* zero byte. The only situation where it must not be called is when we're in
* tunnel mode and we want to forward till the close. It's used both to forward
* remaining data and to resync after end of body. It expects the msg_state to
* be between MSG_BODY and MSG_DONE (inclusive). It returns zero if it needs to
* read more data, or 1 once we can go on with next request or end the session.
* When in MSG_DATA or MSG_TRAILERS, it will automatically forward hdr_content_len
* bytes of pending data + the headers if not already done (between som and sov).
* It eventually adjusts som to match sov after the data in between have been sent.
*/
int http_request_forward_body(struct session *s, struct buffer *req, int an_bit)
{
struct http_txn *txn = &s->txn;
struct http_msg *msg = &s->txn.req;
if (unlikely(msg->msg_state < HTTP_MSG_BODY))
return 0;
if ((req->flags & (BF_READ_ERROR|BF_READ_TIMEOUT|BF_WRITE_ERROR|BF_WRITE_TIMEOUT)) ||
((req->flags & BF_SHUTW) && (req->to_forward || req->send_max))) {
/* Output closed while we were sending data. We must abort. */
buffer_ignore(req, req->l - req->send_max);
req->analysers &= ~an_bit;
return 1;
}
buffer_dont_close(req);
/* Note that we don't have to send 100-continue back because we don't
* need the data to complete our job, and it's up to the server to
* decide whether to return 100, 417 or anything else in return of
* an "Expect: 100-continue" header.
*/
if (msg->msg_state < HTTP_MSG_CHUNK_SIZE) {
/* we have msg->col and msg->sov which both point to the first
* byte of message body. msg->som still points to the beginning
* of the message. We must save the body in req->lr because it
* survives buffer re-alignments.
*/
req->lr = req->data + msg->sov;
if (txn->flags & TX_REQ_TE_CHNK)
msg->msg_state = HTTP_MSG_CHUNK_SIZE;
else {
msg->msg_state = HTTP_MSG_DATA;
}
}
while (1) {
http_silent_debug(__LINE__, s);
/* we may have some data pending */
if (msg->hdr_content_len || msg->som != msg->sov) {
int bytes = msg->sov - msg->som;
if (bytes < 0) /* sov may have wrapped at the end */
bytes += req->size;
buffer_forward(req, bytes + msg->hdr_content_len);
msg->hdr_content_len = 0; /* don't forward that again */
msg->som = msg->sov;
}
if (msg->msg_state == HTTP_MSG_DATA) {
/* must still forward */
if (req->to_forward)
goto missing_data;
/* nothing left to forward */
if (txn->flags & TX_REQ_TE_CHNK)
msg->msg_state = HTTP_MSG_DATA_CRLF;
else
msg->msg_state = HTTP_MSG_DONE;
}
else if (msg->msg_state == HTTP_MSG_CHUNK_SIZE) {
/* read the chunk size and assign it to ->hdr_content_len, then
* set ->sov and ->lr to point to the body and switch to DATA or
* TRAILERS state.
*/
int ret = http_parse_chunk_size(req, msg);
if (!ret)
goto missing_data;
else if (ret < 0)
goto return_bad_req;
/* otherwise we're in HTTP_MSG_DATA or HTTP_MSG_TRAILERS state */
}
else if (msg->msg_state == HTTP_MSG_DATA_CRLF) {
/* we want the CRLF after the data */
int ret;
req->lr = req->w + req->send_max;
if (req->lr >= req->data + req->size)
req->lr -= req->size;
ret = http_skip_chunk_crlf(req, msg);
if (ret == 0)
goto missing_data;
else if (ret < 0)
goto return_bad_req;
/* we're in MSG_CHUNK_SIZE now */
}
else if (msg->msg_state == HTTP_MSG_TRAILERS) {
int ret = http_forward_trailers(req, msg);
if (ret == 0)
goto missing_data;
else if (ret < 0)
goto return_bad_req;
/* we're in HTTP_MSG_DONE now */
}
else {
/* other states, DONE...TUNNEL */
if (http_resync_states(s)) {
/* some state changes occurred, maybe the analyser
* was disabled too.
*/
if (unlikely(msg->msg_state == HTTP_MSG_ERROR))
goto return_bad_req;
return 1;
}
return 0;
}
}
missing_data:
/* stop waiting for data if the input is closed before the end */
if (req->flags & BF_SHUTR)
goto return_bad_req;
wait_other_side:
/* forward the chunk size as well as any pending data */
if (msg->hdr_content_len || msg->som != msg->sov) {
buffer_forward(req, msg->sov - msg->som + msg->hdr_content_len);
msg->hdr_content_len = 0; /* don't forward that again */
msg->som = msg->sov;
}
/* the session handler will take care of timeouts and errors */
return 0;
wait_empty:
/* waiting for the last bits to leave the buffer */
if (req->flags & BF_SHUTW)
goto return_bad_req;
http_silent_debug(__LINE__, s);
return 0;
return_bad_req: /* let's centralize all bad requests */
@ -3631,6 +3861,7 @@ int http_request_forward_body(struct session *s, struct buffer *req, int an_bit)
s->flags |= SN_ERR_PRXCOND;
if (!(s->flags & SN_FINST_MASK))
s->flags |= SN_FINST_R;
http_silent_debug(__LINE__, s);
return 0;
}
@ -4462,6 +4693,7 @@ int http_response_forward_body(struct session *s, struct buffer *res, int an_bit
}
while (1) {
http_silent_debug(__LINE__, s);
/* we may have some data pending */
if (msg->hdr_content_len || msg->som != msg->sov) {
int bytes = msg->sov - msg->som;
@ -4520,80 +4752,29 @@ int http_response_forward_body(struct session *s, struct buffer *res, int an_bit
goto return_bad_res;
/* we're in HTTP_MSG_DONE now */
}
else if (msg->msg_state == HTTP_MSG_DONE) {
/* In theory, we don't need to read anymore, but we must
* still monitor the server connection for a possible close,
* so we don't set the BF_DONT_READ flag here.
*/
if (txn->req.msg_state < HTTP_MSG_DONE && txn->req.msg_state != HTTP_MSG_ERROR) {
/* The client seems to still be sending data, probably
* because we got an error response during an upload.
* We have the choice of either breaking the connection
* or letting it pass through. Let's do the later.
*/
goto wait_other_side;
}
/* when we support keep-alive or server-close modes, we'll have
* to reset the transaction here.
*/
if ((s->fe->options | s->be->options) & PR_O_FORCE_CLO) {
/* option forceclose is set, let's enforce it now that the transfer is complete. */
buffer_abort(res);
}
else if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL) {
/* server close is handled entirely on the req analyser */
s->req->cons->flags |= SI_FL_NOLINGER;
buffer_shutw_now(s->req);
}
if (res->flags & (BF_SHUTW|BF_SHUTW_NOW)) {
if (res->flags & BF_OUT_EMPTY) {
msg->msg_state = HTTP_MSG_CLOSED;
goto http_msg_closed;
}
else {
msg->msg_state = HTTP_MSG_CLOSING;
goto http_msg_closing;
}
}
else {
/* for other modes, we let further responses pass for now */
res->flags &= ~BF_DONT_READ;
/* FIXME: we're still forced to do that here */
s->req->flags &= ~BF_DONT_READ;
break;
}
}
else if (msg->msg_state == HTTP_MSG_CLOSING) {
http_msg_closing:
/* nothing else to forward, just waiting for the buffer to be empty */
if (!(res->flags & BF_OUT_EMPTY))
goto wait_empty;
msg->msg_state = HTTP_MSG_CLOSED;
}
else if (msg->msg_state == HTTP_MSG_CLOSED) {
http_msg_closed:
res->flags &= ~BF_DONT_READ;
/* FIXME: we're still forced to do that here */
s->req->flags &= ~BF_DONT_READ;
break;
}
}
buffer_ignore(res, res->l - res->send_max);
buffer_auto_close(res);
res->analysers &= ~an_bit;
/* other states, DONE...TUNNEL */
if (http_resync_states(s)) {
http_silent_debug(__LINE__, s);
/* some state changes occurred, maybe the analyser
* was disabled too.
*/
if (unlikely(msg->msg_state == HTTP_MSG_ERROR))
goto return_bad_res;
return 1;
}
return 0;
}
}
missing_data:
/* stop waiting for data if the input is closed before the end */
if (res->flags & BF_SHUTR)
goto return_bad_res;
wait_other_side:
if (!s->req->analysers)
goto return_bad_res;
/* forward the chunk size as well as any pending data */
if (msg->hdr_content_len || msg->som != msg->sov) {
buffer_forward(res, msg->sov - msg->som + msg->hdr_content_len);
@ -4602,12 +4783,7 @@ int http_response_forward_body(struct session *s, struct buffer *res, int an_bit
}
/* the session handler will take care of timeouts and errors */
return 0;
wait_empty:
/* waiting for last bits to leave the buffer */
if (res->flags & BF_SHUTW)
goto return_bad_res;
http_silent_debug(__LINE__, s);
return 0;
return_bad_res: /* let's centralize all bad resuests */
@ -4626,6 +4802,7 @@ int http_response_forward_body(struct session *s, struct buffer *res, int an_bit
s->flags |= SN_ERR_PRXCOND;
if (!(s->flags & SN_FINST_MASK))
s->flags |= SN_FINST_R;
http_silent_debug(__LINE__, s);
return 0;
}