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

📄 mod_ext_filter.c

📁 linux网络服务器工具
💻 C
📖 第 1 页 / 共 2 页
字号:
    ap_add_cgi_vars(f->r);    ap_add_common_vars(f->r);    apr_table_setn(f->r->subprocess_env, "DOCUMENT_URI", f->r->uri);    apr_table_setn(f->r->subprocess_env, "DOCUMENT_PATH_INFO", f->r->path_info);    if (f->r->args) {            /* QUERY_STRING is added by ap_add_cgi_vars */        char *arg_copy = apr_pstrdup(f->r->pool, f->r->args);        ap_unescape_url(arg_copy);        apr_table_setn(f->r->subprocess_env, "QUERY_STRING_UNESCAPED",                       ap_escape_shell_cmd(f->r->pool, arg_copy));    }    env = (const char * const *) ap_create_environment(ctx->p,                                                       f->r->subprocess_env);    rc = apr_proc_create(ctx->proc,                            ctx->filter->command,                            (const char * const *)ctx->filter->args,                            env, /* environment */                            ctx->procattr,                            ctx->p);    if (rc != APR_SUCCESS) {        ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, f->r,                      "couldn't create child process to run `%s'",                      ctx->filter->command);        return rc;    }    apr_pool_note_subprocess(ctx->p, ctx->proc, APR_KILL_AFTER_TIMEOUT);    /* We don't want the handle to the child's stdin inherited by any     * other processes created by httpd.  Otherwise, when we close our     * handle, the child won't see EOF because another handle will still     * be open.     */    apr_pool_cleanup_register(ctx->p, ctx->proc->in,                         apr_pool_cleanup_null, /* other mechanism */                         ef_close_file);#if APR_FILES_AS_SOCKETS    {        apr_pollfd_t pfd = { 0 };        rc = apr_pollset_create(&ctx->pollset, 2, ctx->p, 0);        ap_assert(rc == APR_SUCCESS);        pfd.p         = ctx->p;        pfd.desc_type = APR_POLL_FILE;        pfd.reqevents = APR_POLLOUT;        pfd.desc.f    = ctx->proc->in;        rc = apr_pollset_add(ctx->pollset, &pfd);        ap_assert(rc == APR_SUCCESS);        pfd.reqevents = APR_POLLIN;        pfd.desc.f    = ctx->proc->out;        rc = apr_pollset_add(ctx->pollset, &pfd);        ap_assert(rc == APR_SUCCESS);    }#endif    return APR_SUCCESS;}static const char *get_cfg_string(ef_dir_t *dc, ef_filter_t *filter, apr_pool_t *p){    const char *debug_str = dc->debug == -1 ?        "DebugLevel=0" : apr_psprintf(p, "DebugLevel=%d", dc->debug);    const char *log_stderr_str = dc->log_stderr < 1 ?        "NoLogStderr" : "LogStderr";    const char *preserve_content_length_str = filter->preserves_content_length ?        "PreservesContentLength" : "!PreserveContentLength";    const char *intype_str = !filter->intype ?        "*/*" : filter->intype;    const char *outtype_str = !filter->outtype ?        "(unchanged)" : filter->outtype;    return apr_psprintf(p,                        "ExtFilterOptions %s %s %s ExtFilterInType %s "                        "ExtFilterOuttype %s",                        debug_str, log_stderr_str, preserve_content_length_str,                        intype_str, outtype_str);}static ef_filter_t *find_filter_def(const server_rec *s, const char *fname){    ef_server_t *sc;    ef_filter_t *f;    sc = ap_get_module_config(s->module_config, &ext_filter_module);    f = apr_hash_get(sc->h, fname, APR_HASH_KEY_STRING);    if (!f && s != main_server) {        s = main_server;        sc = ap_get_module_config(s->module_config, &ext_filter_module);        f = apr_hash_get(sc->h, fname, APR_HASH_KEY_STRING);    }    return f;}static apr_status_t init_filter_instance(ap_filter_t *f){    ef_ctx_t *ctx;    ef_dir_t *dc;    apr_status_t rv;    f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(ef_ctx_t));    dc = ap_get_module_config(f->r->per_dir_config,                              &ext_filter_module);    ctx->dc = dc;    /* look for the user-defined filter */    ctx->filter = find_filter_def(f->r->server, f->frec->name);    if (!ctx->filter) {        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r,                      "couldn't find definition of filter '%s'",                      f->frec->name);        return APR_EINVAL;    }    ctx->p = f->r->pool;    if (ctx->filter->intype &&        ctx->filter->intype != INTYPE_ALL) {        const char *ctypes;        if (ctx->filter->mode == INPUT_FILTER) {            ctypes = apr_table_get(f->r->headers_in, "Content-Type");        }        else {            ctypes = f->r->content_type;        }        if (ctypes) {            const char *ctype = ap_getword(f->r->pool, &ctypes, ';');            if (strcasecmp(ctx->filter->intype, ctype)) {                /* wrong IMT for us; don't mess with the output */                ctx->noop = 1;            }        }        else {            ctx->noop = 1;        }    }    if (ctx->filter->enable_env &&        !apr_table_get(f->r->subprocess_env, ctx->filter->enable_env)) {        /* an environment variable that enables the filter isn't set; bail */        ctx->noop = 1;    }    if (ctx->filter->disable_env &&        apr_table_get(f->r->subprocess_env, ctx->filter->disable_env)) {        /* an environment variable that disables the filter is set; bail */        ctx->noop = 1;    }    if (!ctx->noop) {        rv = init_ext_filter_process(f);        if (rv != APR_SUCCESS) {            return rv;        }        if (ctx->filter->outtype &&            ctx->filter->outtype != OUTTYPE_UNCHANGED) {            ap_set_content_type(f->r, ctx->filter->outtype);        }        if (ctx->filter->preserves_content_length != 1) {            /* nasty, but needed to avoid confusing the browser             */            apr_table_unset(f->r->headers_out, "Content-Length");        }    }    if (dc->debug >= DBGLVL_SHOWOPTIONS) {        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r,                      "%sfiltering `%s' of type `%s' through `%s', cfg %s",                      ctx->noop ? "NOT " : "",                      f->r->uri ? f->r->uri : f->r->filename,                      f->r->content_type ? f->r->content_type : "(unspecified)",                      ctx->filter->command,                      get_cfg_string(dc, ctx->filter, f->r->pool));    }    return APR_SUCCESS;}/* drain_available_output(): * * if any data is available from the filter, read it and append it * to the the bucket brigade */static apr_status_t drain_available_output(ap_filter_t *f,                                           apr_bucket_brigade *bb){    request_rec *r = f->r;    conn_rec *c = r->connection;    ef_ctx_t *ctx = f->ctx;    ef_dir_t *dc = ctx->dc;    apr_size_t len;    char buf[4096];    apr_status_t rv;    apr_bucket *b;    while (1) {        len = sizeof(buf);        rv = apr_file_read(ctx->proc->out,                      buf,                      &len);        if ((rv && !APR_STATUS_IS_EAGAIN(rv)) ||            dc->debug >= DBGLVL_GORY) {            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r,                          "apr_file_read(child output), len %" APR_SIZE_T_FMT,                          !rv ? len : -1);        }        if (rv != APR_SUCCESS) {            return rv;        }        b = apr_bucket_heap_create(buf, len, NULL, c->bucket_alloc);        APR_BRIGADE_INSERT_TAIL(bb, b);        return APR_SUCCESS;    }    /* we should never get here; if we do, a bogus error message would be     * the least of our problems     */    return APR_ANONYMOUS;}static apr_status_t pass_data_to_filter(ap_filter_t *f, const char *data,                                        apr_size_t len, apr_bucket_brigade *bb){    ef_ctx_t *ctx = f->ctx;    ef_dir_t *dc = ctx->dc;    apr_status_t rv;    apr_size_t bytes_written = 0;    apr_size_t tmplen;    do {        tmplen = len - bytes_written;        rv = apr_file_write(ctx->proc->in,                       (const char *)data + bytes_written,                       &tmplen);        bytes_written += tmplen;        if (rv != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(rv)) {            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r,                          "apr_file_write(child input), len %" APR_SIZE_T_FMT,                          tmplen);            return rv;        }        if (APR_STATUS_IS_EAGAIN(rv)) {            /* XXX handle blocking conditions here...  if we block, we need             * to read data from the child process and pass it down to the             * next filter!             */            rv = drain_available_output(f, bb);            if (APR_STATUS_IS_EAGAIN(rv)) {#if APR_FILES_AS_SOCKETS                int num_events;                const apr_pollfd_t *pdesc;                rv = apr_pollset_poll(ctx->pollset, f->r->server->timeout,                                      &num_events, &pdesc);                if (rv || dc->debug >= DBGLVL_GORY) {                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG,                                  rv, f->r, "apr_pollset_poll()");                }                if (rv != APR_SUCCESS && !APR_STATUS_IS_EINTR(rv)) {                    /* some error such as APR_TIMEUP */                    return rv;                }#else /* APR_FILES_AS_SOCKETS */                /* Yuck... I'd really like to wait until I can read                 * or write, but instead I have to sleep and try again                 */                apr_sleep(100000); /* 100 milliseconds */                if (dc->debug >= DBGLVL_GORY) {                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG,                                  0, f->r, "apr_sleep()");                }#endif /* APR_FILES_AS_SOCKETS */            }            else if (rv != APR_SUCCESS) {                return rv;            }        }    } while (bytes_written < len);    return rv;}/* ef_unified_filter: * * runs the bucket brigade bb through the filter and puts the result into * bb, dropping the previous content of bb (the input) */static int ef_unified_filter(ap_filter_t *f, apr_bucket_brigade *bb){    request_rec *r = f->r;    conn_rec *c = r->connection;    ef_ctx_t *ctx = f->ctx;    apr_bucket *b;    ef_dir_t *dc;    apr_size_t len;    const char *data;    apr_status_t rv;    char buf[4096];    apr_bucket *eos = NULL;    apr_bucket_brigade *bb_tmp;    dc = ctx->dc;    bb_tmp = apr_brigade_create(r->pool, c->bucket_alloc);    for (b = APR_BRIGADE_FIRST(bb);         b != APR_BRIGADE_SENTINEL(bb);         b = APR_BUCKET_NEXT(b))    {        if (APR_BUCKET_IS_EOS(b)) {            eos = b;            break;        }        rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);        if (rv != APR_SUCCESS) {            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "apr_bucket_read()");            return rv;        }        /* Good cast, we just tested len isn't negative */        if (len > 0 &&            (rv = pass_data_to_filter(f, data, (apr_size_t)len, bb_tmp))                != APR_SUCCESS) {            return rv;        }    }    apr_brigade_cleanup(bb);    APR_BRIGADE_CONCAT(bb, bb_tmp);    apr_brigade_destroy(bb_tmp);    if (eos) {        /* close the child's stdin to signal that no more data is coming;         * that will cause the child to finish generating output         */        if ((rv = apr_file_close(ctx->proc->in)) != APR_SUCCESS) {            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,                          "apr_file_close(child input)");            return rv;        }        /* since we've seen eos and closed the child's stdin, set the proper pipe         * timeout; we don't care if we don't return from apr_file_read() for a while...         */        rv = apr_file_pipe_timeout_set(ctx->proc->out,                                       r->server->timeout);        if (rv) {            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,                          "apr_file_pipe_timeout_set(child output)");            return rv;        }    }    do {        len = sizeof(buf);        rv = apr_file_read(ctx->proc->out,                      buf,                      &len);        if ((rv && !APR_STATUS_IS_EOF(rv) && !APR_STATUS_IS_EAGAIN(rv)) ||            dc->debug >= DBGLVL_GORY) {            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r,                          "apr_file_read(child output), len %" APR_SIZE_T_FMT,                          !rv ? len : -1);        }        if (APR_STATUS_IS_EAGAIN(rv)) {            if (eos) {                /* should not occur, because we have an APR timeout in place */                AP_DEBUG_ASSERT(1 != 1);            }            return APR_SUCCESS;        }        if (rv == APR_SUCCESS) {            b = apr_bucket_heap_create(buf, len, NULL, c->bucket_alloc);            APR_BRIGADE_INSERT_TAIL(bb, b);        }    } while (rv == APR_SUCCESS);    if (!APR_STATUS_IS_EOF(rv)) {        return rv;    }    if (eos) {        b = apr_bucket_eos_create(c->bucket_alloc);        APR_BRIGADE_INSERT_TAIL(bb, b);    }    return APR_SUCCESS;}static apr_status_t ef_output_filter(ap_filter_t *f, apr_bucket_brigade *bb){    request_rec *r = f->r;    ef_ctx_t *ctx = f->ctx;    apr_status_t rv;    if (!ctx) {        if ((rv = init_filter_instance(f)) != APR_SUCCESS) {            return rv;        }        ctx = f->ctx;    }    if (ctx->noop) {        ap_remove_output_filter(f);        return ap_pass_brigade(f->next, bb);    }    rv = ef_unified_filter(f, bb);    if (rv != APR_SUCCESS) {        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,                      "ef_unified_filter() failed");    }    if ((rv = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) {        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,                      "ap_pass_brigade() failed");    }    return rv;}static int ef_input_filter(ap_filter_t *f, apr_bucket_brigade *bb,                           ap_input_mode_t mode, apr_read_type_e block,                           apr_off_t readbytes){    ef_ctx_t *ctx = f->ctx;    apr_status_t rv;    if (!ctx) {        if ((rv = init_filter_instance(f)) != APR_SUCCESS) {            return rv;        }        ctx = f->ctx;    }    if (ctx->noop) {        ap_remove_input_filter(f);        return ap_get_brigade(f->next, bb, mode, block, readbytes);    }    rv = ap_get_brigade(f->next, bb, mode, block, readbytes);    if (rv != APR_SUCCESS) {        return rv;    }    rv = ef_unified_filter(f, bb);    return rv;}module AP_MODULE_DECLARE_DATA ext_filter_module ={    STANDARD20_MODULE_STUFF,    create_ef_dir_conf,    merge_ef_dir_conf,    create_ef_server_conf,    NULL,    cmds,    register_hooks};

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -