[MINOR] http: make it possible to pretend keep-alive when doing close

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".
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 2292674a..2b980d5 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -829,6 +829,7 @@
 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 @@
   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> ]
@@ -2684,6 +2689,48 @@
 
   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
@@ -2698,7 +2745,10 @@
   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 @@
   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
diff --git a/include/types/proxy.h b/include/types/proxy.h
index c3fd01a..fb34513 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -138,6 +138,7 @@
 #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 */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 6af60da..dca5e6c 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -150,6 +150,7 @@
 	{ "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 }
 };
 
diff --git a/src/proto_http.c b/src/proto_http.c
index 90f9848..6e8fa41 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -2967,7 +2967,8 @@
 			/* 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 @@
 		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;
 		}