| /* |
| ** Copyright 2003-2004 The Apache Software Foundation |
| ** |
| ** Licensed 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. |
| */ |
| |
| #include "httpd.h" |
| #include "http_config.h" |
| #include "http_log.h" |
| #include "util_filter.h" |
| #include "apr_tables.h" |
| #include "apr_buckets.h" |
| #include "http_request.h" |
| #include "apr_strings.h" |
| |
| #include "apreq.h" |
| #include "apreq_env.h" |
| #include "apreq_params.h" |
| #include "apreq_cookie.h" |
| |
| #define dR request_rec *r = (request_rec *)env |
| |
| struct dir_config { |
| const char *temp_dir; |
| apr_off_t max_body; |
| apr_ssize_t max_brigade; |
| }; |
| |
| |
| static void *apreq_create_dir_config(apr_pool_t *p, char *d) |
| { |
| /* d == OR_ALL */ |
| struct dir_config *dc = apr_palloc(p, sizeof *dc); |
| dc->temp_dir = NULL; |
| dc->max_body = -1; |
| dc->max_brigade = APREQ_MAX_BRIGADE_LEN; |
| return dc; |
| } |
| |
| static void *apreq_merge_dir_config(apr_pool_t *p, void *a_, void *b_) |
| { |
| struct dir_config *a = a_, *b = b_, *c = apr_palloc(p, sizeof *c); |
| c->temp_dir = (b->temp_dir != NULL) ? b->temp_dir : a->temp_dir; |
| c->max_body = (b->max_body >= 0) ? b->max_body : a->max_body; |
| c->max_brigade = (b->max_brigade >= 0) ? b->max_brigade : a->max_brigade; |
| return c; |
| } |
| |
| |
| /* The "warehouse", stored in r->request_config */ |
| struct env_config { |
| apreq_jar_t *jar; /* Active jar for the current request_rec */ |
| apreq_request_t *req; /* Active request for current request_rec */ |
| ap_filter_t *f; /* Active apreq filter for this request_rec */ |
| const char *temp_dir; /* Temporary directory for spool files */ |
| apr_off_t max_body; /* Maximum bytes the parser may see */ |
| apr_ssize_t max_brigade; /* Maximum heap space for brigades */ |
| }; |
| |
| /* Tracks the apreq filter state */ |
| struct filter_ctx { |
| request_rec *r; /* request that originally created this filter */ |
| apr_bucket_brigade *bb; /* input brigade that's passed to the parser */ |
| apr_bucket_brigade *spool; /* copied prefetch data for downstream filters */ |
| apr_status_t status;/* APR_SUCCESS, APR_INCOMPLETE, or parse error */ |
| unsigned saw_eos; /* Has EOS bucket appeared in filter? */ |
| apr_off_t bytes_read; /* Total bytes read into this filter. */ |
| }; |
| |
| static const char filter_name[] = "APREQ"; |
| module AP_MODULE_DECLARE_DATA apreq_module; |
| |
| /** |
| * @defgroup mod_apreq Apache 2.X Filter Module |
| * @ingroup apreq_env |
| * @brief mod_apreq - DSO that ties libapreq2 to Apache 2.X. |
| * |
| * mod_apreq provides the "APREQ" input filter for using libapreq2 |
| * (and allow its parsed data structures to be shared) within |
| * the Apache 2.X webserver. Using it, libapreq2 works properly |
| * in every phase of the HTTP request, from translation handlers |
| * to output filters, and even for subrequests / internal redirects. |
| * |
| * <hr> |
| * |
| * <h2>Activating mod_apreq in Apache 2.X</h2> |
| * |
| * Normally the installation process triggered by |
| * <code>% make install</code> |
| * will make the necessary changes to httpd.conf for you. In any case, |
| * after installing the mod_apreq.so module, be sure your webserver's |
| * httpd.conf activates it on startup with a LoadModule directive, e.g. |
| * @code |
| * |
| * LoadModule modules/mod_apreq.so |
| * |
| * @endcode |
| * |
| * The mod_apreq filter is named "APREQ", and may be used in Apache's |
| * input filter directives, e.g. |
| * @code |
| * |
| * AddInputFilter APREQ # or |
| * SetInputFilter APREQ |
| * |
| * @endcode |
| * |
| * However, this is not required because libapreq2 will add the filter (only) |
| * if it's necessary. You just need to ensure that your module instantiates |
| * an apreq_request_t using apreq_request() <em>before the content handler |
| * ultimately reads from the input filter chain</em>. It is important to |
| * recognize that no matter how the input filters are initially arranged, |
| * the APREQ filter will attempt to reposition itself to be the last input filter |
| * to read the data. |
| * |
| * If you want to use other input filters to transform the incoming HTTP |
| * request data, is important to register those filters with Apache |
| * as having type AP_FTYPE_CONTENT_SET or AP_FTYPE_RESOURCE. Due to the |
| * limitations of Apache's current input filter design, types higher than |
| * AP_FTYPE_CONTENT_SET may not work properly whenever the apreq filter is active. |
| * |
| * This is especially true when a content handler uses libapreq2 to parse |
| * some of the post data before doing an internal redirect. Any input filter |
| * subsequently added to the redirected request will bypass the original apreq |
| * filter (and therefore lose access to some of the original post data), unless |
| * its type is less than the type of the apreq filter (currently AP_FTYPE_PROTOCOL-1). |
| * |
| * |
| * <h2>Server Configuration Directives</h2> |
| * |
| * <TABLE class="qref"><CAPTION>Per-directory commands for mod_apreq</CAPTION> |
| * <TR><TH>Directive</TH><TH>Context</TH><TH>Default</TH><TH>Description</TH></TR> |
| * <TR class="odd"><TD>APREQ_MaxBody</TD><TD>directory</TD><TD>-1 (Unlimited)</TD><TD> |
| * Maximum number of bytes mod_apreq will send off to libapreq for parsing. |
| * mod_apreq will log this event and remove itself from the filter chain. |
| * The APR_EGENERAL error will be reported to libapreq2 users via the return |
| * value of apreq_env_read(). |
| * </TD></TR> |
| * <TR><TD>APREQ_MaxBrigade</TD><TD>directory</TD><TD> #APREQ_MAX_BRIGADE_LEN </TD><TD> |
| * Maximum number of bytes apreq will allow to accumulate |
| * within a brigade. Excess data will be spooled to a |
| * file bucket appended to the brigade. |
| * </TD></TR> |
| * <TR class="odd"><TD>APREQ_TempDir</TD><TD>directory</TD><TD>NULL</TD><TD> |
| * Sets the location of the temporary directory apreq will use to spool |
| * overflow brigade data (based on the APREQ_MaxBrigade setting). |
| * If left unset, libapreq2 will select a platform-specific location via apr_temp_dir_get(). |
| * </TD></TR> |
| * </TABLE> |
| * |
| * <h2>Implementation Details</h2> |
| * <pre> |
| * XXX apreq as a normal input filter |
| * XXX apreq as a "virtual" content handler. |
| * XXX apreq as a transparent "tee". |
| * </pre> |
| * @{ |
| */ |
| |
| |
| #define APREQ_MODULE_NAME "APACHE2" |
| #define APREQ_MODULE_MAGIC_NUMBER 20050105 |
| |
| static void apache2_log(const char *file, int line, int level, |
| apr_status_t status, void *env, const char *fmt, |
| va_list vp) |
| { |
| dR; |
| ap_log_rerror(file, line, level, status, r, |
| "%s", apr_pvsprintf(r->pool, fmt, vp)); |
| } |
| |
| |
| static const char *apache2_query_string(void *env) |
| { |
| dR; |
| return r->args; |
| } |
| |
| static apr_pool_t *apache2_pool(void *env) |
| { |
| dR; |
| return r->pool; |
| } |
| |
| static apr_bucket_alloc_t *apache2_bucket_alloc(void *env) |
| { |
| dR; |
| return r->connection->bucket_alloc; |
| } |
| |
| static const char *apache2_header_in(void *env, const char *name) |
| { |
| dR; |
| return apr_table_get(r->headers_in, name); |
| } |
| |
| /* |
| * r->headers_out ~> r->err_headers_out ? |
| * @bug Sending a Set-Cookie header on a 304 |
| * requires err_headers_out table. |
| */ |
| static apr_status_t apache2_header_out(void *env, const char *name, |
| char *value) |
| { |
| dR; |
| apr_table_add(r->err_headers_out, name, value); |
| return APR_SUCCESS; |
| } |
| |
| |
| APR_INLINE |
| static struct env_config *get_cfg(request_rec *r) |
| { |
| struct env_config *cfg = |
| ap_get_module_config(r->request_config, &apreq_module); |
| if (cfg == NULL) { |
| struct dir_config *d = ap_get_module_config(r->per_dir_config, |
| &apreq_module); |
| cfg = apr_pcalloc(r->pool, sizeof *cfg); |
| ap_set_module_config(r->request_config, &apreq_module, cfg); |
| |
| if (d) { |
| cfg->temp_dir = d->temp_dir; |
| cfg->max_body = d->max_body; |
| cfg->max_brigade = d->max_brigade; |
| } |
| else { |
| cfg->max_body = -1; |
| cfg->max_brigade = APREQ_MAX_BRIGADE_LEN; |
| } |
| } |
| return cfg; |
| } |
| |
| static apreq_jar_t *apache2_jar(void *env, apreq_jar_t *jar) |
| { |
| dR; |
| struct env_config *c = get_cfg(r); |
| if (jar != NULL) { |
| apreq_jar_t *old = c->jar; |
| c->jar = jar; |
| return old; |
| } |
| return c->jar; |
| } |
| |
| APR_INLINE |
| static void apreq_filter_relocate(ap_filter_t *f) |
| { |
| request_rec *r = f->r; |
| if (f != r->input_filters) { |
| ap_filter_t *top = r->input_filters; |
| ap_remove_input_filter(f); |
| r->input_filters = f; |
| f->next = top; |
| } |
| } |
| |
| static ap_filter_t *get_apreq_filter(request_rec *r) |
| { |
| struct env_config *cfg = get_cfg(r); |
| |
| if (cfg->f != NULL) |
| return cfg->f; |
| |
| cfg->f = ap_add_input_filter(filter_name, NULL, r, r->connection); |
| |
| /* ap_add_input_filter does not guarantee cfg->f == r->input_filters, |
| * so we reposition the new filter there as necessary. |
| */ |
| |
| apreq_filter_relocate(cfg->f); |
| return cfg->f; |
| } |
| |
| static apreq_request_t *apache2_request(void *env, |
| apreq_request_t *req) |
| { |
| dR; |
| struct env_config *c = get_cfg(r); |
| |
| if (c->f == NULL) |
| get_apreq_filter(r); |
| |
| if (req != NULL) { |
| apreq_request_t *old = c->req; |
| c->req = req; |
| return old; |
| } |
| |
| return c->req; |
| } |
| |
| APR_INLINE |
| static void apreq_filter_make_context(ap_filter_t *f) |
| { |
| request_rec *r = f->r; |
| struct env_config *cfg = get_cfg(r); |
| apreq_request_t *req = cfg->req; |
| struct filter_ctx *ctx; |
| apr_bucket_alloc_t *alloc; |
| |
| if (f == r->input_filters |
| && r->proto_input_filters == f->next |
| && strcasecmp(f->next->frec->name, filter_name) == 0) |
| { |
| /* Try to steal the context and parse data of the |
| upstream apreq filter. */ |
| ctx = f->next->ctx; |
| |
| switch (ctx->status) { |
| case APR_SUCCESS: |
| case APR_INCOMPLETE: |
| break; |
| default: |
| apreq_log(APREQ_DEBUG ctx->status, r, |
| "cannot steal context: bad filter status"); |
| goto make_new_context; |
| } |
| |
| if (ctx->r != r) { |
| /* r is a new request (subrequest or internal redirect) */ |
| apreq_request_t *old_req; |
| |
| if (req != NULL) { |
| if (req->parser != NULL) { |
| apreq_log(APREQ_DEBUG ctx->status, r, |
| "cannot steal context: new parser detected"); |
| goto make_new_context; |
| } |
| } |
| else { |
| req = apreq_request(r, NULL); |
| } |
| |
| /* steal the parser output */ |
| apreq_log(APREQ_DEBUG 0, r, "stealing parser output"); |
| old_req = apreq_request(ctx->r, NULL); |
| req->parser = old_req->parser; |
| req->body = old_req->body; |
| req->body_status = old_req->body_status; |
| ctx->r = r; |
| } |
| |
| /* steal the filter context */ |
| apreq_log(APREQ_DEBUG 0, r, "stealing filter context"); |
| f->ctx = f->next->ctx; |
| r->proto_input_filters = f; |
| ap_remove_input_filter(f->next); |
| return; |
| } |
| |
| make_new_context: |
| if (req != NULL && f == r->input_filters) { |
| if (req->body_status != APR_EINIT) { |
| req->body = NULL; |
| req->parser = NULL; |
| req->body_status = APR_EINIT; |
| } |
| } |
| |
| alloc = r->connection->bucket_alloc; |
| ctx = apr_palloc(r->pool, sizeof *ctx); |
| |
| f->ctx = ctx; |
| ctx->r = r; |
| ctx->bb = apr_brigade_create(r->pool, alloc); |
| ctx->spool = apr_brigade_create(r->pool, alloc); |
| ctx->status = APR_INCOMPLETE; |
| ctx->saw_eos = 0; |
| ctx->bytes_read = 0; |
| |
| if (cfg->max_body >= 0) { |
| const char *cl = apr_table_get(r->headers_in, "Content-Length"); |
| if (cl != NULL) { |
| char *dummy; |
| apr_int64_t content_length = apr_strtoi64(cl,&dummy,0); |
| |
| if (dummy == NULL || *dummy != 0) { |
| apreq_log(APREQ_ERROR APR_EGENERAL, r, |
| "Invalid Content-Length header (%s)", cl); |
| ctx->status = APR_EGENERAL; |
| apreq_request(r, NULL)->body_status = APR_EGENERAL; |
| } |
| else if (content_length > (apr_int64_t)cfg->max_body) { |
| apreq_log(APREQ_ERROR APR_EGENERAL, r, |
| "Content-Length header (%s) exceeds configured " |
| "max_body limit (%" APR_OFF_T_FMT ")", |
| cl, cfg->max_body); |
| ctx->status = APR_EGENERAL; |
| apreq_request(r, NULL)->body_status = APR_EGENERAL; |
| } |
| } |
| } |
| } |
| |
| /* |
| * Reads data directly into the parser. |
| */ |
| |
| static apr_status_t apache2_read(void *env, |
| apr_read_type_e block, |
| apr_off_t bytes) |
| { |
| dR; |
| ap_filter_t *f = get_apreq_filter(r); /*ensures correct filter for prefetch */ |
| struct filter_ctx *ctx; |
| apr_status_t s; |
| |
| if (f->ctx == NULL) |
| apreq_filter_make_context(f); |
| ctx = f->ctx; |
| if (ctx->status != APR_INCOMPLETE || bytes == 0) |
| return ctx->status; |
| |
| apreq_log(APREQ_DEBUG 0, r, "prefetching %" APR_OFF_T_FMT " bytes", bytes); |
| s = ap_get_brigade(f, NULL, AP_MODE_READBYTES, block, bytes); |
| if (s != APR_SUCCESS) |
| return s; |
| return ctx->status; |
| } |
| |
| |
| static const char *apache2_temp_dir(void *env, const char *path) |
| { |
| dR; |
| struct env_config *c = get_cfg(r); |
| |
| if (path != NULL) { |
| const char *rv = c->temp_dir; |
| c->temp_dir = apr_pstrdup(r->pool, path); |
| return rv; |
| } |
| if (c->temp_dir == NULL) { |
| if (apr_temp_dir_get(&c->temp_dir, r->pool) != APR_SUCCESS) |
| c->temp_dir = NULL; |
| } |
| |
| return c->temp_dir; |
| } |
| |
| |
| static apr_off_t apache2_max_body(void *env, apr_off_t bytes) |
| { |
| dR; |
| struct env_config *c = get_cfg(r); |
| |
| if (bytes >= 0) { |
| apr_off_t rv = c->max_body; |
| c->max_body = bytes; |
| return rv; |
| } |
| return c->max_body; |
| } |
| |
| |
| static apr_ssize_t apache2_max_brigade(void *env, apr_ssize_t bytes) |
| { |
| dR; |
| struct env_config *c = get_cfg(r); |
| |
| if (bytes >= 0) { |
| apr_ssize_t rv = c->max_brigade; |
| c->max_brigade = bytes; |
| return rv; |
| } |
| return c->max_brigade; |
| } |
| |
| |
| /* |
| * Situations to contend with: |
| * |
| * 1) Often the filter will be added by the content handler itself, |
| * so the apreq_filter_init hook will not be run. |
| * 2) If an auth handler uses apreq, the apreq_filter will ensure |
| * it's part of the protocol filters. apreq_filter_init does NOT need |
| * to notify the protocol filter that it must not continue parsing, |
| * the apreq filter can perform this check itself. apreq_filter_init |
| * just needs to ensure cfg->f does not point at it. |
| * 3) If req->proto_input_filters and req->input_filters are apreq |
| * filters, and req->input_filters->next == req->proto_input_filters, |
| * it is safe for apreq_filter to "steal" the proto filter's context |
| * and subsequently drop it from the chain. |
| */ |
| |
| |
| /* Examines the input_filter chain and moves the apreq filter(s) around |
| * before the filter chain is stacked by ap_get_brigade. |
| */ |
| |
| static apr_status_t apreq_filter_init(ap_filter_t *f) |
| { |
| request_rec *r = f->r; |
| struct env_config *cfg = get_cfg(r); |
| ap_filter_t *in; |
| |
| if (f != r->proto_input_filters) { |
| if (f == r->input_filters) { |
| cfg->f = f; |
| return APR_SUCCESS; |
| } |
| for (in = r->input_filters; in != r->proto_input_filters; |
| in = in->next) |
| { |
| if (f == in) { |
| if (strcasecmp(r->input_filters->frec->name, filter_name) == 0) { |
| apreq_log(APREQ_DEBUG 0, r, |
| "removing intermediate apreq filter"); |
| if (cfg->f == f) |
| cfg->f = r->input_filters; |
| ap_remove_input_filter(f); |
| } |
| else { |
| apreq_log(APREQ_DEBUG 0, r, |
| "relocating intermediate apreq filter"); |
| apreq_filter_relocate(f); |
| cfg->f = f; |
| } |
| return APR_SUCCESS; |
| } |
| } |
| } |
| |
| /* else this is a protocol filter which may still be active. |
| * if it is, we must deregister it now. |
| */ |
| if (cfg->f == f) { |
| apreq_log(APREQ_DEBUG 0, r, "disabling stale protocol filter"); |
| cfg->f = NULL; |
| } |
| return APR_SUCCESS; |
| } |
| |
| static apr_status_t apreq_filter(ap_filter_t *f, |
| apr_bucket_brigade *bb, |
| ap_input_mode_t mode, |
| apr_read_type_e block, |
| apr_off_t readbytes) |
| { |
| request_rec *r = f->r; |
| struct filter_ctx *ctx; |
| struct env_config *cfg; |
| apreq_request_t *req; |
| apr_status_t rv; |
| |
| switch (mode) { |
| case AP_MODE_READBYTES: |
| case AP_MODE_EXHAUSTIVE: |
| /* only the modes above are supported */ |
| break; |
| case AP_MODE_GETLINE: /* punt- chunks are b0rked in ap_http_filter */ |
| return ap_get_brigade(f->next, bb, mode, block, readbytes); |
| default: |
| return APR_ENOTIMPL; |
| } |
| |
| cfg = get_cfg(r); |
| req = cfg->req; |
| |
| if (f->ctx == NULL) |
| apreq_filter_make_context(f); |
| |
| ctx = f->ctx; |
| |
| if (cfg->f != f) |
| ctx->status = APR_SUCCESS; |
| |
| if (bb != NULL) { |
| |
| if (!ctx->saw_eos) { |
| |
| if (ctx->status == APR_INCOMPLETE) { |
| apr_off_t len; |
| rv = ap_get_brigade(f->next, bb, mode, block, readbytes); |
| |
| if (rv != APR_SUCCESS) { |
| apreq_log(APREQ_ERROR rv, r, "ap_get_brigade failed"); |
| return rv; |
| } |
| |
| APREQ_BRIGADE_COPY(ctx->bb, bb); |
| apr_brigade_length(bb, 1, &len); |
| ctx->bytes_read += len; |
| |
| if (cfg->max_body >= 0 && ctx->bytes_read > cfg->max_body) { |
| ctx->status = APR_EGENERAL; |
| apreq_request(r, NULL)->body_status = APR_EGENERAL; |
| apreq_log(APREQ_ERROR ctx->status, r, "Bytes read (" APR_OFF_T_FMT |
| ") exceeds configured max_body limit (" APR_OFF_T_FMT ")", |
| ctx->bytes_read, cfg->max_body); |
| } |
| |
| } |
| if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(ctx->bb))) |
| ctx->saw_eos = 1; |
| } |
| |
| if (!APR_BRIGADE_EMPTY(ctx->spool)) { |
| APR_BRIGADE_PREPEND(bb, ctx->spool); |
| if (mode == AP_MODE_READBYTES) { |
| apr_bucket *e; |
| rv = apr_brigade_partition(bb, readbytes, &e); |
| if (rv != APR_SUCCESS && rv != APR_INCOMPLETE) { |
| apreq_log(APREQ_ERROR rv, r, "partition failed"); |
| return rv; |
| } |
| if (APR_BUCKET_IS_EOS(e)) |
| e = APR_BUCKET_NEXT(e); |
| ctx->spool = apr_brigade_split(bb, e); |
| APREQ_BRIGADE_SETASIDE(ctx->spool,r->pool); |
| } |
| } |
| |
| if (ctx->status != APR_INCOMPLETE) { |
| if (APR_BRIGADE_EMPTY(ctx->spool)) { |
| ap_filter_t *next = f->next; |
| |
| if (cfg->f != f) { |
| apreq_log(APREQ_DEBUG ctx->status, r, |
| "removing inactive filter (%d)", |
| r->input_filters == f); |
| |
| ap_remove_input_filter(f); |
| } |
| if (APR_BRIGADE_EMPTY(bb)) |
| return ap_get_brigade(next, bb, mode, block, readbytes); |
| } |
| return APR_SUCCESS; |
| } |
| } |
| else if (!ctx->saw_eos) { |
| /* bb == NULL, so this is a prefetch read! */ |
| apr_off_t total_read = 0; |
| |
| bb = apr_brigade_create(ctx->bb->p, ctx->bb->bucket_alloc); |
| |
| while (total_read < readbytes) { |
| apr_off_t len; |
| apr_bucket *last = APR_BRIGADE_LAST(ctx->spool); |
| |
| if (APR_BUCKET_IS_EOS(last)) { |
| ctx->saw_eos = 1; |
| break; |
| } |
| |
| rv = ap_get_brigade(f->next, bb, mode, block, readbytes); |
| if (rv != APR_SUCCESS) { |
| apreq_log(APREQ_ERROR rv, r, "ap_get_brigade failed"); |
| return rv; |
| } |
| APREQ_BRIGADE_SETASIDE(bb, r->pool); |
| APREQ_BRIGADE_COPY(ctx->bb, bb); |
| |
| apr_brigade_length(bb, 1, &len); |
| total_read += len; |
| |
| rv = apreq_brigade_concat(r, ctx->spool, bb); |
| if (rv != APR_SUCCESS && rv != APR_EOF) { |
| apreq_log(APREQ_ERROR rv, r, |
| "apreq_brigade_concat failed; APREQ_TempDir problem?"); |
| return rv; |
| } |
| } |
| |
| ctx->bytes_read += total_read; |
| |
| if (cfg->max_body >= 0 && ctx->bytes_read > cfg->max_body) { |
| ctx->status = APR_EGENERAL; |
| apreq_request(r, NULL)->body_status = APR_EGENERAL; |
| apreq_log(APREQ_ERROR ctx->status, r, "Bytes read (%" APR_OFF_T_FMT |
| ") exceeds configured max_body limit (%" APR_OFF_T_FMT ")", |
| ctx->bytes_read, cfg->max_body); |
| } |
| |
| /* Adding "f" to the protocol filter chain ensures the |
| * spooled data is preserved across internal redirects. |
| */ |
| if (f != r->proto_input_filters) { |
| ap_filter_t *in; |
| for (in = r->input_filters; in != r->proto_input_filters; |
| in = in->next) |
| { |
| if (f == in) { |
| r->proto_input_filters = f; |
| break; |
| } |
| } |
| } |
| } |
| else |
| return APR_SUCCESS; |
| |
| if (ctx->status == APR_INCOMPLETE) { |
| if (req == NULL) |
| req = apreq_request(r, NULL); |
| |
| ctx->status = apreq_parse_request(req, ctx->bb); |
| apr_brigade_cleanup(ctx->bb); |
| } |
| |
| return APR_SUCCESS; |
| } |
| |
| static APREQ_ENV_MODULE(apache2, APREQ_MODULE_NAME, |
| APREQ_MODULE_MAGIC_NUMBER); |
| |
| static void register_hooks (apr_pool_t *p) |
| { |
| const apreq_env_t *old_env; |
| old_env = apreq_env_module(&apache2_module); |
| ap_register_input_filter(filter_name, apreq_filter, apreq_filter_init, |
| AP_FTYPE_PROTOCOL-1); |
| } |
| |
| |
| /* Configuration directives */ |
| |
| |
| static const char *apreq_set_temp_dir(cmd_parms *cmd, void *data, |
| const char *arg) |
| { |
| struct dir_config *conf = data; |
| const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); |
| |
| if (err != NULL) |
| return err; |
| |
| conf->temp_dir = arg; |
| return NULL; |
| } |
| |
| static const char *apreq_set_max_body(cmd_parms *cmd, void *data, |
| const char *arg) |
| { |
| struct dir_config *conf = data; |
| const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); |
| |
| if (err != NULL) |
| return err; |
| |
| conf->max_body = apreq_atoi64f(arg); |
| |
| if (conf->max_body < 0) |
| return "APREQ_MaxBody requires a non-negative integer."; |
| |
| return NULL; |
| } |
| |
| static const char *apreq_set_max_brigade(cmd_parms *cmd, void *data, |
| const char *arg) |
| { |
| struct dir_config *conf = data; |
| const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); |
| |
| if (err != NULL) |
| return err; |
| |
| conf->max_brigade = apreq_atoi64f(arg); |
| |
| if (conf->max_brigade < 0) |
| return "APREQ_MaxBrigade requires a non-negative integer."; |
| |
| return NULL; |
| } |
| |
| static const command_rec apreq_cmds[] = |
| { |
| AP_INIT_TAKE1("APREQ_TempDir", apreq_set_temp_dir, NULL, OR_ALL, |
| "Default location of temporary directory"), |
| AP_INIT_TAKE1("APREQ_MaxBody", apreq_set_max_body, NULL, OR_ALL, |
| "Maximum amount of data that will be fed into a parser."), |
| AP_INIT_TAKE1("APREQ_MaxBrigade", apreq_set_max_brigade, NULL, OR_ALL, |
| "Maximum in-memory bytes a brigade may use."), |
| {NULL} |
| }; |
| |
| /** @} */ |
| |
| module AP_MODULE_DECLARE_DATA apreq_module = |
| { |
| STANDARD20_MODULE_STUFF, |
| apreq_create_dir_config, |
| apreq_merge_dir_config, |
| NULL, |
| NULL, |
| apreq_cmds, |
| register_hooks, |
| }; |