1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #define APR_WANT_STRFUNC
20 #include "apr_strings.h"
23 #include "http_config.h"
24 #include "http_request.h"
26 #include "util_filter.h"
29 module AP_MODULE_DECLARE_DATA filter_module;
32 * @brief is a filter provider, as defined and implemented by mod_filter.
34 * The struct is a linked list, with dispatch criteria
35 * defined for each filter. The provider implementation itself is a
36 * (2.0-compatible) ap_filter_rec_t* frec.
38 struct ap_filter_provider_t {
42 /** The filter that implements this provider */
43 ap_filter_rec_t *frec;
45 /** The next provider in the list */
46 ap_filter_provider_t *next;
49 /** we need provider_ctx to save ctx values set by providers in filter_init */
50 typedef struct provider_ctx provider_ctx;
52 ap_filter_provider_t *provider;
57 ap_out_filter_func func;
59 provider_ctx *init_ctx;
62 typedef struct mod_filter_chain {
64 struct mod_filter_chain *next;
68 apr_hash_t *live_filters;
69 mod_filter_chain *chain;
77 static void filter_trace(conn_rec *c, int debug, const char *fname,
78 apr_bucket_brigade *bb)
83 case 0: /* normal, operational use */
85 case 1: /* mod_diagnostics level */
86 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01375) "%s", fname);
87 for (b = APR_BRIGADE_FIRST(bb);
88 b != APR_BRIGADE_SENTINEL(bb);
89 b = APR_BUCKET_NEXT(b)) {
91 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01376)
92 "%s: type: %s, length: %" APR_SIZE_T_FMT,
93 fname, b->type->name ? b->type->name : "(unknown)",
100 static int filter_init(ap_filter_t *f)
102 ap_filter_provider_t *p;
105 ap_filter_rec_t *filter = f->frec;
107 harness_ctx *fctx = apr_pcalloc(f->r->pool, sizeof(harness_ctx));
108 for (p = filter->providers; p; p = p->next) {
109 if (p->frec->filter_init_func == filter_init) {
110 ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c, APLOGNO(01377)
111 "Chaining of FilterProviders not supported");
112 return HTTP_INTERNAL_SERVER_ERROR;
114 else if (p->frec->filter_init_func) {
116 if ((err = p->frec->filter_init_func(f)) != OK) {
117 ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c, APLOGNO(01378)
118 "filter_init for %s failed", p->frec->name);
119 return err; /* if anyone errors out here, so do we */
121 if (f->ctx != NULL) {
122 /* the filter init function set a ctx - we need to record it */
123 pctx = apr_pcalloc(f->r->pool, sizeof(provider_ctx));
126 pctx->next = fctx->init_ctx;
127 fctx->init_ctx = pctx;
135 static int filter_lookup(ap_filter_t *f, ap_filter_rec_t *filter)
137 ap_filter_provider_t *provider;
139 const char *err = NULL;
140 request_rec *r = f->r;
141 harness_ctx *ctx = f->ctx;
144 unsigned int proto_flags;
145 mod_filter_ctx *rctx = ap_get_module_config(r->request_config,
149 /* Check registered providers in order */
150 for (provider = filter->providers; provider; provider = provider->next) {
151 if (provider->expr) {
152 match = ap_expr_exec(r, provider->expr, &err);
154 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01379)
155 "Error evaluating filter dispatch condition: %s",
159 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
160 "Expression condition for '%s' %s",
161 provider->frec->name,
162 match ? "matched" : "did not match");
164 else if (r->content_type) {
165 const char **type = provider->types;
166 size_t len = strcspn(r->content_type, "; \t");
167 AP_DEBUG_ASSERT(type != NULL);
168 ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r,
169 "Content-Type '%s' ...", r->content_type);
171 /* Handle 'content-type;charset=...' correctly */
172 if (strncmp(*type, r->content_type, len) == 0
173 && (*type)[len] == '\0') {
174 ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r,
175 "... matched '%s'", *type);
180 ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r,
181 "... did not match '%s'", *type);
185 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
186 "Content-Type condition for '%s' %s",
187 provider->frec->name,
188 match ? "matched" : "did not match");
191 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
192 "Content-Type condition for '%s' did not match: "
193 "no Content-Type", provider->frec->name);
197 /* condition matches this provider */
202 * This is a quick hack and almost certainly buggy.
203 * The idea is that by putting this in mod_filter, we relieve
204 * filter implementations of the burden of fixing up HTTP headers
205 * for cases that are routinely affected by filters.
207 * Default is ALWAYS to do nothing, so as not to tread on the
208 * toes of filters which want to do it themselves.
211 proto_flags = provider->frec->proto_flags;
213 /* some specific things can't happen in a proxy */
215 if (proto_flags & AP_FILTER_PROTO_NO_PROXY) {
216 /* can't use this provider; try next */
220 if (proto_flags & AP_FILTER_PROTO_TRANSFORM) {
221 const char *str = apr_table_get(r->headers_out,
224 if (ap_strcasestr(str, "no-transform")) {
225 /* can't use this provider; try next */
229 apr_table_addn(r->headers_out, "Warning",
230 apr_psprintf(r->pool,
231 "214 %s Transformation applied",
236 /* things that are invalidated if the filter transforms content */
237 if (proto_flags & AP_FILTER_PROTO_CHANGE) {
238 apr_table_unset(r->headers_out, "Content-MD5");
239 apr_table_unset(r->headers_out, "ETag");
240 if (proto_flags & AP_FILTER_PROTO_CHANGE_LENGTH) {
241 apr_table_unset(r->headers_out, "Content-Length");
245 /* no-cache is for a filter that has different effect per-hit */
246 if (proto_flags & AP_FILTER_PROTO_NO_CACHE) {
247 apr_table_unset(r->headers_out, "Last-Modified");
248 apr_table_addn(r->headers_out, "Cache-Control", "no-cache");
251 if (proto_flags & AP_FILTER_PROTO_NO_BYTERANGE) {
252 apr_table_setn(r->headers_out, "Accept-Ranges", "none");
254 else if (rctx && rctx->range) {
255 /* restore range header we saved earlier */
256 apr_table_setn(r->headers_in, "Range", rctx->range);
260 for (pctx = ctx->init_ctx; pctx; pctx = pctx->next) {
261 if (pctx->provider == provider) {
262 ctx->fctx = pctx->ctx ;
265 ctx->func = provider->frec->filter_func.out_func;
270 /* No provider matched */
274 static apr_status_t filter_harness(ap_filter_t *f, apr_bucket_brigade *bb)
278 const char *cachecontrol;
280 harness_ctx *ctx = f->ctx;
281 ap_filter_rec_t *filter = f->frec;
283 if (f->r->status != 200
284 && !apr_table_get(f->r->subprocess_env, "filter-errordocs")) {
285 ap_remove_output_filter(f);
286 return ap_pass_brigade(f->next, bb);
289 filter_trace(f->c, filter->debug, f->frec->name, bb);
291 /* look up a handler function if we haven't already set it */
294 if (f->r->proxyreq) {
295 if (filter->proto_flags & AP_FILTER_PROTO_NO_PROXY) {
296 ap_remove_output_filter(f);
297 return ap_pass_brigade(f->next, bb);
300 if (filter->proto_flags & AP_FILTER_PROTO_TRANSFORM) {
301 cachecontrol = apr_table_get(f->r->headers_out,
304 if (ap_strcasestr(cachecontrol, "no-transform")) {
305 ap_remove_output_filter(f);
306 return ap_pass_brigade(f->next, bb);
312 if (!filter_lookup(f, filter)) {
313 ap_remove_output_filter(f);
314 return ap_pass_brigade(f->next, bb);
316 AP_DEBUG_ASSERT(ctx->func != NULL);
319 /* call the content filter with its own context, then restore our
323 ret = ctx->func(f, bb);
331 static const char *filter_protocol(cmd_parms *cmd, void *CFG, const char *fname,
332 const char *pname, const char *proto)
334 static const char *sep = ";, \t";
337 unsigned int flags = 0;
338 mod_filter_cfg *cfg = CFG;
339 ap_filter_provider_t *provider = NULL;
340 ap_filter_rec_t *filter = apr_hash_get(cfg->live_filters, fname,
341 APR_HASH_KEY_STRING);
344 return "FilterProtocol: No such filter";
347 /* Fixup the args: it's really pname that's optional */
354 for (provider = filter->providers; provider; provider = provider->next) {
355 if (!strcasecmp(provider->frec->name, pname)) {
360 return "FilterProtocol: No such provider for this filter";
364 /* Now set flags from our args */
365 for (arg = apr_strtok(apr_pstrdup(cmd->temp_pool, proto), sep, &tok);
366 arg; arg = apr_strtok(NULL, sep, &tok)) {
368 if (!strcasecmp(arg, "change=yes")) {
369 flags |= AP_FILTER_PROTO_CHANGE | AP_FILTER_PROTO_CHANGE_LENGTH;
371 if (!strcasecmp(arg, "change=no")) {
372 flags &= ~(AP_FILTER_PROTO_CHANGE | AP_FILTER_PROTO_CHANGE_LENGTH);
374 else if (!strcasecmp(arg, "change=1:1")) {
375 flags |= AP_FILTER_PROTO_CHANGE;
377 else if (!strcasecmp(arg, "byteranges=no")) {
378 flags |= AP_FILTER_PROTO_NO_BYTERANGE;
380 else if (!strcasecmp(arg, "proxy=no")) {
381 flags |= AP_FILTER_PROTO_NO_PROXY;
383 else if (!strcasecmp(arg, "proxy=transform")) {
384 flags |= AP_FILTER_PROTO_TRANSFORM;
386 else if (!strcasecmp(arg, "cache=no")) {
387 flags |= AP_FILTER_PROTO_NO_CACHE;
392 provider->frec->proto_flags = flags;
395 filter->proto_flags = flags;
402 static const char *filter_declare(cmd_parms *cmd, void *CFG, const char *fname,
405 mod_filter_cfg *cfg = (mod_filter_cfg *)CFG;
406 ap_filter_rec_t *filter;
408 filter = apr_pcalloc(cmd->pool, sizeof(ap_filter_rec_t));
409 apr_hash_set(cfg->live_filters, fname, APR_HASH_KEY_STRING, filter);
411 filter->name = fname;
412 filter->filter_init_func = filter_init;
413 filter->filter_func.out_func = filter_harness;
414 filter->ftype = AP_FTYPE_RESOURCE;
418 if (!strcasecmp(place, "CONTENT_SET")) {
419 filter->ftype = AP_FTYPE_CONTENT_SET;
421 else if (!strcasecmp(place, "PROTOCOL")) {
422 filter->ftype = AP_FTYPE_PROTOCOL;
424 else if (!strcasecmp(place, "CONNECTION")) {
425 filter->ftype = AP_FTYPE_CONNECTION;
427 else if (!strcasecmp(place, "NETWORK")) {
428 filter->ftype = AP_FTYPE_NETWORK;
435 static const char *add_filter(cmd_parms *cmd, void *CFG,
436 const char *fname, const char *pname,
437 const char *expr, const char **types)
439 mod_filter_cfg *cfg = CFG;
440 ap_filter_provider_t *provider;
442 ap_filter_rec_t* frec;
443 ap_filter_rec_t* provider_frec;
444 ap_expr_info_t *node;
445 const char *err = NULL;
447 /* if provider has been registered, we can look it up */
448 provider_frec = ap_get_output_filter_handle(pname);
449 if (!provider_frec) {
450 return apr_psprintf(cmd->pool, "Unknown filter provider %s", pname);
453 /* fname has been declared with DeclareFilter, so we can look it up */
454 frec = apr_hash_get(cfg->live_filters, fname, APR_HASH_KEY_STRING);
456 /* or if provider is mod_filter itself, we can also look it up */
458 c = filter_declare(cmd, CFG, fname, NULL);
462 frec = apr_hash_get(cfg->live_filters, fname, APR_HASH_KEY_STRING);
463 frec->ftype = provider_frec->ftype;
467 return apr_psprintf(cmd->pool, "Undeclared smart filter %s", fname);
470 provider = apr_palloc(cmd->pool, sizeof(ap_filter_provider_t));
472 node = ap_expr_parse_cmd(cmd, expr, 0, &err, NULL);
474 return apr_pstrcat(cmd->pool,
475 "Error parsing FilterProvider expression:", err,
478 provider->expr = node;
479 provider->types = NULL;
482 provider->types = types;
483 provider->expr = NULL;
485 provider->frec = provider_frec;
486 provider->next = frec->providers;
487 frec->providers = provider;
491 static const char *filter_provider(cmd_parms *cmd, void *CFG,
492 const char *fname, const char *pname,
495 return add_filter(cmd, CFG, fname, pname, expr, NULL);
498 static const char *filter_chain(cmd_parms *cmd, void *CFG, const char *arg)
502 mod_filter_cfg *cfg = CFG;
505 case '+': /* add to end of chain */
506 p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
509 for (q = cfg->chain; q->next; q = q->next);
517 case '@': /* add to start of chain */
518 p = apr_palloc(cmd->pool, sizeof(mod_filter_chain));
520 p->next = cfg->chain;
524 case '-': /* remove from chain */
526 if (strcasecmp(cfg->chain->fname, arg+1)) {
527 for (p = cfg->chain; p->next; p = p->next) {
528 if (!strcasecmp(p->next->fname, arg+1)) {
529 p->next = p->next->next;
534 cfg->chain = cfg->chain->next;
539 case '!': /* Empty the chain */
540 /** IG: Add a NULL provider to the beginning so that
541 * we can ensure that we'll empty everything before
542 * this when doing config merges later */
543 p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
548 case '=': /* initialise chain with this arg */
549 /** IG: Prepend a NULL provider to the beginning as above */
550 p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
552 p->next = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
553 p->next->fname = arg+1;
557 default: /* add to end */
558 p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
561 for (q = cfg->chain; q->next; q = q->next);
573 static const char *filter_bytype1(cmd_parms *cmd, void *CFG,
574 const char *pname, const char **types)
579 mod_filter_cfg *cfg = CFG;
581 /* construct fname from name */
582 fname = apr_pstrcat(cmd->pool, "BYTYPE:", pname, NULL);
584 /* check whether this is already registered, in which case
585 * it's already in the filter chain
587 if (apr_hash_get(cfg->live_filters, fname, APR_HASH_KEY_STRING)) {
591 rv = add_filter(cmd, CFG, fname, pname, NULL, types);
593 /* If it's the first time through, add to filterchain */
594 if (rv == NULL && !seen_name) {
595 rv = filter_chain(cmd, CFG, fname);
600 static const char *filter_bytype(cmd_parms *cmd, void *CFG,
601 int argc, char *const argv[])
603 /* back compatibility, need to parse multiple components in filter name */
605 char *strtok_state = NULL;
608 const char *rv = NULL;
610 return "AddOutputFilterByType requires at least two arguments";
611 name = apr_pstrdup(cmd->temp_pool, argv[0]);
612 types = apr_palloc(cmd->pool, argc * sizeof(char *));
613 memcpy(types, &argv[1], (argc - 1) * sizeof(char *));
614 types[argc-1] = NULL;
615 for (pname = apr_strtok(name, ";", &strtok_state);
616 pname != NULL && rv == NULL;
617 pname = apr_strtok(NULL, ";", &strtok_state)) {
618 rv = filter_bytype1(cmd, CFG, pname, types);
623 static const char *filter_debug(cmd_parms *cmd, void *CFG, const char *fname,
626 mod_filter_cfg *cfg = CFG;
627 ap_filter_rec_t *frec = apr_hash_get(cfg->live_filters, fname,
628 APR_HASH_KEY_STRING);
630 return apr_psprintf(cmd->pool, "Undeclared smart filter %s", fname);
632 frec->debug = atoi(level);
637 static void filter_insert(request_rec *r)
640 ap_filter_rec_t *filter;
641 mod_filter_cfg *cfg = ap_get_module_config(r->per_dir_config,
645 mod_filter_ctx *ctx = apr_pcalloc(r->pool, sizeof(mod_filter_ctx));
646 ap_set_module_config(r->request_config, &filter_module, ctx);
649 /** IG: Now that we've merged to the final config, go one last time
650 * through the chain, and prune out the NULL filters */
652 for (p = cfg->chain; p; p = p->next) {
653 if (p->fname == NULL)
654 cfg->chain = p->next;
657 for (p = cfg->chain; p; p = p->next) {
658 filter = apr_hash_get(cfg->live_filters, p->fname, APR_HASH_KEY_STRING);
659 if (filter == NULL) {
660 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01380)
661 "Unknown filter %s not added", p->fname);
664 ap_add_output_filter_handle(filter, NULL, r, r->connection);
666 if (ranges && (filter->proto_flags
667 & (AP_FILTER_PROTO_NO_BYTERANGE
668 | AP_FILTER_PROTO_CHANGE_LENGTH))) {
669 ctx->range = apr_table_get(r->headers_in, "Range");
670 apr_table_unset(r->headers_in, "Range");
677 static void filter_hooks(apr_pool_t *pool)
679 ap_hook_insert_filter(filter_insert, NULL, NULL, APR_HOOK_MIDDLE);
682 static void *filter_config(apr_pool_t *pool, char *x)
684 mod_filter_cfg *cfg = apr_palloc(pool, sizeof(mod_filter_cfg));
685 cfg->live_filters = apr_hash_make(pool);
690 static void *filter_merge(apr_pool_t *pool, void *BASE, void *ADD)
692 mod_filter_cfg *base = BASE;
693 mod_filter_cfg *add = ADD;
694 mod_filter_chain *savelink = 0;
695 mod_filter_chain *newlink;
697 mod_filter_cfg *conf = apr_palloc(pool, sizeof(mod_filter_cfg));
699 conf->live_filters = apr_hash_overlay(pool, add->live_filters,
701 if (base->chain && add->chain) {
702 for (p = base->chain; p; p = p->next) {
703 newlink = apr_pmemdup(pool, p, sizeof(mod_filter_chain));
704 if (newlink->fname == NULL) {
705 conf->chain = savelink = newlink;
708 savelink->next = newlink;
712 conf->chain = savelink = newlink;
716 for (p = add->chain; p; p = p->next) {
717 newlink = apr_pmemdup(pool, p, sizeof(mod_filter_chain));
718 /** Filter out merged chain resets */
719 if (newlink->fname == NULL) {
720 conf->chain = savelink = newlink;
723 savelink->next = newlink;
727 conf->chain = savelink = newlink;
731 else if (add->chain) {
732 conf->chain = add->chain;
735 conf->chain = base->chain;
741 static const command_rec filter_cmds[] = {
742 AP_INIT_TAKE12("FilterDeclare", filter_declare, NULL, OR_OPTIONS,
743 "filter-name [filter-type]"),
744 AP_INIT_TAKE3("FilterProvider", filter_provider, NULL, OR_OPTIONS,
745 "filter-name provider-name match-expression"),
746 AP_INIT_ITERATE("FilterChain", filter_chain, NULL, OR_OPTIONS,
747 "list of filter names with optional [+-=!@]"),
748 AP_INIT_TAKE2("FilterTrace", filter_debug, NULL, RSRC_CONF | ACCESS_CONF,
749 "filter-name debug-level"),
750 AP_INIT_TAKE_ARGV("AddOutputFilterByType", filter_bytype, NULL, OR_FILEINFO,
751 "output filter name followed by one or more content-types"),
753 AP_INIT_TAKE23("FilterProtocol", filter_protocol, NULL, OR_OPTIONS,
754 "filter-name [provider-name] protocol-args"),
759 AP_DECLARE_MODULE(filter) = {
760 STANDARD20_MODULE_STUFF,