errorloc303 X X X X
error-log-format X X X -
force-persist - - X X
+force-be-switch - X X -
filter - X X X
fullconn X - X X
guid - X X X
is possible to disable many instances at once by adding the "disabled"
keyword in a "defaults" section.
- See also : "enabled"
+ By default, a disabled backend cannot be selected for content-switching.
+ However, a portion of the traffic can ignore this when "force-be-switch" is
+ used.
+
+ See also : "enabled", "force-be-switch"
dispatch <address>:<port> (deprecated)
and section 7 about ACL usage.
+force-be-switch { if | unless } <condition>
+ Allow content switching to select a backend instance even if it is disabled
+ or unpublished. This rule can be used by admins to test traffic to services
+ prior to expose them to the outside world.
+
+ May be used in the following contexts: tcp, http
+
+ May be used in sections: defaults | frontend | listen | backend
+ no | yes | yes | no
+
+ See also : "disabled"
+
+
filter <name> [param*]
Add the filter <name> in the filter list attached to the proxy.
PERSIST_TYPE_NONE = 0, /* no persistence */
PERSIST_TYPE_FORCE, /* force-persist */
PERSIST_TYPE_IGNORE, /* ignore-persist */
+ PERSIST_TYPE_BE_SWITCH, /* force-be-switch */
};
/* final results for http-request rules */
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}"
+ force-be-switch if { req.hdr("x-force") "1" }
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
rxresp
expect resp.status == 200
expect resp.http.x-be == "be2"
+
+ # Static rule matching on unpublished backend with force-be-switch
+ txreq -hdr "x-force: 1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-be == "be"
+
+ # Dynamic rule matching on unpublished backend with force-be-switch
+ txreq -hdr "x-dyn: 1" -hdr "x-target: be" -hdr "x-force: 1"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.x-be == "be"
} -run
haproxy h1 -cli {
}
#endif
+static int proxy_parse_force_be_switch(char **args, int section_type, struct proxy *curpx,
+ const struct proxy *defpx, const char *file, int line,
+ char **err)
+{
+ struct acl_cond *cond = NULL;
+ struct persist_rule *rule;
+
+ if (curpx->cap & PR_CAP_DEF) {
+ memprintf(err, "'%s' not allowed in 'defaults' section.", args[0]);
+ goto err;
+ }
+
+ if (!(curpx->cap & PR_CAP_FE)) {
+ memprintf(err, "'%s' only available in frontend or listen section.", args[0]);
+ goto err;
+ }
+
+ if (strcmp(args[1], "if") != 0 && strcmp(args[1], "unless") != 0) {
+ memprintf(err, "'%s' requires either 'if' or 'unless' followed by a condition.", args[0]);
+ goto err;
+ }
+
+ if (!(cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args + 1, err))) {
+ memprintf(err, "'%s' : %s.", args[0], *err);
+ goto err;
+ }
+
+ if (warnif_cond_conflicts(cond, SMP_VAL_FE_REQ_CNT, err)) {
+ memprintf(err, "'%s' : %s.", args[0], *err);
+ goto err;
+ }
+
+ rule = calloc(1, sizeof(*rule));
+ if (!rule) {
+ memprintf(err, "'%s' : out of memory.", args[0]);
+ goto err;
+ }
+
+ rule->cond = cond;
+ rule->type = PERSIST_TYPE_BE_SWITCH;
+ LIST_INIT(&rule->list);
+ LIST_APPEND(&curpx->persist_rules, &rule->list);
+
+ return 0;
+
+ err:
+ free_acl_cond(cond);
+ return -1;
+}
+
static int proxy_parse_guid(char **args, int section_type, struct proxy *curpx,
const struct proxy *defpx, const char *file, int line,
char **err)
{ CFG_LISTEN, "clitcpka-intvl", proxy_parse_tcpka_intvl },
{ CFG_LISTEN, "srvtcpka-intvl", proxy_parse_tcpka_intvl },
#endif
+ { CFG_LISTEN, "force-be-switch", proxy_parse_force_be_switch },
{ CFG_LISTEN, "guid", proxy_parse_guid },
{ 0, NULL, NULL },
}};
return ACT_RET_STOP;
}
+/* Parses persist-rules attached to <fe> frontend and report the first macthing
+ * entry, using <sess> session and <s> stream as sample source.
+ *
+ * As this function is called several times in the same stream context,
+ * <persist> will act as a caching value to avoid reprocessing of a similar
+ * ruleset. It must be set to a negative value for the first invokation.
+ *
+ * Returns 1 if a rule matches, else 0.
+ */
+static int lookup_fe_persist_rules(struct proxy *fe, struct session *sess,
+ struct stream *s, int *persist)
+{
+ struct persist_rule *prst_rule;
+
+ if (*persist >= 0) {
+ /* Rules already processed, use previous computed result. */
+ return *persist;
+ }
+
+ list_for_each_entry(prst_rule, &fe->persist_rules, list) {
+ if (!acl_match_cond(prst_rule->cond, fe, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL))
+ continue;
+
+ /* force/ignore-persist match */
+ if (prst_rule->type == PERSIST_TYPE_BE_SWITCH) {
+ *persist = 1;
+ break;
+ }
+ }
+
+ if (*persist < 0)
+ *persist = 0;
+ return *persist;
+}
+
/* This stream analyser checks the switching rules and changes the backend
* if appropriate. The default_backend rule is also considered, then the
* target backend's forced persistence rules are also evaluated last if any.
struct session *sess = s->sess;
struct proxy *fe = sess->fe;
struct proxy *backend = NULL;
+ int fe_persist = -1;
req->analysers &= ~an_bit;
req->analyse_exp = TICK_ETERNITY;
}
/* If backend is ineligible, continue rules processing. */
- if (backend && !be_is_eligible(backend)) {
+ if (backend && !be_is_eligible(backend) &&
+ !lookup_fe_persist_rules(fe, sess, s, &fe_persist)) {
backend = NULL;
continue;
}
}
/* Use default backend if possible or stay on the current proxy. */
- if (fe->defbe.be && be_is_eligible(fe->defbe.be))
+ if (fe->defbe.be &&
+ (be_is_eligible(fe->defbe.be) ||
+ lookup_fe_persist_rules(fe, sess, s, &fe_persist))) {
backend = fe->defbe.be;
- else
+ }
+ else {
backend = s->be;
+ }
}
if (!stream_set_backend(s, backend))