mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-09-21 22:01:31 +02:00
[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.
This commit is contained in:
parent
6939b5522d
commit
88d349d25d
@ -788,6 +788,8 @@ option forwardfor X X X X
|
||||
option httpchk X - X X
|
||||
[no] option http-server-
|
||||
close X X X X
|
||||
[no] option http-use-proxy-
|
||||
header X X X -
|
||||
[no] option httpclose X X X X
|
||||
option httplog X X X X
|
||||
[no] option http_proxy X X X X
|
||||
@ -2623,6 +2625,35 @@ no option http-server-close
|
||||
See also : "option forceclose" and "option httpclose"
|
||||
|
||||
|
||||
option http-use-proxy-header
|
||||
[no] option http-use-proxy-header
|
||||
Make use of non-standard Proxy-Connection header instead of Connection
|
||||
May be used in sections : defaults | frontend | listen | backend
|
||||
yes | yes | yes | no
|
||||
Arguments : none
|
||||
|
||||
While RFC2616 explicitly states that HTTP/1.1 agents must use the
|
||||
Connection header to indicate their wish of persistent or non-persistent
|
||||
connections, both browsers and proxies ignore this header for proxied
|
||||
connections and make use of the undocumented, non-standard Proxy-Connection
|
||||
header instead. The issue begins when trying to put a load balancer between
|
||||
browsers and such proxies, because there will be a difference between what
|
||||
haproxy understands and what the client and the proxy agree on.
|
||||
|
||||
By setting this option in a frontend, haproxy can automatically switch to use
|
||||
that non-standard header if it sees proxied requests. A proxied request is
|
||||
defined here as one where the URI begins with neither a '/' nor a '*'. The
|
||||
choice of header only affects requests passing through proxies making use of
|
||||
one of the "httpclose", "forceclose" and "http-server-close" options. Note
|
||||
that this option can only be specified in a frontend and will affect the
|
||||
request along its whole life.
|
||||
|
||||
This option should normally never be used, except in front of a proxy.
|
||||
|
||||
See also : "option httpclose", "option forceclose" and "option
|
||||
http-server-close".
|
||||
|
||||
|
||||
option httpclose
|
||||
no option httpclose
|
||||
Enable or disable passive HTTP connection closing
|
||||
|
@ -98,6 +98,7 @@
|
||||
#define TX_HDR_CONN_PRS 0x08000000 /* "connection" header already parsed (req or res), results below */
|
||||
#define TX_HDR_CONN_CLO 0x10000000 /* "Connection: close" was present at least once */
|
||||
#define TX_HDR_CONN_KAL 0x20000000 /* "Connection: keep-alive" was present at least once */
|
||||
#define TX_USE_PX_CONN 0x40000000 /* Use "Proxy-Connection" instead of "Connection" */
|
||||
|
||||
|
||||
/* The HTTP parser is more complex than it looks like, because we have to
|
||||
|
@ -135,6 +135,7 @@
|
||||
#define PR_O2_AS_M_ANY 0x00010000 /* mask covering all PR_O2_AS_M_* values */
|
||||
|
||||
#define PR_O2_MYSQL_CHK 0x00020000 /* use MYSQL check for server health */
|
||||
#define PR_O2_USE_PXHDR 0x00040000 /* use Proxy-Connection for proxy requests */
|
||||
/* end of proxy->options2 */
|
||||
|
||||
/* bits for sticking rules */
|
||||
|
@ -147,6 +147,7 @@ static const struct cfg_opt cfg_opts2[] =
|
||||
{ "tcp-smart-accept", PR_O2_SMARTACC, PR_CAP_FE, 0 },
|
||||
{ "tcp-smart-connect", PR_O2_SMARTCON, PR_CAP_BE, 0 },
|
||||
{ "independant-streams", PR_O2_INDEPSTR, PR_CAP_FE|PR_CAP_BE, 0 },
|
||||
{ "http-use-proxy-header", PR_O2_USE_PXHDR, PR_CAP_FE, 0 },
|
||||
{ NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
|
@ -761,8 +761,14 @@ void perform_http_redirect(struct session *s, struct stream_interface *si)
|
||||
|
||||
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 @@ static int http_upgrade_v09_to_v10(struct buffer *req, struct http_msg *msg, str
|
||||
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_parse_connection_header(struct http_txn *txn, struct http_msg *msg, st
|
||||
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 @@ void http_change_connection_header(struct http_txn *txn, struct http_msg *msg, s
|
||||
|
||||
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 @@ int http_wait_for_request(struct session *s, struct buffer *req, int an_bit)
|
||||
/* "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 @@ int http_process_req_common(struct session *s, struct buffer *req, int an_bit, s
|
||||
(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 @@ int http_process_req_common(struct session *s, struct buffer *req, int an_bit, s
|
||||
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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user