[MAJOR] http: rework response Connection header handling

This one is the next step of previous patch. It correctly computes
the response mode and the Connection flag transformations depending
on the request mode and version, and the response version and headers.

We're now also able to add "Connection: keep-alive", and to convert
server's close during a keep-alive connection to a server-close
connection.
This commit is contained in:
Willy Tarreau 2010-01-18 19:08:45 +01:00
parent bbf0b37f6c
commit 6046652253
2 changed files with 54 additions and 137 deletions

View File

@ -71,7 +71,7 @@ SCL 1.1 both CLO del_ka
CLO 1.0 - CLO -
CLO 1.0 ka CLO del_ka
CLO 1.0 close CLO del_clo
CLO 1.0 close CLO del_close
CLO 1.0 both CLO del_ka, del_close
CLO 1.1 - CLO add_close
@ -149,7 +149,7 @@ KAL 1.1 ka 1.1 KAL del_ka
KAL 1.1 close 1.0 SCL del_close, add_ka
KAL 1.1 close 1.1 SCL del_close
KAL 1.1 both 1.0 SCL del_close
KAL 1.1 both 1.1 SCL del_ka
KAL 1.1 both 1.1 SCL del_ka, del_close
SCL 1.0 - any SCL add_ka
SCL 1.0 ka any SCL -
@ -163,7 +163,7 @@ SCL 1.1 ka 1.1 SCL del_ka
SCL 1.1 close 1.0 SCL del_close, add_ka
SCL 1.1 close 1.1 SCL del_close
SCL 1.1 both 1.0 SCL del_close
SCL 1.1 both 1.1 SCL del_ka
SCL 1.1 both 1.1 SCL del_ka, del_close
CLO 1.0 - any CLO -
CLO 1.0 ka any CLO del_ka

View File

