MINOR: http: add option prefer-last-server

When the load balancing algorithm in use is not deterministic, and a previous
request was sent to a server to which haproxy still holds a connection, it is
sometimes desirable that subsequent requests on a same session go to the same
server as much as possible. Note that this is different from persistence, as
we only indicate a preference which haproxy tries to apply without any form
of warranty. The real use is for keep-alive connections sent to servers. When
this option is used, haproxy will try to reuse the same connection that is
attached to the server instead of rebalancing to another server, causing a
close of the connection. This can make sense for static file servers. It does
not make much sense to use this in combination with hashing algorithms.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index d0417c0..b521bf3 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -3794,7 +3794,9 @@
 
   If the client request has to go to another backend or another server due to
   content switching or the load balancing algorithm, the idle connection will
-  immediately be closed and a new one re-opened.
+  immediately be closed and a new one re-opened. Option "prefer-last-server" is
+  available to try optimize server selection so that if the server currently
+  attached to an idle connection is usable, it will be used.
 
   In general it is preferred to use "option http-server-close" with application
   servers, and some static servers might benefit from "option http-keep-alive".
@@ -3815,8 +3817,8 @@
   in a specific instance by prepending the "no" keyword before it.
 
   See also : "option forceclose", "option http-server-close",
-             "option httpclose", "option http-pretend-keepalive" and
-             "1.1. The HTTP transaction model".
+             "option prefer-last-server", "option http-pretend-keepalive",
+             "option httpclose", and "1.1. The HTTP transaction model".
 
 
 option http-no-delay
@@ -4428,6 +4430,30 @@
   See also: "option httpchk"
 
 
+option prefer-last-server
+no option prefer-last-server
+  Allow multiple load balanced requests to remain on the same server
+  May be used in sections:    defaults | frontend | listen | backend
+                                 yes   |    no    |   yes  |   yes
+  Arguments : none
+
+  When the load balancing algorithm in use is not deterministic, and a previous
+  request was sent to a server to which haproxy still holds a connection, it is
+  sometimes desirable that subsequent requests on a same session go to the same
+  server as much as possible. Note that this is different from persistence, as
+  we only indicate a preference which haproxy tries to apply without any form
+  of warranty. The real use is for keep-alive connections sent to servers. When
+  this option is used, haproxy will try to reuse the same connection that is
+  attached to the server instead of rebalancing to another server, causing a
+  close of the connection. This can make sense for static file servers. It does
+  not make much sense to use this in combination with hashing algorithms.
+
+  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 http-keep-alive"
+
+
 option redispatch
 no option redispatch
   Enable or disable session redistribution in case of connection failure
diff --git a/include/types/proxy.h b/include/types/proxy.h
index de5c337..6c6cc0b 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -76,7 +76,8 @@
 /* bits for proxy->options */
 #define PR_O_REDISP     0x00000001      /* allow reconnection to dispatch in case of errors */
 #define PR_O_TRANSP     0x00000002      /* transparent mode : use original DEST as dispatch */
-/* unused: 0x04, 0x08, 0x10, 0x20 */
+/* unused: 0x04, 0x08, 0x10 */
+#define PR_O_PREF_LAST  0x00000020      /* prefer last server */
 #define PR_O_DISPATCH   0x00000040      /* use dispatch mode */
 #define PR_O_KEEPALIVE  0x00000080      /* follow keep-alive sessions */
 #define PR_O_FWDFOR     0x00000100      /* conditionally insert x-forwarded-for with client address */
diff --git a/src/backend.c b/src/backend.c
index 7eeed8d..c9f0718 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -532,8 +532,19 @@
 
 	srv = NULL;
 	s->target = NULL;
+	conn = objt_conn(s->req->cons->end);
 
-	if (s->be->lbprm.algo & BE_LB_KIND) {
+	if (conn && (s->be->options & PR_O_PREF_LAST) &&
+	    objt_server(conn->target) && __objt_server(conn->target)->proxy == s->be &&
+	    srv_is_usable(__objt_server(conn->target)->state, __objt_server(conn->target)->eweight)) {
+		/* This session was relying on a server in a previous request
+		 * and the proxy has "option prefer-current-server" set, so
+		 * let's try to reuse the same server.
+		 */
+		srv = __objt_server(conn->target);
+		s->target = &srv->obj_type;
+	}
+	else if (s->be->lbprm.algo & BE_LB_KIND) {
 		/* we must check if we have at least one server available */
 		if (!s->be->lbprm.tot_weight) {
 			err = SRV_STATUS_NOSRV;
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 8d08d01..864a9fb 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -134,6 +134,7 @@
 	{ "httpclose",    PR_O_HTTP_CLOSE, PR_CAP_FE | PR_CAP_BE, 0, PR_MODE_HTTP },
 	{ "http-keep-alive",   PR_O_KEEPALIVE,   PR_CAP_FE | PR_CAP_BE, 0, PR_MODE_HTTP },
 	{ "http-server-close", PR_O_SERVER_CLO,  PR_CAP_FE | PR_CAP_BE, 0, PR_MODE_HTTP },
+	{ "prefer-last-server", PR_O_PREF_LAST,  PR_CAP_BE, 0, PR_MODE_HTTP },
 	{ "logasap",      PR_O_LOGASAP,    PR_CAP_FE, 0, 0 },
 	{ "nolinger",     PR_O_TCP_NOLING, PR_CAP_FE | PR_CAP_BE, 0, 0 },
 	{ "persist",      PR_O_PERSIST,    PR_CAP_BE, 0, 0 },