]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MINOR] http: make it possible to pretend keep-alive when doing close
authorWilly Tarreau <w@1wt.eu>
Mon, 5 Apr 2010 14:15:16 +0000 (16:15 +0200)
committerWilly Tarreau <w@1wt.eu>
Mon, 5 Apr 2010 14:26:34 +0000 (16:26 +0200)
Some servers do not completely conform with RFC2616 requirements for
keep-alive when they receive a request with "Connection: close". More
specifically, they don't bother using chunked encoding, so the client
never knows whether the response is complete or not. One immediately
visible effect is that haproxy cannot maintain client connections alive.
The second issue is that truncated responses may be cached on clients
in case of network error or timeout.

Óscar Frías Barranco reported this issue on Tomcat 6.0.20, and
Patrik Nilsson with Jetty 6.1.21.

Cyril Bonté proposed this smart idea of pretending we run keep-alive
with the server and closing it at the last moment as is already done
with option forceclose. The advantage is that we only change one
emitted header but not the overall behaviour.

Since some servers such as nginx are able to close the connection
very quickly and save network packets when they're aware of the
close negociation in advance, we don't enable this behaviour by
default.

"option http-pretend-keepalive" will have to be used for that, in
conjunction with "option http-server-close".

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

index 2292674a8c334d7d8a9e4e8a827c8407c4c5fb4e..2b980d5a3f0b62fae6e3d628a47a6c806e9623d5 100644 (file)
@@ -829,6 +829,7 @@ option dontlognull                   (*)  X          X         X         -
 option forceclose                    (*)  X          X         X         X
 -- keyword -------------------------- defaults - frontend - listen -- backend -
 option forwardfor                         X          X         X         X
+option http-pretend-keepalive        (*)  X          X         X         X
 option http-server-close             (*)  X          X         X         X
 option http-use-proxy-header         (*)  X          X         X         -
 option httpchk                            X          -         X         X
@@ -2619,10 +2620,14 @@ no option forceclose
   means we can close the connection to the server very quickly, releasing some
   resources earlier than with httpclose.
 
+  This option may also be combined with "option http-pretend-keepalive", which
+  will disable sending of the "Connection: close" header, but will still cause
+  the connection to be closed once the whole response is received.
+
   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.
 
-  See also : "option httpclose"
+  See also : "option httpclose" and "option http-pretend-keepalive"
 
 
 option forwardfor [ except <network> ] [ header <name> ]
@@ -2685,6 +2690,48 @@ option forwardfor [ except <network> ] [ header <name> ]
   See also : "option httpclose"
 
 
+option http-pretend-keepalive
+no option http-pretend-keepalive
+  Define whether haproxy will announce keepalive to the server or not
+  May be used in sections :   defaults | frontend | listen | backend
+                                 yes   |    yes   |   yes  |   yes
+  Arguments : none
+
+  When running with "option http-server-close" or "option forceclose", haproxy
+  adds a "Connection: close" header to the request forwarded to the server.
+  Unfortunately, when some servers see this header, they automatically refrain
+  from using the chunked encoding for responses of unknown length, while this
+  is totally unrelated. The immediate effect is that this prevents haproxy from
+  maintaining the client connection alive. A second effect is that a client or
+  a cache could receive an incomplete response without being aware of it, and
+  consider the response complete.
+
+  By setting "option http-pretend-keepalive", haproxy will make the server
+  believe it will keep the connection alive. The server will then not fall back
+  to the abnormal undesired above. When haproxy gets the whole response, it
+  will close the connection with the server just as it would do with the
+  "forceclose" option. That way the client gets a normal response and the
+  connection is correctly closed on the server side.
+
+  It is recommended not to enable this option by default, because most servers
+  will more efficiently close the connection themselves after the last packet,
+  and release its buffers slightly earlier. Also, the added packet on the
+  network could slightly reduce the overall peak performance. However it is
+  worth noting that when this option is enabled, haproxy will have slightly
+  less work to do. So if haproxy is the bottleneck on the whole architecture,
+  enabling this option might save a few CPU cycles.
+
+  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.
+  This option has no effect if it is combined with "option httpclose", which
+  has precedence.
+
+  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.
+
+  See also : "option forceclose" and "option http-server-close"
+
+
 option http-server-close
 no option http-server-close
   Enable or disable HTTP connection closing on the server side
