[MEDIUM] http: add support for Proxy-Connection header
Despite what is explicitly stated in HTTP specifications,
browsers still use the undocumented Proxy-Connection header
instead of the Connection header when they connect through
a proxy. As such, proxies generally implement support for
this stupid header name, breaking the standards and making
it harder to support keep-alive between clients and proxies.
Thus, we add a new "option http-use-proxy-header" to tell
haproxy that if it sees requests which look like proxy
requests, it should use the Proxy-Connection header instead
of the Connection header.
diff --git a/src/proto_http.c b/src/proto_http.c
index 7cb4faa..b3608d4 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -761,8 +761,14 @@
memcpy(rdr.str + rdr.len, path, len);
rdr.len += len;
- memcpy(rdr.str + rdr.len, "\r\nConnection: close\r\n\r\n", 23);
- rdr.len += 23;
+
+ if (unlikely(txn->flags & TX_USE_PX_CONN)) {
+ memcpy(rdr.str + rdr.len, "\r\nProxy-Connection: close\r\n\r\n", 29);
+ rdr.len += 29;
+ } else {
+ memcpy(rdr.str + rdr.len, "\r\nConnection: close\r\n\r\n", 23);
+ rdr.len += 23;
+ }
/* prepare to return without error. */
si->shutr(si);
@@ -1849,13 +1855,20 @@
void http_parse_connection_header(struct http_txn *txn, struct http_msg *msg, struct buffer *buf, int to_del)
{
struct hdr_ctx ctx;
+ const char *hdr_val = "Connection";
+ int hdr_len = 10;
if (txn->flags & TX_HDR_CONN_PRS)
return;
+ if (unlikely(txn->flags & TX_USE_PX_CONN)) {
+ hdr_val = "Proxy-Connection";
+ hdr_len = 16;
+ }
+
ctx.idx = 0;
txn->flags &= ~(TX_CON_KAL_SET|TX_CON_CLO_SET);
- while (http_find_header2("Connection", 10, msg->sol, &txn->hdr_idx, &ctx)) {
+ while (http_find_header2(hdr_val, hdr_len, msg->sol, &txn->hdr_idx, &ctx)) {
if (ctx.vlen >= 10 && word_match(ctx.line + ctx.val, ctx.vlen, "keep-alive", 10)) {
txn->flags |= TX_HDR_CONN_KAL;
if ((to_del & 2) && buf)
@@ -1884,11 +1897,19 @@
void http_change_connection_header(struct http_txn *txn, struct http_msg *msg, struct buffer *buf, int wanted)
{
struct hdr_ctx ctx;
+ const char *hdr_val = "Connection";
+ int hdr_len = 10;
ctx.idx = 0;
+
+ if (unlikely(txn->flags & TX_USE_PX_CONN)) {
+ hdr_val = "Proxy-Connection";
+ hdr_len = 16;
+ }
+
txn->flags &= ~(TX_CON_CLO_SET | TX_CON_KAL_SET);
- while (http_find_header2("Connection", 10, msg->sol, &txn->hdr_idx, &ctx)) {
+ while (http_find_header2(hdr_val, hdr_len, msg->sol, &txn->hdr_idx, &ctx)) {
if (ctx.vlen >= 10 && word_match(ctx.line + ctx.val, ctx.vlen, "keep-alive", 10)) {
if (wanted & TX_CON_KAL_SET)
txn->flags |= TX_CON_KAL_SET;
@@ -1908,12 +1929,24 @@
if ((wanted & TX_CON_CLO_SET) && !(txn->flags & TX_CON_CLO_SET)) {
txn->flags |= TX_CON_CLO_SET;
- http_header_add_tail2(buf, msg, &txn->hdr_idx, "Connection: close", 17);
+ hdr_val = "Connection: close";
+ hdr_len = 17;
+ if (unlikely(txn->flags & TX_USE_PX_CONN)) {
+ hdr_val = "Proxy-Connection: close";
+ hdr_len = 23;
+ }
+ http_header_add_tail2(buf, msg, &txn->hdr_idx, hdr_val, hdr_len);
}
if ((wanted & TX_CON_KAL_SET) && !(txn->flags & TX_CON_KAL_SET)) {
txn->flags |= TX_CON_KAL_SET;
- http_header_add_tail2(buf, msg, &txn->hdr_idx, "Connection: keep-alive", 22);
+ hdr_val = "Connection: keep-alive";
+ hdr_len = 22;
+ if (unlikely(txn->flags & TX_USE_PX_CONN)) {
+ hdr_val = "Proxy-Connection: keep-alive";
+ hdr_len = 28;
+ }
+ http_header_add_tail2(buf, msg, &txn->hdr_idx, hdr_val, hdr_len);
}
return;
}
@@ -2541,6 +2574,19 @@
/* "connection" has not been parsed yet */
txn->flags &= ~(TX_HDR_CONN_PRS | TX_HDR_CONN_CLO | TX_HDR_CONN_KAL);
+ /* if the frontend has "option http-use-proxy-header", we'll check if
+ * we have what looks like a proxied connection instead of a connection,
+ * and in this case set the TX_USE_PX_CONN flag to use Proxy-connection.
+ * Note that this is *not* RFC-compliant, however browsers and proxies
+ * happen to do that despite being non-standard :-(
+ * We consider that a request not beginning with either '/' or '*' is
+ * a proxied connection, which covers both "scheme://location" and
+ * CONNECT ip:port.
+ */
+ if ((s->fe->options2 & PR_O2_USE_PXHDR) &&
+ msg->sol[msg->sl.rq.u] != '/' && msg->sol[msg->sl.rq.u] != '*')
+ txn->flags |= TX_USE_PX_CONN;
+
/* transfer length unknown*/
txn->flags &= ~TX_REQ_XFER_LEN;
@@ -2940,8 +2986,13 @@
(txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL)) {
/* keep-alive possible */
if (!(txn->flags & TX_REQ_VER_11)) {
- memcpy(rdr.str + rdr.len, "\r\nConnection: keep-alive", 24);
- rdr.len += 24;
+ if (unlikely(txn->flags & TX_USE_PX_CONN)) {
+ memcpy(rdr.str + rdr.len, "\r\nProxy-Connection: keep-alive", 30);
+ rdr.len += 30;
+ } else {
+ memcpy(rdr.str + rdr.len, "\r\nConnection: keep-alive", 24);
+ rdr.len += 24;
+ }
}
memcpy(rdr.str + rdr.len, "\r\n\r\n", 4);
rdr.len += 4;
@@ -2956,8 +3007,13 @@
break;
} else {
/* keep-alive not possible */
- memcpy(rdr.str + rdr.len, "\r\nConnection: close\r\n\r\n", 23);
- rdr.len += 23;
+ if (unlikely(txn->flags & TX_USE_PX_CONN)) {
+ memcpy(rdr.str + rdr.len, "\r\nProxy-Connection: close\r\n\r\n", 29);
+ rdr.len += 29;
+ } else {
+ memcpy(rdr.str + rdr.len, "\r\nConnection: close\r\n\r\n", 23);
+ rdr.len += 23;
+ }
stream_int_retnclose(req->prod, &rdr);
goto return_prx_cond;
}