From: Willy Tarreau Date: Mon, 23 Dec 2013 14:11:25 +0000 (+0100) Subject: MINOR: http: try to stick to same server after status 401/407 X-Git-Tag: v1.5-dev22~90 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=068621e4adcf5c1010b233a442f3cd9f28dc0b6e;p=thirdparty%2Fhaproxy.git MINOR: http: try to stick to same server after status 401/407 In HTTP keep-alive mode, if we receive a 401, we still have a chance of being able to send the visitor again to the same server over the same connection. This is required by some broken protocols such as NTLM, and anyway whenever there is an opportunity for sending the challenge to the proper place, it's better to do it (at least it helps with debugging). --- diff --git a/doc/configuration.txt b/doc/configuration.txt index 54005155f9..98310214a7 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -4446,7 +4446,13 @@ no option prefer-last-server 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. + not make much sense to use this in combination with hashing algorithms. Note, + haproxy already automatically tries to stick to a server which sends a 401 or + to a proxy which sends a 407 (authentication required). This is mandatory for + use with the broken NTLM authentication challenge, and significantly helps in + troubleshooting some faulty applications. Option prefer-last-server might be + desirable in these environments as well, to avoid redistributing the traffic + after every other response. 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. diff --git a/include/types/proto_http.h b/include/types/proto_http.h index 9d65d6f3ad..957ac02348 100644 --- a/include/types/proto_http.h +++ b/include/types/proto_http.h @@ -83,7 +83,7 @@ #define TX_CON_CLO_SET 0x00400000 /* "connection: close" is now set */ #define TX_CON_KAL_SET 0x00800000 /* "connection: keep-alive" is now set */ -/* Unused: 0x1000000 */ +#define TX_PREFER_LAST 0x01000000 /* try to stay on same server if possible (eg: after 401) */ #define TX_HDR_CONN_UPG 0x02000000 /* The "Upgrade" token was found in the "Connection" header */ #define TX_WAIT_NEXT_RQ 0x04000000 /* waiting for the second request to start, use keep-alive timeout */ diff --git a/src/backend.c b/src/backend.c index a80b6b4418..8fcce7dd2e 100644 --- a/src/backend.c +++ b/src/backend.c @@ -534,7 +534,8 @@ int assign_server(struct session *s) s->target = NULL; conn = objt_conn(s->req->cons->end); - if (conn && (s->be->options & PR_O_PREF_LAST) && + if (conn && + ((s->be->options & PR_O_PREF_LAST) || (s->txn.flags & TX_PREFER_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 diff --git a/src/proto_http.c b/src/proto_http.c index 2c6e27273c..624a51fa38 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -4281,6 +4281,8 @@ int http_send_name_header(struct http_txn *txn, struct proxy* be, const char* sr */ void http_end_txn_clean_session(struct session *s) { + int prev_status = s->txn.status; + /* FIXME: We need a more portable way of releasing a backend's and a * server's connections. We need a safer way to reinitialize buffer * flags. We also need a more accurate method for computing per-request @@ -4395,6 +4397,18 @@ void http_end_txn_clean_session(struct session *s) s->txn.meth = 0; http_reset_txn(s); s->txn.flags |= TX_NOT_FIRST | TX_WAIT_NEXT_RQ; + + if (prev_status == 401 || prev_status == 407) { + /* In HTTP keep-alive mode, if we receive a 401, we still have + * a chance of being able to send the visitor again to the same + * server over the same connection. This is required by some + * broken protocols such as NTLM, and anyway whenever there is + * an opportunity for sending the challenge to the proper place, + * it's better to do it (at least it helps with debugging). + */ + s->txn.flags |= TX_PREFER_LAST; + } + if (s->fe->options2 & PR_O2_INDEPSTR) s->req->cons->flags |= SI_FL_INDEP_STR; diff --git a/src/session.c b/src/session.c index fa5d6452c4..f06d06bbf2 100644 --- a/src/session.c +++ b/src/session.c @@ -536,6 +536,7 @@ int session_complete(struct session *s) txn->rsp.cap = NULL; txn->hdr_idx.v = NULL; txn->hdr_idx.size = txn->hdr_idx.used = 0; + txn->flags = 0; txn->req.flags = 0; txn->rsp.flags = 0; /* the HTTP messages need to know what buffer they're associated with */