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
{
39 ap_parse_node_t
*expr
;
41 /** The filter that implements this provider */
42 ap_filter_rec_t
*frec
;
44 /** The next provider in the list */
45 ap_filter_provider_t
*next
;
48 /** we need provider_ctx to save ctx values set by providers in filter_init */
49 typedef struct provider_ctx provider_ctx
;
51 ap_filter_provider_t
*provider
;
56 ap_out_filter_func func
;
58 provider_ctx
*init_ctx
;
61 typedef struct mod_filter_chain
{
63 struct mod_filter_chain
*next
;
67 apr_hash_t
*live_filters
;
68 mod_filter_chain
*chain
;
76 static void filter_trace(conn_rec
*c
, int debug
, const char *fname
,
77 apr_bucket_brigade
*bb
)
82 case 0: /* normal, operational use */
84 case 1: /* mod_diagnostics level */
85 ap_log_cerror(APLOG_MARK
, APLOG_DEBUG
, 0, c
, "%s", fname
);
86 for (b
= APR_BRIGADE_FIRST(bb
);
87 b
!= APR_BRIGADE_SENTINEL(bb
);
88 b
= APR_BUCKET_NEXT(b
)) {
90 ap_log_cerror(APLOG_MARK
, APLOG_DEBUG
, 0, c
,
91 "%s: type: %s, length: %" APR_SIZE_T_FMT
,
92 fname
, b
->type
->name
? b
->type
->name
: "(unknown)",
99 static int filter_init(ap_filter_t
*f
)
101 ap_filter_provider_t
*p
;
104 ap_filter_rec_t
*filter
= f
->frec
;
106 harness_ctx
*fctx
= apr_pcalloc(f
->r
->pool
, sizeof(harness_ctx
));
107 for (p
= filter
->providers
; p
; p
= p
->next
) {
108 if (p
->frec
->filter_init_func
== filter_init
) {
109 ap_log_cerror(APLOG_MARK
, APLOG_ERR
, 0, f
->c
,
110 "Chaining of FilterProviders not supported");
111 return HTTP_INTERNAL_SERVER_ERROR
;
113 else if (p
->frec
->filter_init_func
) {
115 if ((err
= p
->frec
->filter_init_func(f
)) != OK
) {
116 ap_log_cerror(APLOG_MARK
, APLOG_ERR
, 0, f
->c
,
117 "filter_init for %s failed", p
->frec
->name
);
118 return err
; /* if anyone errors out here, so do we */
120 if (f
->ctx
!= NULL
) {
121 /* the filter init function set a ctx - we need to record it */
122 pctx
= apr_pcalloc(f
->r
->pool
, sizeof(provider_ctx
));
125 pctx
->next
= fctx
->init_ctx
;
126 fctx
->init_ctx
= pctx
;
133 static int filter_lookup(ap_filter_t
*f
, ap_filter_rec_t
*filter
)
135 ap_filter_provider_t
*provider
;
136 const char *str
= NULL
;
140 unsigned int proto_flags
;
141 request_rec
*r
= f
->r
;
142 harness_ctx
*ctx
= f
->ctx
;
144 mod_filter_ctx
*rctx
= ap_get_module_config(r
->request_config
,
147 /* Check registered providers in order */
148 for (provider
= filter
->providers
; provider
; provider
= provider
->next
) {
149 match
= ap_expr_eval(r
, provider
->expr
, &err
, NULL
, ap_expr_string
, NULL
);
151 /* log error but accept match value ? */
152 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, 0, r
,
153 "Error evaluating filter dispatch condition");
157 /* condition matches this provider */
162 * This is a quick hack and almost certainly buggy.
163 * The idea is that by putting this in mod_filter, we relieve
164 * filter implementations of the burden of fixing up HTTP headers
165 * for cases that are routinely affected by filters.
167 * Default is ALWAYS to do nothing, so as not to tread on the
168 * toes of filters which want to do it themselves.
171 proto_flags
= provider
->frec
->proto_flags
;
173 /* some specific things can't happen in a proxy */
175 if (proto_flags
& AP_FILTER_PROTO_NO_PROXY
) {
176 /* can't use this provider; try next */
180 if (proto_flags
& AP_FILTER_PROTO_TRANSFORM
) {
181 str
= apr_table_get(r
->headers_out
, "Cache-Control");
183 str1
= apr_pstrdup(r
->pool
, str
);
184 ap_str_tolower(str1
);
185 if (strstr(str1
, "no-transform")) {
186 /* can't use this provider; try next */
190 apr_table_addn(r
->headers_out
, "Warning",
191 apr_psprintf(r
->pool
,
192 "214 %s Transformation applied",
197 /* things that are invalidated if the filter transforms content */
198 if (proto_flags
& AP_FILTER_PROTO_CHANGE
) {
199 apr_table_unset(r
->headers_out
, "Content-MD5");
200 apr_table_unset(r
->headers_out
, "ETag");
201 if (proto_flags
& AP_FILTER_PROTO_CHANGE_LENGTH
) {
202 apr_table_unset(r
->headers_out
, "Content-Length");
206 /* no-cache is for a filter that has different effect per-hit */
207 if (proto_flags
& AP_FILTER_PROTO_NO_CACHE
) {
208 apr_table_unset(r
->headers_out
, "Last-Modified");
209 apr_table_addn(r
->headers_out
, "Cache-Control", "no-cache");
212 if (proto_flags
& AP_FILTER_PROTO_NO_BYTERANGE
) {
213 apr_table_unset(r
->headers_out
, "Accept-Ranges");
215 else if (rctx
&& rctx
->range
) {
216 /* restore range header we saved earlier */
217 apr_table_setn(r
->headers_in
, "Range", rctx
->range
);
221 for (pctx
= ctx
->init_ctx
; pctx
; pctx
= pctx
->next
) {
222 if (pctx
->provider
== provider
) {
223 ctx
->fctx
= pctx
->ctx
;
226 ctx
->func
= provider
->frec
->filter_func
.out_func
;
231 /* No provider matched */
235 static apr_status_t
filter_harness(ap_filter_t
*f
, apr_bucket_brigade
*bb
)
238 const char *cachecontrol
;
240 harness_ctx
*ctx
= f
->ctx
;
241 ap_filter_rec_t
*filter
= f
->frec
;
243 if (f
->r
->status
!= 200) {
244 ap_remove_output_filter(f
);
245 return ap_pass_brigade(f
->next
, bb
);
248 filter_trace(f
->c
, filter
->debug
, f
->frec
->name
, bb
);
250 /* look up a handler function if we haven't already set it */
253 if (f
->r
->proxyreq
) {
254 if (filter
->proto_flags
& AP_FILTER_PROTO_NO_PROXY
) {
255 ap_remove_output_filter(f
);
256 return ap_pass_brigade(f
->next
, bb
);
259 if (filter
->proto_flags
& AP_FILTER_PROTO_TRANSFORM
) {
260 cachecontrol
= apr_table_get(f
->r
->headers_out
,
263 str
= apr_pstrdup(f
->r
->pool
, cachecontrol
);
265 if (strstr(str
, "no-transform")) {
266 ap_remove_output_filter(f
);
267 return ap_pass_brigade(f
->next
, bb
);
273 if (!filter_lookup(f
, filter
)) {
274 ap_remove_output_filter(f
);
275 return ap_pass_brigade(f
->next
, bb
);
279 /* call the content filter with its own context, then restore our
283 ret
= ctx
->func(f
, bb
);
291 static const char *filter_protocol(cmd_parms
*cmd
, void *CFG
, const char *fname
,
292 const char *pname
, const char *proto
)
294 static const char *sep
= ";, \t";
297 unsigned int flags
= 0;
298 mod_filter_cfg
*cfg
= CFG
;
299 ap_filter_provider_t
*provider
= NULL
;
300 ap_filter_rec_t
*filter
= apr_hash_get(cfg
->live_filters
, fname
,
301 APR_HASH_KEY_STRING
);
304 return "FilterProtocol: No such filter";
307 /* Fixup the args: it's really pname that's optional */
314 for (provider
= filter
->providers
; provider
; provider
= provider
->next
){
315 if (!strcasecmp(provider
->frec
->name
, pname
)) {
320 return "FilterProtocol: No such provider for this filter";
324 /* Now set flags from our args */
325 for (arg
= apr_strtok(apr_pstrdup(cmd
->pool
, proto
), sep
, &tok
);
326 arg
; arg
= apr_strtok(NULL
, sep
, &tok
)) {
328 if (!strcasecmp(arg
, "change=yes")) {
329 flags
|= AP_FILTER_PROTO_CHANGE
| AP_FILTER_PROTO_CHANGE_LENGTH
;
331 else if (!strcasecmp(arg
, "change=1:1")) {
332 flags
|= AP_FILTER_PROTO_CHANGE
;
334 else if (!strcasecmp(arg
, "byteranges=no")) {
335 flags
|= AP_FILTER_PROTO_NO_BYTERANGE
;
337 else if (!strcasecmp(arg
, "proxy=no")) {
338 flags
|= AP_FILTER_PROTO_NO_PROXY
;
340 else if (!strcasecmp(arg
, "proxy=transform")) {
341 flags
|= AP_FILTER_PROTO_TRANSFORM
;
343 else if (!strcasecmp(arg
, "cache=no")) {
344 flags
|= AP_FILTER_PROTO_NO_CACHE
;
349 provider
->frec
->proto_flags
= flags
;
352 filter
->proto_flags
= flags
;
359 static const char *filter_declare(cmd_parms
*cmd
, void *CFG
, const char *fname
,
362 mod_filter_cfg
*cfg
= (mod_filter_cfg
*)CFG
;
363 ap_filter_rec_t
*filter
;
365 filter
= apr_pcalloc(cmd
->pool
, sizeof(ap_filter_rec_t
));
366 apr_hash_set(cfg
->live_filters
, fname
, APR_HASH_KEY_STRING
, filter
);
368 filter
->name
= fname
;
369 filter
->filter_init_func
= filter_init
;
370 filter
->filter_func
.out_func
= filter_harness
;
371 filter
->ftype
= AP_FTYPE_RESOURCE
;
375 if (!strcasecmp(place
, "CONTENT_SET")) {
376 filter
->ftype
= AP_FTYPE_CONTENT_SET
;
378 else if (!strcasecmp(place
, "PROTOCOL")) {
379 filter
->ftype
= AP_FTYPE_PROTOCOL
;
381 else if (!strcasecmp(place
, "CONNECTION")) {
382 filter
->ftype
= AP_FTYPE_CONNECTION
;
384 else if (!strcasecmp(place
, "NETWORK")) {
385 filter
->ftype
= AP_FTYPE_NETWORK
;
392 static const char *filter_provider(cmd_parms
*cmd
, void *CFG
,
393 const char *fname
, const char *pname
,
396 mod_filter_cfg
*cfg
= CFG
;
397 ap_filter_provider_t
*provider
;
399 ap_filter_rec_t
* frec
;
400 ap_filter_rec_t
* provider_frec
;
401 ap_parse_node_t
*node
;
404 /* fname has been declared with DeclareFilter, so we can look it up */
405 frec
= apr_hash_get(cfg
->live_filters
, fname
, APR_HASH_KEY_STRING
);
407 /* or if provider is mod_filter itself, we can also look it up */
409 c
= filter_declare(cmd
, CFG
, fname
, NULL
);
413 frec
= apr_hash_get(cfg
->live_filters
, fname
, APR_HASH_KEY_STRING
);
417 return apr_psprintf(cmd
->pool
, "Undeclared smart filter %s", fname
);
420 /* if provider has been registered, we can look it up */
421 provider_frec
= ap_get_output_filter_handle(pname
);
422 if (!provider_frec
) {
423 return apr_psprintf(cmd
->pool
, "Unknown filter provider %s", pname
);
425 node
= ap_expr_parse(cmd
->pool
, expr
, &err
);
427 return "Error parsing FilterProvider expression.";
430 provider
= apr_palloc(cmd
->pool
, sizeof(ap_filter_provider_t
));
431 provider
->expr
= node
;
432 provider
->frec
= provider_frec
;
433 provider
->next
= frec
->providers
;
434 frec
->providers
= provider
;
439 static const char *filter_chain(cmd_parms
*cmd
, void *CFG
, const char *arg
)
443 mod_filter_cfg
*cfg
= CFG
;
446 case '+': /* add to end of chain */
447 p
= apr_pcalloc(cmd
->pool
, sizeof(mod_filter_chain
));
450 for (q
= cfg
->chain
; q
->next
; q
= q
->next
);
458 case '@': /* add to start of chain */
459 p
= apr_palloc(cmd
->pool
, sizeof(mod_filter_chain
));
461 p
->next
= cfg
->chain
;
465 case '-': /* remove from chain */
467 if (strcasecmp(cfg
->chain
->fname
, arg
+1)) {
468 for (p
= cfg
->chain
; p
->next
; p
= p
->next
) {
469 if (!strcasecmp(p
->next
->fname
, arg
+1)) {
470 p
->next
= p
->next
->next
;
475 cfg
->chain
= cfg
->chain
->next
;
480 case '!': /* Empty the chain */
481 /** IG: Add a NULL provider to the beginning so that
482 * we can ensure that we'll empty everything before
483 * this when doing config merges later */
484 p
= apr_pcalloc(cmd
->pool
, sizeof(mod_filter_chain
));
489 case '=': /* initialise chain with this arg */
490 /** IG: Prepend a NULL provider to the beginning as above */
491 p
= apr_pcalloc(cmd
->pool
, sizeof(mod_filter_chain
));
493 p
->next
= apr_pcalloc(cmd
->pool
, sizeof(mod_filter_chain
));
494 p
->next
->fname
= arg
+1;
498 default: /* add to end */
499 p
= apr_pcalloc(cmd
->pool
, sizeof(mod_filter_chain
));
502 for (q
= cfg
->chain
; q
->next
; q
= q
->next
);
514 static const char *filter_debug(cmd_parms
*cmd
, void *CFG
, const char *fname
,
517 mod_filter_cfg
*cfg
= CFG
;
518 ap_filter_rec_t
*frec
= apr_hash_get(cfg
->live_filters
, fname
,
519 APR_HASH_KEY_STRING
);
521 return apr_psprintf(cmd
->pool
, "Undeclared smart filter %s", fname
);
523 frec
->debug
= atoi(level
);
528 static void filter_insert(request_rec
*r
)
531 ap_filter_rec_t
*filter
;
532 mod_filter_cfg
*cfg
= ap_get_module_config(r
->per_dir_config
,
536 mod_filter_ctx
*ctx
= apr_pcalloc(r
->pool
, sizeof(mod_filter_ctx
));
537 ap_set_module_config(r
->request_config
, &filter_module
, ctx
);
540 /** IG: Now that we've merged to the final config, go one last time
541 * through the chain, and prune out the NULL filters */
543 for (p
= cfg
->chain
; p
; p
= p
->next
) {
544 if (p
->fname
== NULL
)
545 cfg
->chain
= p
->next
;
548 for (p
= cfg
->chain
; p
; p
= p
->next
) {
549 filter
= apr_hash_get(cfg
->live_filters
, p
->fname
, APR_HASH_KEY_STRING
);
550 if (filter
== NULL
) {
551 ap_log_rerror(APLOG_MARK
, APLOG_WARNING
, 0, r
,
552 "Unknown filter %s not added", p
->fname
);
555 ap_add_output_filter_handle(filter
, NULL
, r
, r
->connection
);
557 if (ranges
&& (filter
->proto_flags
558 & (AP_FILTER_PROTO_NO_BYTERANGE
559 | AP_FILTER_PROTO_CHANGE_LENGTH
))) {
560 ctx
->range
= apr_table_get(r
->headers_in
, "Range");
561 apr_table_unset(r
->headers_in
, "Range");
570 static void filter_hooks(apr_pool_t
*pool
)
572 ap_hook_insert_filter(filter_insert
, NULL
, NULL
, APR_HOOK_MIDDLE
);
575 static void *filter_config(apr_pool_t
*pool
, char *x
)
577 mod_filter_cfg
*cfg
= apr_palloc(pool
, sizeof(mod_filter_cfg
));
578 cfg
->live_filters
= apr_hash_make(pool
);
583 static void *filter_merge(apr_pool_t
*pool
, void *BASE
, void *ADD
)
585 mod_filter_cfg
*base
= BASE
;
586 mod_filter_cfg
*add
= ADD
;
587 mod_filter_chain
*savelink
= 0;
588 mod_filter_chain
*newlink
;
590 mod_filter_cfg
*conf
= apr_palloc(pool
, sizeof(mod_filter_cfg
));
592 conf
->live_filters
= apr_hash_overlay(pool
, add
->live_filters
,
594 if (base
->chain
&& add
->chain
) {
595 for (p
= base
->chain
; p
; p
= p
->next
) {
596 newlink
= apr_pmemdup(pool
, p
, sizeof(mod_filter_chain
));
597 if (newlink
->fname
== NULL
) {
598 conf
->chain
= savelink
= newlink
;
601 savelink
->next
= newlink
;
605 conf
->chain
= savelink
= newlink
;
609 for (p
= add
->chain
; p
; p
= p
->next
) {
610 newlink
= apr_pmemdup(pool
, p
, sizeof(mod_filter_chain
));
611 /** Filter out merged chain resets */
612 if (newlink
->fname
== NULL
) {
613 conf
->chain
= savelink
= newlink
;
616 savelink
->next
= newlink
;
620 conf
->chain
= savelink
= newlink
;
624 else if (add
->chain
) {
625 conf
->chain
= add
->chain
;
628 conf
->chain
= base
->chain
;
634 static const command_rec filter_cmds
[] = {
635 AP_INIT_TAKE12("FilterDeclare", filter_declare
, NULL
, OR_OPTIONS
,
636 "filter-name [filter-type]"),
637 /** we don't have a TAKE4, so we have to use RAW_ARGS */
638 AP_INIT_TAKE3("FilterProvider", filter_provider
, NULL
, OR_OPTIONS
,
639 "filter-name provider-name match-expression"),
640 AP_INIT_ITERATE("FilterChain", filter_chain
, NULL
, OR_OPTIONS
,
641 "list of filter names with optional [+-=!@]"),
642 AP_INIT_TAKE2("FilterTrace", filter_debug
, NULL
, RSRC_CONF
| ACCESS_CONF
,
643 "filter-name debug-level"),
645 AP_INIT_TAKE23("FilterProtocol", filter_protocol
, NULL
, OR_OPTIONS
,
646 "filter-name [provider-name] protocol-args"),
651 module AP_MODULE_DECLARE_DATA filter_module
= {
652 STANDARD20_MODULE_STUFF
,