diff --git a/doc/configuration.txt b/doc/configuration.txt index 51496b65b..181303538 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -7103,6 +7103,9 @@ default_backend used when no rule has matched. It generally is the dynamic backend which will catch all undetermined requests. + If a backend used as default is disabled, no traffic will be redirected to + it. + Example : use_backend dynamic if url_dyn @@ -14752,14 +14755,17 @@ use_backend [{if | unless} ] There may be as many "use_backend" rules as desired. All of these rules are evaluated in their declaration order, and the first one which matches will - assign the backend. + assign the backend. This is even the case if the backend is considered as + down. However, if a matching rule targets a disabled backend, it is ignored + instead and rules evaluation continue. In the first form, the backend will be used if the condition is met. In the second form, the backend will be used if the condition is not met. If no - condition is valid, the backend defined with "default_backend" will be used. - If no default backend is defined, either the servers in the same section are - used (in case of a "listen" section) or, in case of a frontend, no server is - used and a 503 service unavailable response is returned. + condition is valid, the backend defined with "default_backend" will be used + unless it is disabled. If no default backend is defined, either the servers + in the same section are used (in case of a "listen" section) or, in case of a + frontend, no server is used and a 503 service unavailable response is + returned. Note that it is possible to switch from a TCP frontend to an HTTP backend. In this case, either the frontend has already checked that the protocol is HTTP, diff --git a/include/haproxy/backend.h b/include/haproxy/backend.h index bdb457e33..00414be2f 100644 --- a/include/haproxy/backend.h +++ b/include/haproxy/backend.h @@ -85,6 +85,16 @@ static inline int be_usable_srv(struct proxy *be) return be->srv_bck; } +/* Returns true if backend can be used as target to a switching rules. */ +static inline int be_is_eligible(const struct proxy *be) +{ + /* A disabled backend cannot be selected for traffic. Note that STOPPED + * state is ignored as there is a risk of breaking requests during + * soft-stop. + */ + return !(be->flags & PR_FL_DISABLED); +} + /* set the time of last session on the backend */ static inline void be_set_sess_last(struct proxy *be) { diff --git a/reg-tests/stream/test_content_switching.vtc b/reg-tests/stream/test_content_switching.vtc index d580ed0cc..c850faedf 100644 --- a/reg-tests/stream/test_content_switching.vtc +++ b/reg-tests/stream/test_content_switching.vtc @@ -34,11 +34,13 @@ haproxy h1 -conf { frontend fe bind "fd@${fe1S}" use_backend %[req.hdr("x-target")] if { req.hdr("x-dyn") "1" } + use_backend be if { req.hdr("x-target") "be" } frontend fe_default bind "fd@${fe2S}" use_backend %[req.hdr("x-target")] if { req.hdr("x-dyn") "1" } + use_backend be_disabled if { req.hdr("x-target") "be_disabled" } use_backend be default_backend be_default @@ -50,6 +52,10 @@ haproxy h1 -conf { backend be http-request return status 200 hdr "x-be" %[be_name] + backend be_disabled + disabled + http-request return status 200 hdr "x-be" %[be_name] + backend be_default http-request return status 200 hdr "x-be" %[be_name] } -start @@ -80,6 +86,12 @@ client c2 -connect ${h1_fe2S_sock} { rxresp expect resp.status == 200 expect resp.http.x-be == "be_default" + + # Static rule on disabled backend -> continue to next rule + txreq -hdr "x-target: be_disabled" + rxresp + expect resp.status == 200 + expect resp.http.x-be == "be" } -run # Connect to listen proxy type diff --git a/src/stream.c b/src/stream.c index 8941b1e19..5bb0c247d 100644 --- a/src/stream.c +++ b/src/stream.c @@ -1159,6 +1159,12 @@ static int process_switching_rules(struct stream *s, struct channel *req, int an backend = rule->be.backend; } + /* If backend is ineligible, continue rules processing. */ + if (backend && !be_is_eligible(backend)) { + backend = NULL; + continue; + } + /* Break the loop at the first matching rule found. If * the dynamic name resolution has fail, fallback will * be performed on the default backend. @@ -1178,7 +1184,11 @@ static int process_switching_rules(struct stream *s, struct channel *req, int an return 0; } - backend = fe->defbe.be ? fe->defbe.be : s->be; + /* Use default backend if possible or stay on the current proxy. */ + if (fe->defbe.be && be_is_eligible(fe->defbe.be)) + backend = fe->defbe.be; + else + backend = s->be; } if (!stream_set_backend(s, backend))