]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MEDIUM] http: add support for Proxy-Connection header
authorWilly Tarreau <w@1wt.eu>
Mon, 25 Jan 2010 11:15:43 +0000 (12:15 +0100)
committerWilly Tarreau <w@1wt.eu>
Mon, 25 Jan 2010 11:48:26 +0000 (12:48 +0100)
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.

doc/configuration.txt
include/types/proto_http.h
include/types/proxy.h
src/cfgparse.c
src/proto_http.c

index 7c87dc82ff546c7485f0f3e8fd00a6fa54259c56..a591a21f9cbd3129123bb9f9b449026063cfae4f 100644 (file)
@@ -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
index ae4e1eb7da6cf216cb315b4b1029c701b9a9f747..3d38d57b21219fdddd460fb7b4ffd16ce651c8ef 100644 (file)
@@ -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
index c4fb5053294e64b4a8d76e441c407fb0b3d51894..0571f1747760bed4d46bb894770bc1bcdfd89d0f 100644 (file)
 #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 */
index eea9d3e7d5055d5c3341c1eeda3fa4e6cff84cc5..109c0583dfbf7948693b8029b193ba02647ed0a9 100644 (file)
@@ -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 }
 };
 
index 7cb4faa491a30805ab79b9889135a2592be2de28..b3608d4e4cee58014a44be7751f3a73b9d8816ad 100644 (file)
@@ -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;
                        }