mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2026-04-04 02:21:53 +02:00
MEDIUM: filters: add "filter-sequence" directive
This is another pre-requisite work for upcoming decompression filter. In this patch we implement the "filter-sequence" directive which can be used in proxy section (frontend,backend,listen) and takes 2 parameters The first one is the direction (request or response), the second one is a comma separated list of filter names previously declared on the proxy using the "filter" keyword. The main goal of this directive is to be able to instruct haproxy in which order the filters should be executed on request and response paths, especially if the ordering between request and response handling must differ, and without relying on the filter declaration ordering (within the proxy) which is used by default by haproxy. Another benefit of this feature is that it becomes possible to "ignore" a previously declared filter on the proxy. Indeed, when filter-sequence is defined for a given direction (request/response), then it will be used over the implicit filter ordering, but if a filter which was previously declared is not specified in the related filter-sequence, it will not be executed on purpose. This can be used as a way to temporarily disable a filter without completely removing its configuration. Documentation was updated (check examples for more info)
This commit is contained in:
parent
629a5ae531
commit
8d28c0e37b
@ -6001,6 +6001,7 @@ external-check path X - X X
|
||||
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
|
||||
@ -7891,8 +7892,41 @@ filter <name> [param*]
|
||||
|
||||
server srv1 192.168.0.1:80
|
||||
|
||||
See also : section 9.
|
||||
See also : section 9., "filter-sequence"
|
||||
|
||||
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
|
||||
|
||||
@ -215,6 +215,12 @@ struct flt_conf {
|
||||
unsigned int flags; /* FLT_CFG_FL_* */
|
||||
};
|
||||
|
||||
struct filter_sequence_elt {
|
||||
char *flt_name; /* filter name (set during parsing) */
|
||||
struct flt_conf *flt_conf; /* associated filter conf (set after parsing) */
|
||||
struct list list; /* list element */
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure reprensenting a filter instance attached to a stream
|
||||
*
|
||||
|
||||
@ -509,6 +509,12 @@ struct proxy {
|
||||
* name is used
|
||||
*/
|
||||
struct list filter_configs; /* list of the filters that are declared on this proxy */
|
||||
struct { /* sequence in which declared filters on the proxy should be execute
|
||||
* (list of filter_sequence_elt)
|
||||
*/
|
||||
struct list req; /* during request handling */
|
||||
struct list res; /* during response handling */
|
||||
} filter_sequence;
|
||||
|
||||
struct guid_node guid; /* GUID global tree node */
|
||||
struct mt_list watcher_list; /* list of elems which currently references this proxy instance (currently only used with backends) */
|
||||
|
||||
183
src/filters.c
183
src/filters.c
@ -297,6 +297,136 @@ parse_filter(char **args, int section_type, struct proxy *curpx,
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* 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"))
|
||||
list = &curpx->filter_sequence.req;
|
||||
else if (!strcmp(args[1], "response"))
|
||||
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 (!strcmp(elt->flt_name, fconf->name)) {
|
||||
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
|
||||
@ -401,6 +531,7 @@ void
|
||||
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)
|
||||
@ -408,6 +539,16 @@ flt_deinit(struct proxy *proxy)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -438,7 +579,7 @@ flt_deinit_all_per_thread()
|
||||
|
||||
/* Attaches a filter to a stream. Returns -1 if an error occurs, 0 otherwise. */
|
||||
static int
|
||||
flt_stream_add_filter(struct stream *s, struct flt_conf *fconf, unsigned int flags)
|
||||
flt_stream_add_filter(struct stream *s, struct proxy *px, struct flt_conf *fconf, unsigned int flags)
|
||||
{
|
||||
struct filter *f;
|
||||
|
||||
@ -461,18 +602,38 @@ flt_stream_add_filter(struct stream *s, struct flt_conf *fconf, unsigned int fla
|
||||
}
|
||||
|
||||
LIST_APPEND(&strm_flt(s)->filters, &f->list);
|
||||
LIST_INIT(&f->req_list);
|
||||
LIST_INIT(&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);
|
||||
/* 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);
|
||||
|
||||
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.
|
||||
@ -489,9 +650,10 @@ flt_stream_init(struct stream *s)
|
||||
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, fconf, 0) < 0)
|
||||
if (flt_stream_add_filter(s, strm_fe(s), fconf, 0) < 0)
|
||||
return -1;
|
||||
}
|
||||
flt_stream_organize_filters(s, strm_fe(s));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -605,10 +767,12 @@ flt_set_stream_backend(struct stream *s, struct proxy *be)
|
||||
goto end;
|
||||
|
||||
list_for_each_entry(fconf, &be->filter_configs, list) {
|
||||
if (flt_stream_add_filter(s, fconf, FLT_FL_IS_BACKEND_FILTER) < 0)
|
||||
if (flt_stream_add_filter(s, be, 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) {
|
||||
@ -1231,6 +1395,7 @@ handle_analyzer_result(struct stream *s, struct channel *chn,
|
||||
* 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 },
|
||||
}
|
||||
};
|
||||
|
||||
@ -1578,6 +1578,8 @@ void init_new_proxy(struct proxy *p)
|
||||
LIST_INIT(&p->conf.lf_checks);
|
||||
LIST_INIT(&p->filter_configs);
|
||||
LIST_INIT(&p->tcpcheck.preset_vars);
|
||||
LIST_INIT(&p->filter_sequence.req);
|
||||
LIST_INIT(&p->filter_sequence.res);
|
||||
|
||||
MT_LIST_INIT(&p->lbprm.lb_free_list);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user