@ -4260,7 +4260,7 @@ int http_wait_for_response(struct session *s, struct buffer *rep, int an_bit)
txn->flags |= TX_RES_VER_11;
/* "connection" has not been parsed yet */
txn->flags &= ~(TX_HDR_CONN_PRS | TX_HDR_CONN_CLO | TX_HDR_CONN_KAL);
txn->flags &= ~(TX_HDR_CONN_PRS|TX_HDR_CONN_CLO|TX_HDR_CONN_KAL|TX_CON_CLO_SET|TX_CON_KAL_SET);
/* transfer length unknown*/
txn->flags &= ~TX_RES_XFER_LEN;
@ -4424,9 +4424,6 @@ int http_process_res_common(struct session *t, struct buffer *rep, int an_bit, s
struct http_msg *msg = &txn->rsp;
struct proxy *cur_proxy;
struct wordlist *wl;
int conn_ka = 0, conn_cl = 0;
int must_close = 0;
int must_del_close = 0, must_keep = 0;
DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n",
now_ms, __FUNCTION__,
@ -4457,135 +4454,46 @@ int http_process_res_common(struct session *t, struct buffer *rep, int an_bit, s
* ambiguous cases such as both close and keepalive are seen, then we
* will fall back to explicit close. Note that we won't take risks with
* HTTP/1.0 clients which may not necessarily understand keep-alive.
* See doc/internals/connection-header.txt for the complete matrix.
*/
if ((txn->meth != HTTP_METH_CONNECT) &&
(txn->status >= 200) && !(txn->flags & TX_HDR_CONN_PRS) &&
((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;
int to_del = 0;
ctx.idx = 0;
while (http_find_header2("Connection", 10, msg->sol, &txn->hdr_idx, &ctx)) {
if (ctx.vlen == 5 && strncasecmp(ctx.line + ctx.val, "close", 5) == 0)
conn_cl = 1;
else if (ctx.vlen == 10 && strncasecmp(ctx.line + ctx.val, "keep-alive", 10) == 0)
conn_ka = 1;
}
if (conn_cl) {
/* close header present */
may_close = 1;
if (conn_ka) /* we have both close and keep-alive */
may_keep = 1;
}
else if (conn_ka) {
/* keep-alive alone */
may_keep = 1;
}
else {
/* no close nor keep-alive header */
if (txn->flags & TX_RES_VER_11)
may_keep = 1;
else
may_close = 1;
if (txn->flags & TX_REQ_VER_11)
may_keep = 1;
else
may_close = 1;
}
/* let's update the transaction status to reflect any close.
* Note that ambiguous cases with keep & close will also be
* handled. We also explicitly state that we will close in
* case of an ambiguous response having no content-length.
*/
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))
/* on unknown transfer length, we must close */
if (!(txn->flags & TX_RES_XFER_LEN) &&
(txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN)
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 | 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
*/
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)) {
must_del_close = 1;
if (!(txn->flags & TX_REQ_VER_11))
must_keep = 1;
/* now adjust header transformations depending on current state */
if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_TUN ||
(txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_CLO) {
to_del |= 2; /* remove "keep-alive" on any response */
if (!(txn->flags & TX_RES_VER_11))
to_del |= 1; /* remove "close" for HTTP/1.0 responses */
}
else { /* SCL / KAL */
to_del |= 1; /* remove "close" on any response */
if ((txn->flags & (TX_RES_VER_11|TX_REQ_VER_11)) == (TX_RES_VER_11|TX_REQ_VER_11))
to_del |= 2; /* remove "keep-alive" on pure 1.1 responses */
}
txn->flags |= TX_CON_HDR_PARS;
/* Parse and remove some headers from the connection header */
http_parse_connection_header(txn, msg, rep, to_del);
/* Some keep-alive responses are converted to Server-close if
* the server wants to close.
*/
if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL) {
if ((txn->flags & TX_HDR_CONN_CLO) ||
(txn->flags & (TX_HDR_CONN_KAL|TX_RES_VER_11)) == 0)
txn->flags = (txn->flags & ~TX_CON_WANT_MSK) | TX_CON_WANT_SCL;
}
}
/* We might have to check for "Connection:" if the server
* returns a connection status that is not compatible with
* the client's or with the config.
*/
if ((txn->status >= 200) && (must_del_close|must_close) && (conn_cl|conn_ka)) {
char *cur_ptr, *cur_end, *cur_next;
int cur_idx, old_idx, delta, val;
int must_delete;
struct hdr_idx_elem *cur_hdr;
/* we just have to remove the headers if both sides are 1.0 */
must_delete = !(txn->flags & TX_REQ_VER_11) && !(txn->flags & TX_RES_VER_11);
/* same if we want to re-enable keep-alive on 1.1 */
must_delete |= must_del_close;
cur_next = txn->rsp.sol + hdr_idx_first_pos(&txn->hdr_idx);
for (old_idx = 0; (cur_idx = txn->hdr_idx.v[old_idx].next); old_idx = cur_idx) {
cur_hdr = &txn->hdr_idx.v[cur_idx];
cur_ptr = cur_next;
cur_end = cur_ptr + cur_hdr->len;
cur_next = cur_end + cur_hdr->cr + 1;
val = http_header_match2(cur_ptr, cur_end, "Connection", 10);
if (!val)
continue;
/* 3 possibilities :
* - we have already set "Connection: close" or we're in
* HTTP/1.0, so we remove this line.
* - we have not yet set "Connection: close", but this line
* indicates close. We leave it untouched and set the flag.
* - we have not yet set "Connection: close", and this line
* indicates non-close. We replace it and set the flag.
*/
if (must_delete) {
delta = buffer_replace2(rep, cur_ptr, cur_next, NULL, 0);
http_msg_move_end(&txn->rsp, delta);
cur_next += delta;
txn->hdr_idx.v[old_idx].next = cur_hdr->next;
txn->hdr_idx.used--;
cur_hdr->len = 0;
must_close = 0;
must_del_close = 0;
} else {
if (cur_end - cur_ptr - val != 5 ||
strncasecmp(cur_ptr + val, "close", 5) != 0) {
delta = buffer_replace2(rep, cur_ptr + val, cur_end,
"close", 5);
cur_next += delta;
cur_hdr->len += delta;
http_msg_move_end(&txn->rsp, delta);
}
must_delete = 1;
must_close = 0;
}
} /* for loop */
} /* if must close keep-alive */
if (1) {
/*
* 3: we will have to evaluate the filters.
@ -4748,21 +4656,30 @@ int http_process_res_common(struct session *t, struct buffer *rep, int an_bit, s
}
/*
* 8: add "Connection: close" if needed and not yet set. This is
* only needed for 1.1 responses since we know there is no other
* Connection header.
* 8: adjust "Connection: close" or "Connection: keep-alive" if needed.
*/
if (must_close && (txn->flags & TX_RES_VER_11)) {
if (unlikely(http_header_add_tail2(rep, &txn->rsp, &txn->hdr_idx,
"Connection: close", 17) < 0))
goto return_bad_resp;
must_close = 0;
}
else if (must_keep && !(txn->flags & TX_REQ_VER_11)) {
if (unlikely(http_header_add_tail2(rep, &txn->rsp, &txn->hdr_idx,
"Connection: keep-alive", 22) < 0))
goto return_bad_resp;
must_keep = 0;
if (((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN) ||
((t->fe->options|t->be->options) & PR_O_HTTP_CLOSE)) {
unsigned int want_flags = 0;
if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL ||
(txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL) {
/* we want a keep-alive response here. Keep-alive header
* required if either side is not 1.1.
*/
if ((txn->flags & (TX_REQ_VER_11|TX_RES_VER_11)) != (TX_REQ_VER_11|TX_RES_VER_11))
want_flags |= TX_CON_KAL_SET;
}
else {
/* we want a close response here. Close header required if
* the server is 1.1, regardless of the client.
*/
if (txn->flags & TX_RES_VER_11)
want_flags |= TX_CON_CLO_SET;
}
if (want_flags != (txn->flags & (TX_CON_CLO_SET|TX_CON_KAL_SET)))
http_change_connection_header(txn, msg, rep, want_flags);
}
if (txn->flags & TX_RES_XFER_LEN)