force-persist - - X X
force-be-switch - X X -
filter - X X X
-filter-sequence - X X X
fullconn X - X X
guid - X X X
hash-balance-factor X - X X
implicitly enable decompression filter ("decomp-req" or "decomp-res" whether
the decompression option targets request, response) automatically. However to
prevent misconfiguration, it is recommended to explicitly declare filters
- when multiple filters are involved on the proxy. See also "filter-sequence".
+ when multiple filters are involved on the proxy, because filters are
+ evaluated in their declaration order.
May be used in the following contexts: http, tcp
associated decompression filter is automatically enabled. For requests, the
filter is called "decomp-req", and for responses, the filter is "decomp-res".
In order to prevent misconfigurations, particularly when combining multiple
- filters, it is highly recommended to explicitly declare the filters sequence
- via the "filter-sequence" directive in the proxy.
+ filters, it is highly recommended to explicitly declare the filters, because
+ they are evaluated in their declaration order.
Mode "auto", which is only available for HTTP, means haproxy will try to find
a compatible algorithms in algo-req or algo-res list to be able to decompress
a hostile agent could trivially amplify its traffic 1000x by
sending it compressed.
- See also "decompression algo-req", "decompression algo-res", and
- "filter-sequence".
+ See also "decompression algo-req", "decompression algo-res", and "filter".
default-server [name <name>] [from <origin>] [param*]
server srv1 192.168.0.1:80
- See also : section 9., "filter-sequence"
+ See also : section 9.
-filter-sequence { request | response } <filter_list>
-
- Specifies in which order filters declared on the proxy should be
- executed.
-
- May be used in the following contexts: tcp, http
-
- May be used in sections: defaults | frontend | listen | backend
- no | yes | yes | yes
-
- Comma-separated list of filter names (<filter_list>) to specify in which
- order filters declared on the proxy should be executed, for request
- or response path, respectively.
-
- When filter-sequence is not specified for a given path (ie: request vs
- response), the order in which filters are declared on the proxy is used.
-
- If filter-sequence omits some filters that were declared on the proxy,
- they will not be executed. This is an effective way of temporarily
- disabling a filter without removing it from the configuration.
-
- Example:
- global
- lua-load my-filter.lua # defines custom "lua.my-filter"
- frontend myfront
- filter comp-req
- filter comp-res
- filter lua.my-filter
-
- filter-sequence request lua.my-filter,comp-req
- filter-sequence response lua.my-filter,comp-res
-
- See also : "filter"
fullconn <conns>
Specify at what backend load the servers will reach their maxconn
+++ /dev/null
-varnishtest "Test filter-sequence feature"
-
-feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(3.4-dev0)'"
-feature ignore_unknown_macro
-feature cmd "$HAPROXY_PROGRAM -cc 'feature(LUA)'"
-
-barrier b1 cond 2 -cyclic
-
-server s1 {
- rxreq
- txresp
-} -repeat 2 -start
-
-syslog Slg1 -level info {
- recv
- expect ~ ".* haproxy ${h1_pid} .* FilterA.req"
- recv
- expect ~ ".* haproxy ${h1_pid} .* FilterB.req"
- recv
- expect ~ ".* haproxy ${h1_pid} .* FilterA.resp"
- recv
- expect ~ ".* haproxy ${h1_pid} .* FilterB.resp"
-
- barrier b1 sync
-
- recv
- expect ~ ".* haproxy ${h1_pid} .* FilterB.req"
- recv
- expect ~ ".* haproxy ${h1_pid} .* FilterA.req"
- recv
- expect ~ ".* haproxy ${h1_pid} .* FilterB.resp"
-} -start
-
-haproxy h1 -conf {
- global
- tune.lua.bool-sample-conversion normal
- lua-load ${testdir}/dummy_filters.lua
- log udp@${Slg1_addr}:${Slg1_port} format rfc5424 local0
-
- defaults
- timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
- timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
- timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
-
- listen fe1
- mode http
- filter lua.FilterA
- filter lua.FilterB
-
- bind "fd@${fe1}"
-
- # test normal sequence (inherited from filter ordering)
-
- server app1 ${s1_addr}:${s1_port}
-
- listen fe2
- mode http
- filter lua.FilterA
- filter lua.FilterB
-
- # test forced filter-sequence on request
- filter-sequence request lua.FilterB,lua.FilterA
- # ignore filterA on response
- filter-sequence response lua.FilterB
-
- bind "fd@${fe2}"
-
- server app1 ${s1_addr}:${s1_port}
-} -start
-
-client c0 -connect ${h1_fe1_sock} {
- txreq -url "/"
- rxresp
- expect resp.status == 200
-} -start -wait
-
-# Wait matching log messages
-barrier b1 sync
-
-client c1 -connect ${h1_fe2_sock} {
- txreq -url "/"
- rxresp
- expect resp.status == 200
-} -start -wait
-
-syslog Slg1 -wait
cur_arg = 1;
kw = flt_find_kw(args[cur_arg]);
if (kw) {
- /* default name is keyword name, unless overridden by parse func */
- fconf->name = kw->kw;
if (!kw->parse) {
memprintf(err, "parsing [%s:%d] : '%s' : "
"'%s' option is not implemented in this version (check build options).",
}
-/*
- * Parses the "filter-sequence" keyword
- */
-static int
-parse_filter_sequence(char **args, int section_type, struct proxy *curpx,
- const struct proxy *defpx, const char *file, int line, char **err)
-{
- /* filter-sequence cannot be defined on a default proxy */
- if (curpx == defpx) {
- memprintf(err, "parsing [%s:%d] : %s is not allowed in a 'default' section.",
- file, line, args[0]);
- return -1;
- }
- if (strcmp(args[0], "filter-sequence") == 0) {
- struct list *list;
- char *str;
- size_t cur_sep;
-
- if (!*args[1]) {
- memprintf(err,
- "parsing [%s:%d] : missing argument for '%s' in %s '%s'.",
- file, line, args[0], proxy_type_str(curpx), curpx->id);
- goto error;
- }
-
- if (strcmp(args[1], "request") == 0)
- list = &curpx->filter_sequence.req;
- else if (strcmp(args[1], "response") == 0)
- list = &curpx->filter_sequence.res;
- else {
- memprintf(err,
- "parsing [%s:%d] : expected either 'request' or 'response' for '%s' in %s '%s'.",
- file, line, args[0], proxy_type_str(curpx), curpx->id);
- goto error;
- }
-
- if (!*args[2]) {
- memprintf(err,
- "parsing [%s:%d] : missing filter list for '%s' in %s '%s'.",
- file, line, args[0], proxy_type_str(curpx), curpx->id);
- goto error;
- }
-
- str = args[2];
- while (str[0]) {
- struct filter_sequence_elt *elt;
-
- elt = calloc(1, sizeof(*elt));
- if (!elt) {
- memprintf(err, "'%s %s' : out of memory", args[0], args[1]);
- goto error;
- }
-
- cur_sep = strcspn(str, ",");
-
- elt->flt_name = my_strndup(str, cur_sep);
- if (!elt->flt_name) {
- ha_free(&elt);
- goto error;
- }
-
- LIST_APPEND(list, &elt->list);
-
- if (str[cur_sep])
- str += cur_sep + 1;
- else
- str += cur_sep;
- }
- }
-
- return 0;
-
- error:
- return -1;
-}
-
-static int compile_filter_sequence_elt(struct proxy *px, struct filter_sequence_elt *elt, char **errmsg)
-{
- struct flt_conf *fconf;
- int ret = ERR_NONE;
-
- list_for_each_entry(fconf, &px->filter_configs, list) {
- if (fconf->name && strcmp(elt->flt_name, fconf->name) == 0) {
- elt->flt_conf = fconf;
- break;
- }
- }
- if (!elt->flt_conf) {
- memprintf(errmsg, "invalid filter name: '%s' is not defined on the proxy", elt->flt_name);
- ret = ERR_FATAL;
- }
-
- return ret;
-}
-
-/* after config is checked, time to resolve filter-sequence (both request and response)
- * used on the proxy in order to associate filter names with valid flt_conf entries
- * this will help decrease filter lookup time during runtime (filter ids are compared
- * using their address, not string content)
- */
-static int postcheck_filter_sequence(struct proxy *px)
-{
- struct filter_sequence_elt *elt;
- char *errmsg = NULL;
- int ret = ERR_NONE;
-
- list_for_each_entry(elt, &px->filter_sequence.req, list) {
- ret = compile_filter_sequence_elt(px, elt, &errmsg);
- if (ret & ERR_CODE) {
- memprintf(&errmsg, "error while postparsing request filter-sequence '%s' : %s", elt->flt_name, errmsg);
- goto error;
- }
- }
- list_for_each_entry(elt, &px->filter_sequence.res, list) {
- ret = compile_filter_sequence_elt(px, elt, &errmsg);
- if (ret & ERR_CODE) {
- memprintf(&errmsg, "error while postparsing response filter-sequence '%s' : %s", elt->flt_name, errmsg);
- goto error;
- }
- }
-
- return ret;
-
- error:
- ha_alert("%s: %s\n", px->id, errmsg);
- ha_free(&errmsg);
- return ret;
-}
-REGISTER_POST_PROXY_CHECK(postcheck_filter_sequence);
-
/*
* Calls 'init' callback for all filters attached to a proxy. This happens after
* the configuration parsing. Filters can finish to fill their config. Returns
flt_deinit(struct proxy *proxy)
{
struct flt_conf *fconf, *back;
- struct filter_sequence_elt *fsequence, *fsequenceb;
list_for_each_entry_safe(fconf, back, &proxy->filter_configs, list) {
if (fconf->ops->deinit)
LIST_DELETE(&fconf->list);
free(fconf);
}
- list_for_each_entry_safe(fsequence, fsequenceb, &proxy->filter_sequence.req, list) {
- LIST_DEL_INIT(&fsequence->list);
- ha_free(&fsequence->flt_name);
- ha_free(&fsequence);
- }
- list_for_each_entry_safe(fsequence, fsequenceb, &proxy->filter_sequence.res, list) {
- LIST_DEL_INIT(&fsequence->list);
- ha_free(&fsequence->flt_name);
- ha_free(&fsequence);
- }
}
/*
/* Attaches a filter to a stream. Returns -1 if an error occurs, 0 otherwise. */
static int
-flt_stream_add_filter(struct stream *s, struct proxy *px, struct flt_conf *fconf, unsigned int flags)
+flt_stream_add_filter(struct stream *s, struct flt_conf *fconf, unsigned int flags)
{
struct filter *f;
}
LIST_APPEND(&strm_flt(s)->filters, &f->list);
- LIST_INIT(&f->req_list);
- LIST_INIT(&f->res_list);
- /* use filter config ordering unless filter-sequence says otherwise */
- if (LIST_ISEMPTY(&px->filter_sequence.req))
- LIST_APPEND(&s->req.flt.filters, &f->req_list);
- if (LIST_ISEMPTY(&px->filter_sequence.res))
- LIST_APPEND(&s->res.flt.filters, &f->res_list);
+ /* for now f->req_list == f->res_list to preserve
+ * historical behavior, but the ordering will change
+ * in the future
+ */
+ LIST_APPEND(&s->req.flt.filters, &f->req_list);
+ LIST_APPEND(&s->res.flt.filters, &f->res_list);
strm_flt(s)->flags |= STRM_FLT_FL_HAS_FILTERS;
return 0;
}
-static void flt_stream_organize_filters(struct stream *s, struct proxy *px)
-{
- struct filter_sequence_elt *fsequence;
- struct filter *filter;
-
- list_for_each_entry(fsequence, &px->filter_sequence.req, list) {
- list_for_each_entry(filter, &strm_flt(s)->filters, list) {
- if (filter->config == fsequence->flt_conf && !LIST_INLIST(&filter->req_list))
- LIST_APPEND(&s->req.flt.filters, &filter->req_list);
- }
- }
- list_for_each_entry(fsequence, &px->filter_sequence.res, list) {
- list_for_each_entry(filter, &strm_flt(s)->filters, list) {
- if (filter->config == fsequence->flt_conf && !LIST_INLIST(&filter->res_list))
- LIST_APPEND(&s->res.flt.filters, &filter->res_list);
- }
- }
-}
-
/*
* Called when a stream is created. It attaches all frontend filters to the
* stream. Returns -1 if an error occurs, 0 otherwise.
memset(&s->res.flt, 0, sizeof(s->res.flt));
LIST_INIT(&s->res.flt.filters);
list_for_each_entry(fconf, &strm_fe(s)->filter_configs, list) {
- if (flt_stream_add_filter(s, strm_fe(s), fconf, 0) < 0)
+ if (flt_stream_add_filter(s, fconf, 0) < 0)
return -1;
}
- flt_stream_organize_filters(s, strm_fe(s));
return 0;
}
goto end;
list_for_each_entry(fconf, &be->filter_configs, list) {
- if (flt_stream_add_filter(s, be, fconf, FLT_FL_IS_BACKEND_FILTER) < 0)
+ if (flt_stream_add_filter(s, fconf, FLT_FL_IS_BACKEND_FILTER) < 0)
return -1;
}
- flt_stream_organize_filters(s, be);
-
end:
list_for_each_entry(filter, &strm_flt(s)->filters, list) {
if (FLT_OPS(filter)->stream_set_backend) {
* not enabled. */
static struct cfg_kw_list cfg_kws = {ILH, {
{ CFG_LISTEN, "filter", parse_filter },
- { CFG_LISTEN, "filter-sequence", parse_filter_sequence },
{ 0, NULL, NULL },
}
};