📄 mod_filter.c
字号:
/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */#define APR_WANT_STRFUNC#include "apr_want.h"#include "apr_lib.h"#include "apr_strings.h"#include "apr_hash.h"#include "httpd.h"#include "http_config.h"#include "http_request.h"#include "http_log.h"#include "util_filter.h"module AP_MODULE_DECLARE_DATA filter_module;/** * @brief is a filter provider, as defined and implemented by mod_filter. * * The struct is a linked list, with dispatch criteria * defined for each filter. The provider implementation itself is a * (2.0-compatible) ap_filter_rec_t* frec. */struct ap_filter_provider_t { /** How to match this provider to filter dispatch criterion */ enum { STRING_MATCH, STRING_CONTAINS, REGEX_MATCH, INT_EQ, INT_LT, INT_LE, INT_GT, INT_GE, DEFINED } match_type; /** negation on match_type */ int not; /** The dispatch match itself - union member depends on match_type */ union { const char *string; ap_regex_t *regex; int number; } match; /** The filter that implements this provider */ ap_filter_rec_t *frec; /** The next provider in the list */ ap_filter_provider_t *next; /** Dispatch criteria for filter providers */ enum { HANDLER, REQUEST_HEADERS, RESPONSE_HEADERS, SUBPROCESS_ENV, CONTENT_TYPE } dispatch; /** Match value for filter providers */ const char* value;};/** we need provider_ctx to save ctx values set by providers in filter_init */typedef struct provider_ctx provider_ctx;struct provider_ctx { ap_filter_provider_t *provider; void *ctx; provider_ctx *next;};typedef struct { ap_out_filter_func func; void *fctx; provider_ctx *init_ctx;} harness_ctx;typedef struct mod_filter_chain { const char *fname; struct mod_filter_chain *next;} mod_filter_chain;typedef struct { apr_hash_t *live_filters; mod_filter_chain *chain;} mod_filter_cfg;typedef struct { const char* range ;} mod_filter_ctx ;static void filter_trace(conn_rec *c, int debug, const char *fname, apr_bucket_brigade *bb){ apr_bucket *b; switch (debug) { case 0: /* normal, operational use */ return; case 1: /* mod_diagnostics level */ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "%s", fname); for (b = APR_BRIGADE_FIRST(bb); b != APR_BRIGADE_SENTINEL(bb); b = APR_BUCKET_NEXT(b)) { ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "%s: type: %s, length: %" APR_SIZE_T_FMT, fname, b->type->name ? b->type->name : "(unknown)", b->length); } break; }}static int filter_init(ap_filter_t *f){ ap_filter_provider_t *p; provider_ctx *pctx; int err; ap_filter_rec_t *filter = f->frec; harness_ctx *fctx = apr_pcalloc(f->r->pool, sizeof(harness_ctx)); for (p = filter->providers; p; p = p->next) { if (p->frec->filter_init_func == filter_init) { ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c, "Chaining of FilterProviders not supported"); return HTTP_INTERNAL_SERVER_ERROR; } else if (p->frec->filter_init_func) { f->ctx = NULL; if ((err = p->frec->filter_init_func(f)) != OK) { ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c, "filter_init for %s failed", p->frec->name); return err; /* if anyone errors out here, so do we */ } if (f->ctx != NULL) { /* the filter init function set a ctx - we need to record it */ pctx = apr_pcalloc(f->r->pool, sizeof(provider_ctx)); pctx->provider = p; pctx->ctx = f->ctx; pctx->next = fctx->init_ctx; fctx->init_ctx = pctx; } } } f->ctx = fctx; return OK;}static int filter_lookup(ap_filter_t *f, ap_filter_rec_t *filter){ ap_filter_provider_t *provider; const char *str = NULL; char *str1; int match; unsigned int proto_flags; request_rec *r = f->r; harness_ctx *ctx = f->ctx; provider_ctx *pctx; mod_filter_ctx *rctx = ap_get_module_config(r->request_config, &filter_module); /* Check registered providers in order */ for (provider = filter->providers; provider; provider = provider->next) { match = 1; switch (provider->dispatch) { case REQUEST_HEADERS: str = apr_table_get(r->headers_in, provider->value); break; case RESPONSE_HEADERS: str = apr_table_get(r->headers_out, provider->value); break; case SUBPROCESS_ENV: str = apr_table_get(r->subprocess_env, provider->value); break; case CONTENT_TYPE: str = r->content_type; break; case HANDLER: str = r->handler; break; } /* treat nulls so we don't have to check every strcmp individually * Not sure if there's anything better to do with them */ if (!str) { if (provider->match_type == DEFINED && provider->match.string) { match = 0; } } /* we can't check for NULL in provider as that kills integer 0 * so we have to test each string/regexp case in the switch */ else { switch (provider->match_type) { case STRING_MATCH: if (strcasecmp(str, provider->match.string)) { match = 0; } break; case STRING_CONTAINS: str1 = apr_pstrdup(r->pool, str); ap_str_tolower(str1); if (!strstr(str1, provider->match.string)) { match = 0; } break; case REGEX_MATCH: if (ap_regexec(provider->match.regex, str, 0, NULL, 0) == AP_REG_NOMATCH) { match = 0; } break; case INT_EQ: if (atoi(str) != provider->match.number) { match = 0; } break; /* Integer comparisons should be [var] OP [match] * We need to set match = 0 if the condition fails */ case INT_LT: if (atoi(str) >= provider->match.number) { match = 0; } break; case INT_LE: if (atoi(str) > provider->match.number) { match = 0; } break; case INT_GT: if (atoi(str) <= provider->match.number) { match = 0; } break; case INT_GE: if (atoi(str) < provider->match.number) { match = 0; } break; case DEFINED: /* we already handled this:-) */ break; } } if (match != provider->not) { /* condition matches this provider */#ifndef NO_PROTOCOL /* check protocol * * FIXME: * This is a quick hack and almost certainly buggy. * The idea is that by putting this in mod_filter, we relieve * filter implementations of the burden of fixing up HTTP headers * for cases that are routinely affected by filters. * * Default is ALWAYS to do nothing, so as not to tread on the * toes of filters which want to do it themselves. * */ proto_flags = provider->frec->proto_flags; /* some specific things can't happen in a proxy */ if (r->proxyreq) { if (proto_flags & AP_FILTER_PROTO_NO_PROXY) { /* can't use this provider; try next */ continue; } if (proto_flags & AP_FILTER_PROTO_TRANSFORM) { str = apr_table_get(r->headers_out, "Cache-Control"); if (str) { str1 = apr_pstrdup(r->pool, str); ap_str_tolower(str1); if (strstr(str1, "no-transform")) { /* can't use this provider; try next */ continue; } } apr_table_addn(r->headers_out, "Warning", apr_psprintf(r->pool, "214 %s Transformation applied", r->hostname)); } } /* things that are invalidated if the filter transforms content */ if (proto_flags & AP_FILTER_PROTO_CHANGE) { apr_table_unset(r->headers_out, "Content-MD5"); apr_table_unset(r->headers_out, "ETag"); if (proto_flags & AP_FILTER_PROTO_CHANGE_LENGTH) { apr_table_unset(r->headers_out, "Content-Length"); } } /* no-cache is for a filter that has different effect per-hit */ if (proto_flags & AP_FILTER_PROTO_NO_CACHE) { apr_table_unset(r->headers_out, "Last-Modified"); apr_table_addn(r->headers_out, "Cache-Control", "no-cache"); } if (proto_flags & AP_FILTER_PROTO_NO_BYTERANGE) { apr_table_unset(r->headers_out, "Accept-Ranges"); } else if (rctx && rctx->range) { /* restore range header we saved earlier */ apr_table_setn(r->headers_in, "Range", rctx->range); rctx->range = NULL; }#endif for (pctx = ctx->init_ctx; pctx; pctx = pctx->next) { if (pctx->provider == provider) { ctx->fctx = pctx->ctx ; } } ctx->func = provider->frec->filter_func.out_func; return 1; } } /* No provider matched */ return 0;}static apr_status_t filter_harness(ap_filter_t *f, apr_bucket_brigade *bb){ apr_status_t ret; const char *cachecontrol; char *str; harness_ctx *ctx = f->ctx; ap_filter_rec_t *filter = f->frec; if (f->r->status != 200) { ap_remove_output_filter(f); return ap_pass_brigade(f->next, bb); } filter_trace(f->c, filter->debug, f->frec->name, bb); /* look up a handler function if we haven't already set it */ if (!ctx->func) {#ifndef NO_PROTOCOL if (f->r->proxyreq) { if (filter->proto_flags & AP_FILTER_PROTO_NO_PROXY) { ap_remove_output_filter(f); return ap_pass_brigade(f->next, bb); } if (filter->proto_flags & AP_FILTER_PROTO_TRANSFORM) { cachecontrol = apr_table_get(f->r->headers_out, "Cache-Control"); if (cachecontrol) { str = apr_pstrdup(f->r->pool, cachecontrol); ap_str_tolower(str); if (strstr(str, "no-transform")) { ap_remove_output_filter(f); return ap_pass_brigade(f->next, bb); } } } }#endif if (!filter_lookup(f, filter)) { ap_remove_output_filter(f); return ap_pass_brigade(f->next, bb); } } /* call the content filter with its own context, then restore our * context */ f->ctx = ctx->fctx; ret = ctx->func(f, bb); ctx->fctx = f->ctx; f->ctx = ctx; return ret;}#ifndef NO_PROTOCOLstatic const char *filter_protocol(cmd_parms *cmd, void *CFG, const char *fname, const char *pname, const char *proto){ static const char *sep = ";, \t"; char *arg; char *tok = 0; unsigned int flags = 0; mod_filter_cfg *cfg = CFG; ap_filter_provider_t *provider = NULL; ap_filter_rec_t *filter = apr_hash_get(cfg->live_filters, fname, APR_HASH_KEY_STRING); if (!filter) { return "FilterProtocol: No such filter"; } /* Fixup the args: it's really pname that's optional */ if (proto == NULL) { proto = pname; pname = NULL; } else { /* Find provider */ for (provider = filter->providers; provider; provider = provider->next){ if (!strcasecmp(provider->frec->name, pname)) { break; } } if (!provider) { return "FilterProtocol: No such provider for this filter"; } } /* Now set flags from our args */ for (arg = apr_strtok(apr_pstrdup(cmd->pool, proto), sep, &tok); arg; arg = apr_strtok(NULL, sep, &tok)) { if (!strcasecmp(arg, "change=yes")) { flags |= AP_FILTER_PROTO_CHANGE | AP_FILTER_PROTO_CHANGE_LENGTH; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -