removed some of the debug logging and added author details
[httpd-crcsyncproxy.git] / modules / filters / mod_filter.c
blob6ff1ad43c84c7042ccff143cb4acba15a6437b3b
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
18 #include "apr_want.h"
19 #include "apr_lib.h"
20 #include "apr_strings.h"
21 #include "apr_hash.h"
22 #include "httpd.h"
23 #include "http_config.h"
24 #include "http_request.h"
25 #include "http_log.h"
26 #include "util_filter.h"
27 #include "ap_expr.h"
29 module AP_MODULE_DECLARE_DATA filter_module;
31 /**
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;
50 struct provider_ctx {
51 ap_filter_provider_t *provider;
52 void *ctx;
53 provider_ctx *next;
55 typedef struct {
56 ap_out_filter_func func;
57 void *fctx;
58 provider_ctx *init_ctx;
59 } harness_ctx;
61 typedef struct mod_filter_chain {
62 const char *fname;
63 struct mod_filter_chain *next;
64 } mod_filter_chain;
66 typedef struct {
67 apr_hash_t *live_filters;
68 mod_filter_chain *chain;
69 } mod_filter_cfg;
71 typedef struct {
72 const char* range ;
73 } mod_filter_ctx ;
76 static void filter_trace(conn_rec *c, int debug, const char *fname,
77 apr_bucket_brigade *bb)
79 apr_bucket *b;
81 switch (debug) {
82 case 0: /* normal, operational use */
83 return;
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)",
93 b->length);
95 break;
99 static int filter_init(ap_filter_t *f)
101 ap_filter_provider_t *p;
102 provider_ctx *pctx;
103 int err;
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) {
114 f->ctx = NULL;
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));
123 pctx->provider = p;
124 pctx->ctx = f->ctx;
125 pctx->next = fctx->init_ctx;
126 fctx->init_ctx = pctx;
130 f->ctx = fctx;
131 return OK;
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;
137 char *str1;
138 int match;
139 int err = 0;
140 unsigned int proto_flags;
141 request_rec *r = f->r;
142 harness_ctx *ctx = f->ctx;
143 provider_ctx *pctx;
144 mod_filter_ctx *rctx = ap_get_module_config(r->request_config,
145 &filter_module);
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);
150 if (err) {
151 /* log error but accept match value ? */
152 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
153 "Error evaluating filter dispatch condition");
156 if (match) {
157 /* condition matches this provider */
158 #ifndef NO_PROTOCOL
159 /* check protocol
161 * FIXME:
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 */
174 if (r->proxyreq) {
175 if (proto_flags & AP_FILTER_PROTO_NO_PROXY) {
176 /* can't use this provider; try next */
177 continue;
180 if (proto_flags & AP_FILTER_PROTO_TRANSFORM) {
181 str = apr_table_get(r->headers_out, "Cache-Control");
182 if (str) {
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 */
187 continue;
190 apr_table_addn(r->headers_out, "Warning",
191 apr_psprintf(r->pool,
192 "214 %s Transformation applied",
193 r->hostname));
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);
218 rctx->range = NULL;
220 #endif
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;
227 return 1;
231 /* No provider matched */
232 return 0;
235 static apr_status_t filter_harness(ap_filter_t *f, apr_bucket_brigade *bb)
237 apr_status_t ret;
238 const char *cachecontrol;
239 char *str;
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 */
251 if (!ctx->func) {
252 #ifndef NO_PROTOCOL
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,
261 "Cache-Control");
262 if (cachecontrol) {
263 str = apr_pstrdup(f->r->pool, cachecontrol);
264 ap_str_tolower(str);
265 if (strstr(str, "no-transform")) {
266 ap_remove_output_filter(f);
267 return ap_pass_brigade(f->next, bb);
272 #endif
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
280 * context
282 f->ctx = ctx->fctx;
283 ret = ctx->func(f, bb);
284 ctx->fctx = f->ctx;
285 f->ctx = ctx;
287 return ret;
290 #ifndef NO_PROTOCOL
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";
295 char *arg;
296 char *tok = 0;
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);
303 if (!filter) {
304 return "FilterProtocol: No such filter";
307 /* Fixup the args: it's really pname that's optional */
308 if (proto == NULL) {
309 proto = pname;
310 pname = NULL;
312 else {
313 /* Find provider */
314 for (provider = filter->providers; provider; provider = provider->next){
315 if (!strcasecmp(provider->frec->name, pname)) {
316 break;
319 if (!provider) {
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;
348 if (pname) {
349 provider->frec->proto_flags = flags;
351 else {
352 filter->proto_flags = flags;
355 return NULL;
357 #endif
359 static const char *filter_declare(cmd_parms *cmd, void *CFG, const char *fname,
360 const char *place)
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;
372 filter->next = NULL;
374 if (place) {
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;
389 return NULL;
392 static const char *filter_provider(cmd_parms *cmd, void *CFG,
393 const char *fname, const char *pname,
394 const char *expr)
396 mod_filter_cfg *cfg = CFG;
397 ap_filter_provider_t *provider;
398 const char *c;
399 ap_filter_rec_t* frec;
400 ap_filter_rec_t* provider_frec;
401 ap_parse_node_t *node;
402 int err = 0;
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 */
408 if (!frec) {
409 c = filter_declare(cmd, CFG, fname, NULL);
410 if ( c ) {
411 return c;
413 frec = apr_hash_get(cfg->live_filters, fname, APR_HASH_KEY_STRING);
416 if (!frec) {
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);
426 if (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;
436 return NULL;
439 static const char *filter_chain(cmd_parms *cmd, void *CFG, const char *arg)
441 mod_filter_chain *p;
442 mod_filter_chain *q;
443 mod_filter_cfg *cfg = CFG;
445 switch (arg[0]) {
446 case '+': /* add to end of chain */
447 p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
448 p->fname = arg+1;
449 if (cfg->chain) {
450 for (q = cfg->chain; q->next; q = q->next);
451 q->next = p;
453 else {
454 cfg->chain = p;
456 break;
458 case '@': /* add to start of chain */
459 p = apr_palloc(cmd->pool, sizeof(mod_filter_chain));
460 p->fname = arg+1;
461 p->next = cfg->chain;
462 cfg->chain = p;
463 break;
465 case '-': /* remove from chain */
466 if (cfg->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;
474 else {
475 cfg->chain = cfg->chain->next;
478 break;
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));
485 p->fname = NULL;
486 cfg->chain = p;
487 break;
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));
492 p->fname = NULL;
493 p->next = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
494 p->next->fname = arg+1;
495 cfg->chain = p;
496 break;
498 default: /* add to end */
499 p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
500 p->fname = arg;
501 if (cfg->chain) {
502 for (q = cfg->chain; q->next; q = q->next);
503 q->next = p;
505 else {
506 cfg->chain = p;
508 break;
511 return NULL;
514 static const char *filter_debug(cmd_parms *cmd, void *CFG, const char *fname,
515 const char *level)
517 mod_filter_cfg *cfg = CFG;
518 ap_filter_rec_t *frec = apr_hash_get(cfg->live_filters, fname,
519 APR_HASH_KEY_STRING);
520 if (!frec) {
521 return apr_psprintf(cmd->pool, "Undeclared smart filter %s", fname);
523 frec->debug = atoi(level);
525 return NULL;
528 static void filter_insert(request_rec *r)
530 mod_filter_chain *p;
531 ap_filter_rec_t *filter;
532 mod_filter_cfg *cfg = ap_get_module_config(r->per_dir_config,
533 &filter_module);
534 #ifndef NO_PROTOCOL
535 int ranges = 1;
536 mod_filter_ctx *ctx = apr_pcalloc(r->pool, sizeof(mod_filter_ctx));
537 ap_set_module_config(r->request_config, &filter_module, ctx);
538 #endif
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);
553 continue;
555 ap_add_output_filter_handle(filter, NULL, r, r->connection);
556 #ifndef NO_PROTOCOL
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");
562 ranges = 0;
564 #endif
567 return;
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);
579 cfg->chain = NULL;
580 return cfg;
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;
589 mod_filter_chain *p;
590 mod_filter_cfg *conf = apr_palloc(pool, sizeof(mod_filter_cfg));
592 conf->live_filters = apr_hash_overlay(pool, add->live_filters,
593 base->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;
600 else if (savelink) {
601 savelink->next = newlink;
602 savelink = newlink;
604 else {
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;
615 else if (savelink) {
616 savelink->next = newlink;
617 savelink = newlink;
619 else {
620 conf->chain = savelink = newlink;
624 else if (add->chain) {
625 conf->chain = add->chain;
627 else {
628 conf->chain = base->chain;
631 return conf;
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"),
644 #ifndef NO_PROTOCOL
645 AP_INIT_TAKE23("FilterProtocol", filter_protocol, NULL, OR_OPTIONS,
646 "filter-name [provider-name] protocol-args"),
647 #endif
648 { NULL }
651 module AP_MODULE_DECLARE_DATA filter_module = {
652 STANDARD20_MODULE_STUFF,
653 filter_config,
654 filter_merge,
655 NULL,
656 NULL,
657 filter_cmds,
658 filter_hooks