⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mod_filter.c

📁 Apache官方在今天放出产品系列2.2的最新版本2.2.11的源码包 最流行的HTTP服务器软件之一
💻 C
📖 第 1 页 / 共 2 页
字号:
/* 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 + -