From 0dfdf19b6438c2cea47b1dea0442d65bacfc77cf Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Tue, 5 Jan 2010 11:33:11 +0100 Subject: [PATCH] [MEDIUM] http: restore the original behaviour of option httpclose Historically, "option httpclose" has always worked the same way. It only mangles the "Connection" header in the request and the response if needed, but does not affect the connection by itself, and ignores any further data. It is dangerous to change this behaviour without leaving any other alternative. If an active close is desired, it's better to make use of "option forceclose" which does exactly what it intends to do. So as of now, "option httpclose" will only mangle the headers as before, and will only affect the connection by itself when combined with another connection-related option (eg: keepalive or server-close). --- doc/configuration.txt | 22 +++++++++++----- src/proto_http.c | 61 +++++++++++++++++++++++++++---------------- 2 files changed, 53 insertions(+), 30 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index 0fa20025c..46787e59d 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -2396,7 +2396,10 @@ no option forceclose When this happens, it is possible to use "option forceclose". It will actively close the outgoing server channel as soon as the server has finished - to respond. This option implicitly enables the "httpclose" option. + to respond. This option implicitly enables the "httpclose" option. Note that + this option also enables the parsing of the full request and response, which + means we can close the connection to the server very quickly, releasing some + resources earlier than with httpclose. If this option has been enabled in a "defaults" section, it can be disabled in a specific instance by prepending the "no" keyword before it. @@ -2535,8 +2538,9 @@ no option http-server-close This option may be set both in a frontend and in a backend. It is enabled if at least one of the frontend or backend holding a connection has it enabled. - It is worth noting that "option forceclose" has precedence over "httpclose", - which itself has precedence over "option http-server-close". + It is worth noting that "option forceclose" has precedence over "option + http-server-close" and that combining "http-server-close" with "httpclose" + basically achieve the same result as "forceclose". If this option has been enabled in a "defaults" section, it can be disabled in a specific instance by prepending the "no" keyword before it. @@ -2562,14 +2566,18 @@ no option httpclose be removed. It seldom happens that some servers incorrectly ignore this header and do not - close the connection eventough they reply "Connection: close". For this - reason, they are not compatible with older HTTP 1.0 browsers. If this - happens it is possible to use the "option forceclose" which actively closes - the request connection once the server responds. + close the connection eventhough they reply "Connection: close". For this + reason, they are not compatible with older HTTP 1.0 browsers. If this happens + it is possible to use the "option forceclose" which actively closes the + request connection once the server responds. Option "forceclose" also + releases the server connection earlier because it does not have to wait for + the client to acknowledge it. This option may be set both in a frontend and in a backend. It is enabled if at least one of the frontend or backend holding a connection has it enabled. If "option forceclose" is specified too, it has precedence over "httpclose". + If "option http-server-close" is enabled at the same time as "httpclose", it + basically achieves the same result as "option forceclose". If this option has been enabled in a "defaults" section, it can be disabled in a specific instance by prepending the "no" keyword before it. diff --git a/src/proto_http.c b/src/proto_http.c index ae7288a73..07e701de9 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -1808,17 +1808,21 @@ void http_req_parse_connection_header(struct http_txn *txn) * it, so that it can be enforced later. */ - if (txn->flags & TX_REQ_VER_11) { /* HTTP/1.1 */ + if (conn_cl && conn_ka) { + txn->flags = (txn->flags & ~TX_CON_WANT_MSK) | TX_CON_WANT_CLO; + } + else if (txn->flags & TX_REQ_VER_11) { /* HTTP/1.1 */ if (conn_cl) { - txn->flags = (txn->flags & ~TX_CON_WANT_MSK) | TX_CON_WANT_CLO; - if (!conn_ka) - txn->flags |= TX_REQ_CONN_CLO; + txn->flags |= TX_REQ_CONN_CLO; + if ((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN) + txn->flags = (txn->flags & ~TX_CON_WANT_MSK) | TX_CON_WANT_CLO; } } else { /* HTTP/1.0 */ - if (!conn_ka) - txn->flags = (txn->flags & ~TX_CON_WANT_MSK) | TX_CON_WANT_CLO | TX_REQ_CONN_CLO; - else if (conn_cl) - txn->flags = (txn->flags & ~TX_CON_WANT_MSK) | TX_CON_WANT_CLO; + if (!conn_ka) { + txn->flags |= TX_REQ_CONN_CLO; + if ((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN) + txn->flags = (txn->flags & ~TX_CON_WANT_MSK) | TX_CON_WANT_CLO; + } } txn->flags |= TX_CON_HDR_PARS; } @@ -2616,6 +2620,9 @@ int http_process_req_common(struct session *s, struct buffer *req, int an_bit, s /* Until set to anything else, the connection mode is set as TUNNEL. It will * only change if both the request and the config reference something else. + * Option httpclose by itself does not set a mode, it remains a tunnel mode + * in which headers are mangled. However, if another mode is set, it will + * affect it (eg: server-close/keep-alive + httpclose = close). */ if ((txn->meth != HTTP_METH_CONNECT) && @@ -2625,10 +2632,7 @@ int http_process_req_common(struct session *s, struct buffer *req, int an_bit, s tmp = TX_CON_WANT_KAL; if ((s->fe->options|s->be->options) & PR_O_SERVER_CLO) tmp = TX_CON_WANT_SCL; - if ((s->fe->options|s->be->options) & (PR_O_HTTP_CLOSE|PR_O_FORCE_CLO)) - tmp = TX_CON_WANT_CLO; - - if (!(txn->flags & TX_REQ_XFER_LEN)) + if ((s->fe->options|s->be->options) & PR_O_FORCE_CLO) tmp = TX_CON_WANT_CLO; if (!(txn->flags & TX_CON_HDR_PARS)) @@ -2636,6 +2640,13 @@ int http_process_req_common(struct session *s, struct buffer *req, int an_bit, s if ((txn->flags & TX_CON_WANT_MSK) < tmp) txn->flags = (txn->flags & ~TX_CON_WANT_MSK) | tmp; + + if ((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN) { + if ((s->fe->options|s->be->options) & PR_O_HTTP_CLOSE) + txn->flags = (txn->flags & ~TX_CON_WANT_MSK) | TX_CON_WANT_CLO; + if (!(txn->flags & TX_REQ_XFER_LEN)) + txn->flags = (txn->flags & ~TX_CON_WANT_MSK) | TX_CON_WANT_CLO; + } } /* We're really certain of the connection mode (tunnel, close, keep-alive) @@ -2650,7 +2661,9 @@ int http_process_req_common(struct session *s, struct buffer *req, int an_bit, s * Connection header exists. Note that a CONNECT method will not enter * here. */ - if (!(txn->flags & TX_REQ_CONN_CLO) && ((txn->flags & TX_CON_WANT_MSK) >= TX_CON_WANT_SCL)) { + if (!(txn->flags & TX_REQ_CONN_CLO) && + ((txn->flags & TX_CON_WANT_MSK) >= TX_CON_WANT_SCL || + ((s->fe->options|s->be->options) & PR_O_HTTP_CLOSE))) { char *cur_ptr, *cur_end, *cur_next; int old_idx, delta, val; int must_delete; @@ -3070,7 +3083,9 @@ int http_process_request(struct session *s, struct buffer *req, int an_bit) } /* 11: add "Connection: close" if needed and not yet set. */ - if (!(txn->flags & TX_REQ_CONN_CLO) && ((txn->flags & TX_CON_WANT_MSK) >= TX_CON_WANT_SCL)) { + if (!(txn->flags & TX_REQ_CONN_CLO) && + ((txn->flags & TX_CON_WANT_MSK) >= TX_CON_WANT_SCL || + ((s->fe->options|s->be->options) & PR_O_HTTP_CLOSE))) { if (unlikely(http_header_add_tail2(req, &txn->req, &txn->hdr_idx, "Connection: close", 17) < 0)) goto return_bad_req; @@ -3471,7 +3486,7 @@ int http_sync_req_state(struct session *s) buffer_shutw_now(buf); buf->cons->flags |= SI_FL_NOLINGER; } - else if ((s->fe->options | s->be->options) & PR_O_FORCE_CLO) { + else if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_CLO) { /* Option forceclose is set, let's enforce it now * that we're not expecting any new data to come. */ @@ -4322,9 +4337,9 @@ int http_process_res_common(struct session *t, struct buffer *rep, int an_bit, s */ if ((txn->meth != HTTP_METH_CONNECT) && - (txn->status >= 200) && - (txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN && - !(txn->flags & TX_CON_HDR_PARS)) { + (txn->status >= 200) && !(txn->flags & TX_CON_HDR_PARS) && + ((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN || + ((t->fe->options|t->be->options) & PR_O_HTTP_CLOSE))) { int may_keep = 0, may_close = 0; /* how it may be understood */ struct hdr_ctx ctx; @@ -4364,19 +4379,19 @@ int http_process_res_common(struct session *t, struct buffer *rep, int an_bit, s * handled. We also explicitly state that we will close in * case of an ambiguous response having no content-length. */ - if ((may_close && + if ((may_close && ((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN) && (may_keep || ((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_SCL))) || !(txn->flags & TX_RES_XFER_LEN)) txn->flags = (txn->flags & ~TX_CON_WANT_MSK) | TX_CON_WANT_CLO; /* Now we must adjust the response header : - * - set "close" if may_keep and WANT_CLO + * - set "close" if may_keep and (WANT_CLO | httpclose) * - remove "close" if WANT_SCL and REQ_1.1 and may_close and (content-length or TE_CHNK) * - add "keep-alive" if WANT_SCL and REQ_1.0 and may_close and content-length - * - * Until we support the server-close mode, we'll only support the set "close". */ - if (may_keep && (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_CLO) + if (may_keep && + ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_CLO || + ((t->fe->options|t->be->options) & PR_O_HTTP_CLOSE))) must_close = 1; else if (((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL) && may_close && (txn->flags & TX_RES_XFER_LEN)) {