@@ -2698,7 +2745,10 @@ no option http-server-close
   fastest session reuse on the server side to save server resources, similarly
   to "option forceclose". It also permits non-keepalive capable servers to be
   served in keep-alive mode to the clients if they conform to the requirements
-  of RFC2616.
+  of RFC2616. Please note that some servers do not always conform to those
+  requirements when they see "Connection: close" in the request. The effect
+  will be that keep-alive will never be used. A workaround consists in enabling
+  "option http-pretend-keepalive".
 
   At the moment, logs will not indicate whether requests came from the same
   session or not. The accept date reported in the logs corresponds to the end
@@ -2716,7 +2766,8 @@ no option http-server-close
   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.
 
-  See also : "option forceclose" and "option httpclose"
+  See also : "option forceclose", "option http-pretend-keepalive" and
+             "option httpclose".
 
 
 option http-use-proxy-header
index c3fd01a519e0e06bc7aac193566cbec00049e61f..fb34513821f685e70dab1bb13f9064a60301babe 100644 (file)
 #define PR_O2_USE_PXHDR 0x00040000      /* use Proxy-Connection for proxy requests */
 #define PR_O2_CHK_SNDST 0x00080000      /* send the state of each server along with HTTP health checks */
 #define PR_O2_SSL3_CHK  0x00100000      /* use SSLv3 CLIENT_HELLO packets for server health */
+#define PR_O2_FAKE_KA   0x00200000      /* pretend we do keep-alive with server eventhough we close */
 /* end of proxy->options2 */
 
 /* bits for sticking rules */
index 6af60da6c51c8e18dbb0333e70249b54b0a7ed3f..dca5e6c57ec8bbd4eed1e063d621223ddcba46dd 100644 (file)
@@ -150,6 +150,7 @@ static const struct cfg_opt cfg_opts2[] =
        { "tcp-smart-connect",            PR_O2_SMARTCON,  PR_CAP_BE, 0, 0 },
        { "independant-streams",          PR_O2_INDEPSTR,  PR_CAP_FE|PR_CAP_BE, 0, 0 },
        { "http-use-proxy-header",        PR_O2_USE_PXHDR, PR_CAP_FE, 0, PR_MODE_HTTP },
+       { "http-pretend-keepalive",       PR_O2_FAKE_KA,   PR_CAP_FE, 0, PR_MODE_HTTP },
        { NULL, 0, 0, 0 }
 };
 
index 90f984827dc68ca1b88a8226de76f2bd972e7daa..6e8fa411f20e2646d048c5d4bae13be7a307c932 100644 (file)
@@ -2967,7 +2967,8 @@ int http_process_req_common(struct session *s, struct buffer *req, int an_bit, s
                        /* parse the Connection header and possibly clean it */
                        int to_del = 0;
                        if ((txn->flags & TX_REQ_VER_11) ||
-                           (txn->flags & TX_CON_WANT_MSK) >= TX_CON_WANT_SCL)
+                           ((txn->flags & TX_CON_WANT_MSK) >= TX_CON_WANT_SCL &&
+                            !((s->fe->options2|s->be->options2) & PR_O2_FAKE_KA)))
                                to_del |= 2; /* remove "keep-alive" */
                        if (!(txn->flags & TX_REQ_VER_11))
                                to_del |= 1; /* remove "close" */
@@ -3405,11 +3406,14 @@ int http_process_request(struct session *s, struct buffer *req, int an_bit)
                unsigned int want_flags = 0;
 
                if (txn->flags & TX_REQ_VER_11) {
-                       if ((txn->flags & TX_CON_WANT_MSK) >= TX_CON_WANT_SCL ||
+                       if (((txn->flags & TX_CON_WANT_MSK) >= TX_CON_WANT_SCL &&
+                            !((s->fe->options2|s->be->options2) & PR_O2_FAKE_KA)) ||
                            ((s->fe->options|s->be->options) & PR_O_HTTP_CLOSE))
                                want_flags |= TX_CON_CLO_SET;
                } else {
-                       if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL)
+                       if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL ||
+                           (((s->fe->options2|s->be->options2) & PR_O2_FAKE_KA) &&
+                            !((s->fe->options|s->be->options) & PR_O_HTTP_CLOSE)))
                                want_flags |= TX_CON_KAL_SET;
                }