From: Christopher Faulet Date: Wed, 30 Sep 2026 16:10:22 +0000 (+0200) Subject: MINOR: filters: Add instance parsers for the built-in classes X-Git-Tag: v3.5-dev9~90 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=52124978c9cf12aa14193accd9949295e855ad1f;p=haproxy.git MINOR: filters: Add instance parsers for the built-in classes Connect built-in classes to parsers that produce a instance's flt_conf without attaching it to the legacy evaluation list. Provide a bridge to legacy keyword parsers, translating the stored argument vector to their empty-string-terminated convention and rejecting leftover arguments. Use it for trace, bandwidth limitation, SPOE and Lua; restore curproxy around SPOE parsing and resolve Lua filters by instance id. Use dedicated parsers for cache, FCGI, compression and decompression so their legacy implicit/explicit reconciliation does not modify filter_configs. Cache and FCGI application names are supplied by the instance identifier. The filter-config directive and instance finalization follow separately. --- diff --git a/include/haproxy/filters.h b/include/haproxy/filters.h index 69808e271..707561b9f 100644 --- a/include/haproxy/filters.h +++ b/include/haproxy/filters.h @@ -67,6 +67,27 @@ extern const char *fcgi_filter_cls_name; extern struct filter_class flt_fcgi_cls; #endif +/* Parsing functions of the internal filter classes, called to finalize a + * filter instance (see "filter-config" directive) + */ +int trace_flt_parse_instance(char **args, struct proxy *px, struct filter_instance *inst, char **err); +int cache_store_flt_parse_instance(char **args, struct proxy *px, struct filter_instance *inst, char **err); +int http_comp_req_flt_parse_instance(char **args, struct proxy *px, struct filter_instance *inst, char **err); +int http_comp_res_flt_parse_instance(char **args, struct proxy *px, struct filter_instance *inst, char **err); +int decomp_req_flt_parse_instance(char **args, struct proxy *px, struct filter_instance *inst, char **err); +int decomp_res_flt_parse_instance(char **args, struct proxy *px, struct filter_instance *inst, char **err); +#if defined(USE_LUA) +int hlua_flt_parse_instance(char **args, struct proxy *px, struct filter_instance *inst, char **err); +#endif +#if defined(USE_SPOE) +int spoe_flt_parse_instance(char **args, struct proxy *px, struct filter_instance *inst, char **err); +#endif +int bwlim_in_flt_parse_instance(char **args, struct proxy *px, struct filter_instance *inst, char **err); +int bwlim_out_flt_parse_instance(char **args, struct proxy *px, struct filter_instance *inst, char **err); +#if defined(USE_FCGI) +int fcgi_flt_parse_instance(char **args, struct proxy *px, struct filter_instance *inst, char **err); +#endif + #define FLT_ID(flt) (flt)->config->id #define FLT_CONF(flt) (flt)->config->conf #define FLT_OPS(flt) (flt)->config->ops @@ -273,6 +294,18 @@ flt_update_offsets(struct filter *filter, struct channel *chn, int len) struct filter_class *filter_find_class(const char *name); +/* Helper function for filter classes: calls the legacy "filter" keyword + * parsing function on the arguments of the filter instance , + * as if the line "filter " was found in the configuration + * of the proxy . is passed as-is to the parsing function. + * Returns 0 on success, < 0 on error. + */ +int flt_parse_instance_legacy(struct proxy *px, struct filter_instance *inst, char **err, + const char *kw, + int (*parse)(char **args, int *cur_arg, struct proxy *px, + struct flt_conf *fconf, char **err, void *private), + void *private); + int filter_register_class(struct filter_class *cls, const char *name, int (*parse)(char **args, struct proxy *px, struct filter_instance *inst, char **err)); diff --git a/src/cache.c b/src/cache.c index ed8729dcd..aeb17735f 100644 --- a/src/cache.c +++ b/src/cache.c @@ -3606,6 +3606,44 @@ parse_cache_flt(char **args, int *cur_arg, struct proxy *px, return -1; } +/* Parse the arguments of a "cache-store" filter instance. The cache name is the + * instance id (the id is mandatory for this class, several instances are + * allowed), no argument is expected. + * Returns 0 on success, < 0 on error. + */ +int cache_store_flt_parse_instance(char **args, struct proxy *px, struct filter_instance *inst, char **err) +{ + struct cache_flt_conf *cconf; + + if (!inst->id) { + memprintf(err, "'filter-config %s' : missing filter instance id", + cache_store_filter_cls_name); + return -1; + } + if (*args) { + memprintf(err, "'filter-config %s' : unexpected argument '%s'", + cache_store_filter_cls_name, args[0]); + return -1; + } + + cconf = calloc(1, sizeof(*cconf)); + if (!cconf) { + memprintf(err, "out of memory"); + return -1; + } + cconf->c.name = strdup(inst->id); + if (!cconf->c.name) { + free(cconf); + memprintf(err, "out of memory"); + return -1; + } + + inst->fconf->id = cache_store_flt_id; + inst->fconf->conf = cconf; + inst->fconf->ops = &cache_ops; + return 0; +} + /* It reserves a struct show_cache_ctx for the local variables */ static int cli_parse_show_cache(char **args, char *payload, struct appctx *appctx, void *private) { diff --git a/src/fcgi-app.c b/src/fcgi-app.c index caecc129e..29f355a87 100644 --- a/src/fcgi-app.c +++ b/src/fcgi-app.c @@ -561,6 +561,45 @@ parse_fcgi_flt(char **args, int *cur_arg, struct proxy *px, return -1; } +/* Parse the arguments of a "fcgi" filter instance. The fcgi-app name is the + * instance id, it must be set, and no argument is expected. + * Returns 0 on success, < 0 on error. + */ +int fcgi_flt_parse_instance(char **args, struct proxy *px, struct filter_instance *inst, char **err) +{ + struct fcgi_flt_conf *fcgi_conf; + + if (!inst->id) { + memprintf(err, "'filter-config %s' : missing filter instance id, the fcgi-app name is expected", + fcgi_filter_cls_name); + return -1; + } + if (*args) { + memprintf(err, "'filter-config %s' : unexpected argument '%s'", + fcgi_filter_cls_name, args[0]); + return -1; + } + + fcgi_conf = calloc(1, sizeof(*fcgi_conf)); + if (!fcgi_conf) { + memprintf(err, "out of memory"); + return -1; + } + fcgi_conf->name = strdup(inst->id); + if (!fcgi_conf->name) { + free(fcgi_conf); + memprintf(err, "out of memory"); + return -1; + } + LIST_INIT(&fcgi_conf->param_rules); + LIST_INIT(&fcgi_conf->hdr_rules); + + inst->fconf->id = fcgi_flt_id; + inst->fconf->conf = fcgi_conf; + inst->fconf->ops = &fcgi_flt_ops; + return 0; +} + /* Parses the "use-fcgi-app" proxy keyword */ static int proxy_parse_use_fcgi_app(char **args, int section, struct proxy *curpx, const struct proxy *defpx, const char *file, int line, diff --git a/src/filters.c b/src/filters.c index a56c5c0d6..1c4da4825 100644 --- a/src/filters.c +++ b/src/filters.c @@ -1759,6 +1759,42 @@ int flt_init_class_refs(struct proxy *px) return -1; } +/* Helper function for filter classes: calls the legacy "filter" keyword + * parsing function on the arguments of the filter instance , + * as if the line "filter " was found in the configuration + * of the proxy . is passed as-is to the parsing function. + * Returns 0 on success, < 0 on error. + */ +int flt_parse_instance_legacy(struct proxy *px, struct filter_instance *inst, char **err, + const char *kw, + int (*parse)(char **args, int *cur_arg, struct proxy *px, + struct flt_conf *fconf, char **err, void *private), + void *private) +{ + char *fargs[MAX_LINE_ARGS+1]; + int i, ret, cur_arg; + + /* the legacy parsing functions expect the keyword at args[*cur_arg], + * followed by the filter arguments. Like for the config parser, the + * arguments must be terminated by an empty string, not a NULL pointer. + */ + + fargs[0] = (char *)kw; + for (i = 0; inst->conf.argv[i] && *inst->conf.argv[i]; i++) + fargs[i+1] = inst->conf.argv[i]; + fargs[i+1] = ""; + + cur_arg = 0; + ret = parse(fargs, &cur_arg, px, inst->fconf, err, private); + if (ret == 0 && *fargs[cur_arg]) { + /* some arguments were not consumed by the parsing function */ + memprintf(err, "'filter-config %s' : unknown keyword '%s'", + inst->class->name, fargs[cur_arg]); + ret = -1; + } + return ret; +} + /* Registers a new filter class, with the parsing function used to * finalize the filter instances of this class (may be NULL if the class does * not support the "filter-config" directive). The class is initialized and @@ -1900,22 +1936,22 @@ int filter_register_class_full(struct filter_class *cls, const char *name, static void filter_init_classes(void) { /* TODO: the filter names must come from the filters (the filter id most probably) */ - flt_init_class(&flt_trace_cls, trace_filter_cls_name, FLT_CLS_FL_MULTI, NULL); - flt_init_class(&flt_cache_store_cls, cache_store_filter_cls_name, FLT_CLS_FL_MULTI, NULL); - flt_init_class(&flt_http_comp_req_cls, http_comp_req_filter_cls_name, 0, NULL); - flt_init_class(&flt_http_comp_res_cls, http_comp_res_filter_cls_name, 0, NULL); - flt_init_class(&flt_decomp_req_cls, decomp_req_filter_cls_name, 0, NULL); - flt_init_class(&flt_decomp_res_cls, decomp_res_filter_cls_name, 0, NULL); + flt_init_class(&flt_trace_cls, trace_filter_cls_name, FLT_CLS_FL_MULTI, trace_flt_parse_instance); + flt_init_class(&flt_cache_store_cls, cache_store_filter_cls_name, FLT_CLS_FL_MULTI, cache_store_flt_parse_instance); + flt_init_class(&flt_http_comp_req_cls, http_comp_req_filter_cls_name, 0, http_comp_req_flt_parse_instance); + flt_init_class(&flt_http_comp_res_cls, http_comp_res_filter_cls_name, 0, http_comp_res_flt_parse_instance); + flt_init_class(&flt_decomp_req_cls, decomp_req_filter_cls_name, 0, decomp_req_flt_parse_instance); + flt_init_class(&flt_decomp_res_cls, decomp_res_filter_cls_name, 0, decomp_res_flt_parse_instance); #if defined(USE_SPOE) - flt_init_class(&flt_spoe_cls, spoe_filter_cls_name, FLT_CLS_FL_MULTI, NULL); + flt_init_class(&flt_spoe_cls, spoe_filter_cls_name, FLT_CLS_FL_MULTI, spoe_flt_parse_instance); #endif #if defined(USE_LUA) - flt_init_class(&flt_lua_cls, hlua_filter_cls_name, FLT_CLS_FL_MULTI, NULL); + flt_init_class(&flt_lua_cls, hlua_filter_cls_name, FLT_CLS_FL_MULTI, hlua_flt_parse_instance); #endif - flt_init_class(&flt_bwlim_in_cls, bwlim_in_filter_cls_name, FLT_CLS_FL_MULTI, NULL); - flt_init_class(&flt_bwlim_out_cls, bwlim_out_filter_cls_name, FLT_CLS_FL_MULTI, NULL); + flt_init_class(&flt_bwlim_in_cls, bwlim_in_filter_cls_name, FLT_CLS_FL_MULTI, bwlim_in_flt_parse_instance); + flt_init_class(&flt_bwlim_out_cls, bwlim_out_filter_cls_name, FLT_CLS_FL_MULTI, bwlim_out_flt_parse_instance); #if defined(USE_FCGI) - flt_init_class(&flt_fcgi_cls, fcgi_filter_cls_name, 0, NULL); + flt_init_class(&flt_fcgi_cls, fcgi_filter_cls_name, 0, fcgi_flt_parse_instance); #endif LIST_APPEND(&filter_classes, &flt_trace_cls.list); diff --git a/src/flt_bwlim.c b/src/flt_bwlim.c index 6c61c43df..d52b11f3f 100644 --- a/src/flt_bwlim.c +++ b/src/flt_bwlim.c @@ -967,6 +967,56 @@ static int parse_bwlim_out_flt(char **args, int *cur_arg, struct proxy *px, stru return parse_bwlim_flt(args, cur_arg, px, fconf, err, private); } +static int bwlim_flt_parse_instance(const char *kw, char **args, struct proxy *px, struct filter_instance *inst, char **err) +{ + char *fargs[MAX_LINE_ARGS+1]; + int i, ret, cur_arg; + + fargs[0] = (char *)kw; + fargs[1] = (char *)inst->id; + for (i = 0; args[i] && *args[i]; i++) + fargs[i+2] = args[i]; + fargs[i+2] = ""; + + cur_arg = 0; + ret = parse_bwlim_flt(fargs, &cur_arg, px, inst->fconf, err, NULL); + if (ret == 0 && *fargs[cur_arg]) { + memprintf(err, "'filter-config %s' : unknown keyword '%s'", inst->class->name, fargs[cur_arg]); + ret = -1; + } + return ret; +} + +/* Parse the arguments of a "bwlim-in" filter instance. the bandwith limitation + * name is the instance id. It relies on bwlim_flt_parse_instance(). + * Returns 0 on success, < 0 on error. + */ +int bwlim_in_flt_parse_instance(char **args, struct proxy *px, struct filter_instance *inst, char **err) +{ + if (flt_find_instance(px, &flt_bwlim_out_cls, inst->id)) { + memprintf(err, "bwlim filter '%s' already declared for proxy '%s'\n", inst->id, px->id); + return -1; + } + + inst->fconf->id = bwlim_in_flt_id; + return bwlim_flt_parse_instance("bwlim-in", args, px, inst, err); +} + +/* Parse the arguments of a "bwlim-out" filter instance. the bandwith limitation + * name is the instance id. It relies on bwlim_flt_parse_instance(). + * Returns 0 on success, < 0 on error. + */ +int bwlim_out_flt_parse_instance(char **args, struct proxy *px, struct filter_instance *inst, char **err) +{ + if (flt_find_instance(px, &flt_bwlim_in_cls, inst->id)) { + memprintf(err, "bwlim filter '%s' already declared for proxy '%s'\n", inst->id, px->id); + return -1; + } + + inst->fconf->id = bwlim_out_flt_id; + return bwlim_flt_parse_instance("bwlim-out", args, px, inst, err); +} + /* Declare the filter parser for "trace" keyword */ static struct flt_kw_list flt_kws = { "BWLIM", { }, { { "bwlim-in", parse_bwlim_in_flt, NULL }, diff --git a/src/flt_decomp.c b/src/flt_decomp.c index 72ee1c836..0f0af55e2 100644 --- a/src/flt_decomp.c +++ b/src/flt_decomp.c @@ -1254,6 +1254,70 @@ parse_decomp_res_flt(char **args, int *cur_arg, struct proxy *px, return 0; } +/* Parse the arguments of a "decomp-req" filter instance. No argument is + * expected. + * Returns 0 on success, < 0 on error. + */ +int decomp_req_flt_parse_instance(char **args, struct proxy *px, struct filter_instance *inst, char **err) +{ + struct decomp *decomp; + + if (!(px->cap & PR_CAP_FE)) { + memprintf(err, "'decomp-req' filter not allowed because %s '%s' has no frontend capability\n", + proxy_type_str(px), px->id); + return -1; + } + if (*args) { + memprintf(err, "'filter-config %s' : unexpected argument '%s'", + decomp_req_filter_cls_name, args[0]); + return -1; + } + + decomp = proxy_get_decomp(px, 0); + if (decomp == NULL) { + memprintf(err, "out of memory"); + return -1; + } + decomp->flags |= DECOMP_FL_DIR_REQ; + + inst->fconf->id = decomp_req_flt_id; + inst->fconf->conf = &decomp->req; + inst->fconf->ops = &decomp_req_ops; + return 0; +} + +/* Parse the arguments of a "decomp-res" filter instance. No argument is + * expected. + * Returns 0 on success, < 0 on error. + */ +int decomp_res_flt_parse_instance(char **args, struct proxy *px, struct filter_instance *inst, char **err) +{ + struct decomp *decomp; + + if (!(px->cap & PR_CAP_BE)) { + memprintf(err, "'decomp-res' filter not allowed because %s '%s' has no backend capability\n", + proxy_type_str(px), px->id); + return -1; + } + if (*args) { + memprintf(err, "'filter-config %s' : unexpected argument '%s'", + decomp_res_filter_cls_name, args[0]); + return -1; + } + + decomp = proxy_get_decomp(px, 0); + if (decomp == NULL) { + memprintf(err, "out of memory"); + return -1; + } + decomp->flags |= DECOMP_FL_DIR_RES; + + inst->fconf->id = decomp_res_flt_id; + inst->fconf->conf = &decomp->res; + inst->fconf->ops = &decomp_res_ops; + return 0; +} + int check_implicit_decomp_flt(struct proxy *proxy) { diff --git a/src/flt_http_comp.c b/src/flt_http_comp.c index e28193222..299f7ba59 100644 --- a/src/flt_http_comp.c +++ b/src/flt_http_comp.c @@ -1143,6 +1143,65 @@ parse_http_comp_res_flt(char **args, int *cur_arg, struct proxy *px, return 0; } +/* Parse the arguments of a "http-comp-req" filter instance. No argument is + * expected. + * Returns 0 on success, < 0 on error. + */ +int http_comp_req_flt_parse_instance(char **args, struct proxy *px, struct filter_instance *inst, char **err) +{ + struct comp *comp; + + if (*args) { + memprintf(err, "'filter-config %s' : unexpected argument '%s'", + http_comp_req_filter_cls_name, args[0]); + return -1; + } + + comp = proxy_get_comp(px, 0); + if (comp == NULL) { + memprintf(err, "out of memory"); + return -1; + } + /* Implicit instances mirror options; they must not restore a direction + * removed by a later option or a local defaults override. + */ + if (!(inst->flags & FLT_INST_F_IMPLICIT)) + comp->flags |= COMP_FL_DIR_REQ; + + inst->fconf->id = http_comp_req_flt_id; + inst->fconf->conf = px->comp; + inst->fconf->ops = &comp_req_ops; + return 0; +} + +/* Parse the arguments of a "http-comp-res" filter instance. No argument is + * expected. + * Returns 0 on success, < 0 on error. + */ +int http_comp_res_flt_parse_instance(char **args, struct proxy *px, struct filter_instance *inst, char **err) +{ + struct comp *comp; + + if (*args) { + memprintf(err, "'filter-config %s' : unexpected argument '%s'", + http_comp_res_filter_cls_name, args[0]); + return -1; + } + + comp = proxy_get_comp(px, 0); + if (comp == NULL) { + memprintf(err, "out of memory"); + return -1; + } + if (!(inst->flags & FLT_INST_F_IMPLICIT)) + comp->flags |= COMP_FL_DIR_RES; + + inst->fconf->id = http_comp_res_flt_id; + inst->fconf->conf = px->comp; + inst->fconf->ops = &comp_res_ops; + return 0; +} + int check_implicit_http_comp_flt(struct proxy *proxy) { diff --git a/src/flt_spoe.c b/src/flt_spoe.c index aa5287d50..9c475de79 100644 --- a/src/flt_spoe.c +++ b/src/flt_spoe.c @@ -2678,6 +2678,21 @@ static int parse_spoe_flt(char **args, int *cur_arg, struct proxy *px, return -1; } +/* Parse the arguments of a "spoe" filter instance, relying on the legacy + * "filter" keyword parser. + * Returns 0 on success, < 0 on error. + */ +int spoe_flt_parse_instance(char **args, struct proxy *px, struct filter_instance *inst, char **err) +{ + struct proxy *old_curproxy = curproxy; + int ret; + + curproxy = px; + ret = flt_parse_instance_legacy(px, inst, err, "spoe", parse_spoe_flt, NULL); + curproxy = old_curproxy; + return ret; +} + /* Send message of a SPOE group. This is the action_ptr callback of a rule * associated to a "send-spoe-group" action. * diff --git a/src/flt_trace.c b/src/flt_trace.c index 3284f37d1..a29cc786b 100644 --- a/src/flt_trace.c +++ b/src/flt_trace.c @@ -691,6 +691,31 @@ parse_trace_flt(char **args, int *cur_arg, struct proxy *px, return -1; } +/* Parse the arguments of a "trace" filter instance (see "filter-config" + * directive), relying on the legacy "filter" keyword parser. + * Returns 0 on success, < 0 on error. + */ +int trace_flt_parse_instance(char **args, struct proxy *px, struct filter_instance *inst, char **err) +{ + char *fargs[MAX_LINE_ARGS+1]; + int i, ret, cur_arg; + + fargs[0] = "trace"; + fargs[1] = "name"; + fargs[2] = (char *)inst->id; + for (i = 0; args[i] && *args[i]; i++) + fargs[i+3] = args[i]; + fargs[i+3] = ""; + + cur_arg = 0; + ret = parse_trace_flt(fargs, &cur_arg, px, inst->fconf, err, NULL); + if (ret == 0 && *fargs[cur_arg]) { + memprintf(err, "'filter-config %s' : unknown keyword '%s'", inst->class->name, fargs[cur_arg]); + ret = -1; + } + return ret; +} + /* Declare the filter parser for "trace" keyword */ static struct flt_kw_list flt_kws = { "TRACE", { }, { { "trace", parse_trace_flt, NULL }, diff --git a/src/hlua.c b/src/hlua.c index 3c54791b2..66f794c82 100644 --- a/src/hlua.c +++ b/src/hlua.c @@ -12588,6 +12588,42 @@ static int hlua_filter_parse_fct(char **args, int *cur_arg, struct proxy *px, return -1; } +/* Parse the arguments of a "lua" filter instance. The lua filter name is the + * instance id, as declared with core.register_filter() in a lua script (the id + * is mandatory for this class, several instances are allowed). The instance + * arguments are passed to the lua filter, relying on the legacy "filter" + * keyword parser. + * Returns 0 on success, < 0 on error. + */ +int hlua_flt_parse_instance(char **args, struct proxy *px, struct filter_instance *inst, char **err) +{ + struct flt_kw *kw; + char *kw_name = NULL; + int ret; + + if (!inst->id) { + memprintf(err, "'%s' : missing filter instance id", hlua_filter_cls_name); + return -1; + } + + kw_name = memprintf(&kw_name, "lua.%s", inst->id); + if (!kw_name) { + memprintf(err, "out of memory"); + return -1; + } + kw = flt_find_kw(kw_name); + if (!kw || !kw->parse) { + memprintf(err, "unknown lua filter '%s'", inst->id); + free(kw_name); + return -1; + } + + ret = flt_parse_instance_legacy(px, inst, err, kw_name, kw->parse, kw->private); + + free(kw_name); + return ret; +} + __LJMP static int hlua_register_data_filter(lua_State *L) { struct filter *filter;