--- /dev/null
+/*
+** 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.
+*/
+
+#include "assert.h"
+
+#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_module_apache2.h"
+#include "apreq_private_apache2.h"
+#include "apreq_error.h"
+#include "apreq_util.h"
+#include "apreq_version.h"
+
+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->read_limit = APREQ_DEFAULT_READ_LIMIT;
+ dc->brigade_limit = APREQ_DEFAULT_BRIGADE_LIMIT;
+ 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) /* overrides ok */
+ ? b->temp_dir : a->temp_dir;
+
+ c->brigade_limit = (b->brigade_limit == (apr_size_t)-1) /* overrides ok */
+ ? a->brigade_limit : b->brigade_limit;
+
+ c->read_limit = (b->read_limit < a->read_limit) /* why min? */
+ ? b->read_limit : a->read_limit;
+
+ return c;
+}
+
+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_read_limit(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->read_limit = apreq_atoi64f(arg);
+ return NULL;
+}
+
+static const char *apreq_set_brigade_limit(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->brigade_limit = apreq_atoi64f(arg);
+ return NULL;
+}
+
+
+static const command_rec apreq_cmds[] =
+{
+ AP_INIT_TAKE1("APREQ2_TempDir", apreq_set_temp_dir, NULL, OR_ALL,
+ "Default location of temporary directory"),
+ AP_INIT_TAKE1("APREQ2_ReadLimit", apreq_set_read_limit, NULL, OR_ALL,
+ "Maximum amount of data that will be fed into a parser."),
+ AP_INIT_TAKE1("APREQ2_BrigadeLimit", apreq_set_brigade_limit, NULL, OR_ALL,
+ "Maximum in-memory bytes a brigade may use."),
+ { NULL }
+};
+
+
+void apreq_filter_init_context(ap_filter_t *f)
+{
+ request_rec *r = f->r;
+ struct filter_ctx *ctx = f->ctx;
+ apr_bucket_alloc_t *ba = r->connection->bucket_alloc;
+ const char *cl_header;
+
+ if (r->method_number == M_GET) {
+ /* Don't parse GET (this protects against subrequest body parsing). */
+ ctx->body_status = APREQ_ERROR_NODATA;
+ return;
+ }
+
+ cl_header = apr_table_get(r->headers_in, "Content-Length");
+
+ if (cl_header != NULL) {
+ char *dummy;
+ apr_uint64_t content_length = apr_strtoi64(cl_header,&dummy,0);
+
+ if (dummy == NULL || *dummy != 0) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r,
+ "Invalid Content-Length header (%s)", cl_header);
+ ctx->body_status = APREQ_ERROR_BADHEADER;
+ return;
+ }
+ else if (content_length > ctx->read_limit) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r,
+ "Content-Length header (%s) exceeds configured "
+ "max_body limit (%" APR_UINT64_T_FMT ")",
+ cl_header, ctx->read_limit);
+ ctx->body_status = APREQ_ERROR_OVERLIMIT;
+ return;
+ }
+ }
+
+ if (ctx->parser == NULL) {
+ const char *ct_header = apr_table_get(r->headers_in, "Content-Type");
+
+ if (ct_header != NULL) {
+ apreq_parser_function_t pf = apreq_parser(ct_header);
+
+ if (pf != NULL) {
+ ctx->parser = apreq_parser_make(r->pool, ba, ct_header, pf,
+ ctx->brigade_limit,
+ ctx->temp_dir,
+ ctx->hook_queue,
+ NULL);
+ }
+ else {
+ ctx->body_status = APREQ_ERROR_NOPARSER;
+ return;
+ }
+ }
+ else {
+ ctx->body_status = APREQ_ERROR_NOHEADER;
+ return;
+ }
+ }
+ else {
+ if (ctx->parser->brigade_limit > ctx->brigade_limit)
+ ctx->parser->brigade_limit = ctx->brigade_limit;
+ if (ctx->temp_dir != NULL)
+ ctx->parser->temp_dir = ctx->temp_dir;
+ if (ctx->hook_queue != NULL)
+ apreq_parser_add_hook(ctx->parser, ctx->hook_queue);
+ }
+
+ ctx->hook_queue = NULL;
+ ctx->bb = apr_brigade_create(r->pool, ba);
+ ctx->bbtmp = apr_brigade_create(r->pool, ba);
+ ctx->spool = apr_brigade_create(r->pool, ba);
+ ctx->body = apr_table_make(r->pool, APREQ_DEFAULT_NELTS);
+ ctx->body_status = APR_INCOMPLETE;
+}
+
+
+/*
+ * 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 filter_ctx *ctx = f->ctx;
+ struct apache2_handle *handle =
+ (struct apache2_handle *)apreq_handle_apache2(r);
+
+ /* Don't parse GET (this protects against subrequest body parsing). */
+ if (f->r->method_number == M_GET)
+ return APR_SUCCESS;
+
+ if (ctx == NULL || ctx->body_status == APR_EINIT) {
+ if (f == r->input_filters) {
+ handle->f = f;
+ }
+ else if (r->input_filters->frec->filter_func.in_func == apreq_filter) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
+ "removing intermediate apreq filter");
+ if (handle->f == f)
+ handle->f = r->input_filters;
+ ap_remove_input_filter(f);
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
+ "relocating intermediate apreq filter");
+ apreq_filter_relocate(f);
+ handle->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 (handle->f == f) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
+ "disabling stale protocol filter");
+ if (ctx->body_status == APR_INCOMPLETE)
+ ctx->body_status = APREQ_ERROR_INTERRUPT;
+ handle->f = NULL;
+ }
+ return APR_SUCCESS;
+}
+
+
+
+apr_status_t apreq_filter_prefetch(ap_filter_t *f, apr_off_t readbytes)
+{
+ struct filter_ctx *ctx = f->ctx;
+ request_rec *r = f->r;
+ apr_status_t rv;
+ apr_off_t len;
+
+ if (ctx->body_status == APR_EINIT)
+ apreq_filter_init_context(f);
+
+ if (ctx->body_status != APR_INCOMPLETE || readbytes == 0)
+ return ctx->body_status;
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
+ "prefetching %" APR_OFF_T_FMT " bytes", readbytes);
+
+ rv = ap_get_brigade(f->next, ctx->bb, AP_MODE_READBYTES,
+ APR_BLOCK_READ, readbytes);
+
+ if (rv != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+ "ap_get_brigade failed during prefetch");
+ ctx->filter_error = rv;
+ return ctx->body_status = APREQ_ERROR_GENERAL;
+ }
+
+ apreq_brigade_setaside(ctx->bb, r->pool);
+ apreq_brigade_copy(ctx->bbtmp, ctx->bb);
+
+ rv = apreq_brigade_concat(r->pool, ctx->temp_dir, ctx->brigade_limit,
+ ctx->spool, ctx->bbtmp);
+ if (rv != APR_SUCCESS && rv != APR_EOF) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+ "apreq_brigade_concat failed; TempDir problem?");
+ ctx->filter_error = APR_EGENERAL;
+ return ctx->body_status = rv;
+ }
+
+ /* 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;
+ }
+ }
+ }
+
+ apr_brigade_length(ctx->bb, 1, &len);
+ ctx->bytes_read += len;
+
+ if (ctx->bytes_read > ctx->read_limit) {
+ ctx->body_status = APREQ_ERROR_OVERLIMIT;
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, ctx->body_status, r,
+ "Bytes read (%" APR_UINT64_T_FMT
+ ") exceeds configured read limit (%" APR_UINT64_T_FMT ")",
+ ctx->bytes_read, ctx->read_limit);
+ return ctx->body_status;
+ }
+
+ ctx->body_status = apreq_parser_run(ctx->parser, ctx->body, ctx->bb);
+ apr_brigade_cleanup(ctx->bb);
+
+ return ctx->body_status;
+}
+
+
+
+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;
+ apr_status_t rv;
+ apr_off_t len;
+
+ switch (mode) {
+ case AP_MODE_READBYTES:
+ /* only the modes above are supported */
+ break;
+
+ case AP_MODE_EXHAUSTIVE: /* not worth supporting at this level */
+ case AP_MODE_GETLINE: /* chunked trailers are b0rked in ap_http_filter */
+ return ap_get_brigade(f->next, bb, mode, block, readbytes);
+
+ default:
+ return APR_ENOTIMPL;
+ }
+
+ if (f->ctx == NULL)
+ apreq_filter_make_context(f);
+
+ ctx = f->ctx;
+
+ if (ctx->body_status == APR_EINIT)
+ apreq_filter_init_context(f);
+
+ if (ctx->spool && !APR_BRIGADE_EMPTY(ctx->spool)) {
+ apr_bucket *e;
+ rv = apr_brigade_partition(ctx->spool, readbytes, &e);
+ if (rv != APR_SUCCESS && rv != APR_INCOMPLETE)
+ return rv;
+
+ if (APR_BUCKET_IS_EOS(e))
+ e = APR_BUCKET_NEXT(e);
+
+ apreq_brigade_move(bb, ctx->spool, e);
+ return APR_SUCCESS;
+ }
+ else if (ctx->body_status != APR_INCOMPLETE) {
+ if (ctx->filter_error)
+ return ctx->filter_error;
+
+ rv = ap_get_brigade(f->next, bb, mode, block, readbytes);
+ ap_remove_input_filter(f);
+ return rv;
+ }
+
+
+ rv = ap_get_brigade(f->next, bb, mode, block, readbytes);
+ if (rv != APR_SUCCESS)
+ return rv;
+
+ apreq_brigade_copy(ctx->bb, bb);
+ apr_brigade_length(bb, 1, &len);
+ ctx->bytes_read += len;
+
+ if (ctx->bytes_read > ctx->read_limit) {
+ ctx->body_status = APREQ_ERROR_OVERLIMIT;
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, ctx->body_status, r,
+ "Bytes read (%" APR_UINT64_T_FMT
+ ") exceeds configured max_body limit (%"
+ APR_UINT64_T_FMT ")",
+ ctx->bytes_read, ctx->read_limit);
+ }
+ else {
+ ctx->body_status = apreq_parser_run(ctx->parser, ctx->body, ctx->bb);
+ apr_brigade_cleanup(ctx->bb);
+ }
+ return APR_SUCCESS;
+}
+
+
+static int apreq_pre_init(apr_pool_t *p, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *base_server)
+{
+ apr_status_t status;
+
+ status = apreq_pre_initialize(p);
+ if (status != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_ERR, status, base_server,
+ "Failed to pre-initialize libapreq2");
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ return OK;
+}
+
+static int apreq_post_init(apr_pool_t *p, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *base_server)
+{
+ apr_status_t status;
+
+ ap_add_version_component(p, apr_psprintf(p,
+ "mod_apreq2-%d/%s",
+ APREQ_APACHE2_MMN,
+ apreq_version_string()));
+
+ status = apreq_post_initialize(p);
+ if (status != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_ERR, status, base_server,
+ "Failed to post-initialize libapreq2");
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ return OK;
+}
+
+static void register_hooks (apr_pool_t *p)
+{
+ /* APR_HOOK_FIRST because we want other modules to be able to
+ * register parsers in their post_config hook via APR_HOOK_MIDDLE.
+ */
+ ap_hook_post_config(apreq_pre_init, NULL, NULL, APR_HOOK_FIRST);
+
+ /* APR_HOOK_LAST because we need to lock the default_parsers hash
+ * (to prevent further modifications) before the server forks.
+ */
+ ap_hook_post_config(apreq_post_init, NULL, NULL, APR_HOOK_LAST);
+
+ ap_register_input_filter(APREQ_FILTER_NAME, apreq_filter, apreq_filter_init,
+ AP_FTYPE_PROTOCOL-1);
+}
+
+
+
+/** @} */
+
+
+module AP_MODULE_DECLARE_DATA apreq_module = {
+#line __LINE__ "mod_apreq2.c"
+ STANDARD20_MODULE_STUFF,
+ apreq_create_dir_config,
+ apreq_merge_dir_config,
+ NULL,
+ NULL,
+ apreq_cmds,
+ register_hooks,
+};
+
+
+void apreq_filter_make_context(ap_filter_t *f)
+{
+ request_rec *r;
+ struct filter_ctx *ctx;
+ struct dir_config *d;
+
+ r = f->r;
+ d = ap_get_module_config(r->per_dir_config, &apreq_module);
+
+ if (f == r->input_filters
+ && r->proto_input_filters == f->next
+ && f->next->frec->filter_func.in_func == apreq_filter
+ && f->r->method_number != M_GET)
+ {
+
+ ctx = f->next->ctx;
+
+ switch (ctx->body_status) {
+
+ case APREQ_ERROR_INTERRUPT:
+ ctx->body_status = APR_INCOMPLETE;
+ /* fall thru */
+
+ case APR_SUCCESS:
+
+ if (d != NULL) {
+ ctx->temp_dir = d->temp_dir;
+ ctx->read_limit = d->read_limit;
+ ctx->brigade_limit = d->brigade_limit;
+
+ if (ctx->parser != NULL) {
+ ctx->parser->temp_dir = d->temp_dir;
+ ctx->parser->brigade_limit = d->brigade_limit;
+ }
+
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
+ "stealing filter context");
+ f->ctx = ctx;
+ r->proto_input_filters = f;
+ ap_remove_input_filter(f->next);
+
+ return;
+
+ default:
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, ctx->body_status, r,
+ "cannot steal context: bad filter status");
+ }
+ }
+
+ ctx = apr_pcalloc(r->pool, sizeof *ctx);
+ ctx->body_status = APR_EINIT;
+
+ if (d == NULL) {
+ ctx->read_limit = (apr_uint64_t)-1;
+ ctx->brigade_limit = APREQ_DEFAULT_BRIGADE_LIMIT;
+ } else {
+ ctx->temp_dir = d->temp_dir;
+ ctx->read_limit = d->read_limit;
+ ctx->brigade_limit = d->brigade_limit;
+ }
+
+ f->ctx = ctx;
+}
+
--- /dev/null
+/*
+** 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.
+*/
+
+#include "assert.h"
+
+#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_module_apache2.h"
+#include "apreq_private_apache2.h"
+#include "apreq_error.h"
+
+
+APR_INLINE
+static ap_filter_t *get_apreq_filter(apreq_handle_t *handle)
+{
+ struct apache2_handle *req = (struct apache2_handle *)handle;
+
+ if (req->f == NULL) {
+ req->f = ap_add_input_filter(APREQ_FILTER_NAME, NULL,
+ req->r,
+ req->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(req->f);
+ }
+
+ return req->f;
+}
+
+
+static apr_status_t apache2_jar(apreq_handle_t *handle, const apr_table_t **t)
+{
+ struct apache2_handle *req = (struct apache2_handle*)handle;
+ request_rec *r = req->r;
+
+ if (req->jar_status == APR_EINIT) {
+ const char *cookies = apr_table_get(r->headers_in, "Cookie");
+ if (cookies != NULL) {
+ req->jar = apr_table_make(handle->pool, APREQ_DEFAULT_NELTS);
+ req->jar_status =
+ apreq_parse_cookie_header(handle->pool, req->jar, cookies);
+ }
+ else
+ req->jar_status = APREQ_ERROR_NODATA;
+ }
+
+ *t = req->jar;
+ return req->jar_status;
+}
+
+static apr_status_t apache2_args(apreq_handle_t *handle, const apr_table_t **t)
+{
+ struct apache2_handle *req = (struct apache2_handle*)handle;
+ request_rec *r = req->r;
+
+ if (req->args_status == APR_EINIT) {
+ if (r->args != NULL) {
+ req->args = apr_table_make(handle->pool, APREQ_DEFAULT_NELTS);
+ req->args_status =
+ apreq_parse_query_string(handle->pool, req->args, r->args);
+ }
+ else
+ req->args_status = APREQ_ERROR_NODATA;
+ }
+
+ *t = req->args;
+ return req->args_status;
+}
+
+
+
+
+static apreq_cookie_t *apache2_jar_get(apreq_handle_t *handle, const char *name)
+{
+ struct apache2_handle *req = (struct apache2_handle *)handle;
+ const apr_table_t *t;
+ const char *val;
+
+ if (req->jar_status == APR_EINIT)
+ apache2_jar(handle, &t);
+ else
+ t = req->jar;
+
+ if (t == NULL)
+ return NULL;
+
+ val = apr_table_get(t, name);
+ if (val == NULL)
+ return NULL;
+
+ return apreq_value_to_cookie(val);
+}
+
+static apreq_param_t *apache2_args_get(apreq_handle_t *handle, const char *name)
+{
+ struct apache2_handle *req = (struct apache2_handle *)handle;
+ const apr_table_t *t;
+ const char *val;
+
+ if (req->args_status == APR_EINIT)
+ apache2_args(handle, &t);
+ else
+ t = req->args;
+
+ if (t == NULL)
+ return NULL;
+
+ val = apr_table_get(t, name);
+ if (val == NULL)
+ return NULL;
+
+ return apreq_value_to_param(val);
+}
+
+
+static apr_status_t apache2_body(apreq_handle_t *handle, const apr_table_t **t)
+{
+ ap_filter_t *f = get_apreq_filter(handle);
+ struct filter_ctx *ctx;
+
+ if (f->ctx == NULL)
+ apreq_filter_make_context(f);
+
+ ctx = f->ctx;
+
+ switch (ctx->body_status) {
+
+ case APR_EINIT:
+ apreq_filter_init_context(f);
+ if (ctx->body_status != APR_INCOMPLETE)
+ break;
+
+ case APR_INCOMPLETE:
+ while (apreq_filter_prefetch(f, APREQ_DEFAULT_READ_BLOCK_SIZE) == APR_INCOMPLETE)
+ ; /*loop*/
+ }
+
+ *t = ctx->body;
+ return ctx->body_status;
+}
+
+static apreq_param_t *apache2_body_get(apreq_handle_t *handle, const char *name)
+{
+ ap_filter_t *f = get_apreq_filter(handle);
+ struct filter_ctx *ctx;
+ const char *val;
+ apreq_hook_t *h;
+
+ if (f->ctx == NULL)
+ apreq_filter_make_context(f);
+
+ ctx = f->ctx;
+
+ switch (ctx->body_status) {
+
+ case APR_SUCCESS:
+
+ val = apr_table_get(ctx->body, name);
+ if (val != NULL)
+ return apreq_value_to_param(val);
+ return NULL;
+
+
+ case APR_EINIT:
+
+ apreq_filter_init_context(f);
+ if (ctx->body_status != APR_INCOMPLETE)
+ return NULL;
+ apreq_filter_prefetch(f, APREQ_DEFAULT_READ_BLOCK_SIZE);
+
+
+ case APR_INCOMPLETE:
+
+ val = apr_table_get(ctx->body, name);
+ if (val != NULL)
+ return apreq_value_to_param(val);
+
+ /* Not seen yet, so we need to scan for
+ param while prefetching the body */
+
+ if (ctx->find_param == NULL)
+ ctx->find_param = apreq_hook_make(handle->pool,
+ apreq_hook_find_param,
+ NULL, NULL);
+ h = ctx->find_param;
+ h->next = ctx->parser->hook;
+ ctx->parser->hook = h;
+ h->ctx = (void *)name;
+
+ do {
+ apreq_filter_prefetch(f, APREQ_DEFAULT_READ_BLOCK_SIZE);
+ if (h->ctx != name) {
+ ctx->parser->hook = h->next;
+ return h->ctx;
+ }
+ } while (ctx->body_status == APR_INCOMPLETE);
+
+ ctx->parser->hook = h->next;
+ return NULL;
+
+
+ default:
+
+ if (ctx->body == NULL)
+ return NULL;
+
+ val = apr_table_get(ctx->body, name);
+ if (val != NULL)
+ return apreq_value_to_param(val);
+ return NULL;
+
+ }
+
+ /* not reached */
+ return NULL;
+}
+
+static
+apr_status_t apache2_parser_get(apreq_handle_t *handle,
+ const apreq_parser_t **parser)
+{
+ ap_filter_t *f = get_apreq_filter(handle);
+ struct filter_ctx *ctx = f->ctx;
+
+ if (ctx == NULL) {
+ *parser = NULL;
+ return APR_EINIT;
+ }
+ *parser = ctx->parser;
+ return APR_SUCCESS;
+}
+
+static
+apr_status_t apache2_parser_set(apreq_handle_t *handle,
+ apreq_parser_t *parser)
+{
+ ap_filter_t *f = get_apreq_filter(handle);
+ struct filter_ctx *ctx;
+
+ if (f->ctx == NULL)
+ apreq_filter_make_context(f);
+
+ ctx = f->ctx;
+
+ if (ctx->parser == NULL) {
+ ctx->parser = parser;
+ return APR_SUCCESS;
+ }
+ else
+ return APREQ_ERROR_NOTEMPTY;
+}
+
+
+
+static
+apr_status_t apache2_hook_add(apreq_handle_t *handle,
+ apreq_hook_t *hook)
+{
+ ap_filter_t *f = get_apreq_filter(handle);
+ struct filter_ctx *ctx;
+
+ if (f->ctx == NULL)
+ apreq_filter_make_context(f);
+
+ ctx = f->ctx;
+
+ if (ctx->parser != NULL) {
+ return apreq_parser_add_hook(ctx->parser, hook);
+ }
+ else if (ctx->hook_queue != NULL) {
+ apreq_hook_t *h = ctx->hook_queue;
+ while (h->next != NULL)
+ h = h->next;
+ h->next = hook;
+ }
+ else {
+ ctx->hook_queue = hook;
+ }
+ return APR_SUCCESS;
+
+}
+
+static
+apr_status_t apache2_brigade_limit_set(apreq_handle_t *handle,
+ apr_size_t bytes)
+{
+ ap_filter_t *f = get_apreq_filter(handle);
+ struct filter_ctx *ctx;
+
+ if (f->ctx == NULL)
+ apreq_filter_make_context(f);
+
+ ctx = f->ctx;
+
+ if (ctx->body_status == APR_EINIT || ctx->brigade_limit > bytes) {
+ ctx->brigade_limit = bytes;
+ return APR_SUCCESS;
+ }
+
+ return APREQ_ERROR_MISMATCH;
+}
+
+static
+apr_status_t apache2_brigade_limit_get(apreq_handle_t *handle,
+ apr_size_t *bytes)
+{
+ ap_filter_t *f = get_apreq_filter(handle);
+ struct filter_ctx *ctx;
+
+ if (f->ctx == NULL)
+ apreq_filter_make_context(f);
+
+ ctx = f->ctx;
+ *bytes = ctx->brigade_limit;
+ return APR_SUCCESS;
+}
+
+static
+apr_status_t apache2_read_limit_set(apreq_handle_t *handle,
+ apr_uint64_t bytes)
+{
+ ap_filter_t *f = get_apreq_filter(handle);
+ struct filter_ctx *ctx;
+
+ if (f->ctx == NULL)
+ apreq_filter_make_context(f);
+
+ ctx = f->ctx;
+
+ if (ctx->read_limit > bytes && ctx->bytes_read < bytes) {
+ ctx->read_limit = bytes;
+ return APR_SUCCESS;
+ }
+
+ return APREQ_ERROR_MISMATCH;
+}
+
+static
+apr_status_t apache2_read_limit_get(apreq_handle_t *handle,
+ apr_uint64_t *bytes)
+{
+ ap_filter_t *f = get_apreq_filter(handle);
+ struct filter_ctx *ctx;
+
+ if (f->ctx == NULL)
+ apreq_filter_make_context(f);
+
+ ctx = f->ctx;
+ *bytes = ctx->read_limit;
+ return APR_SUCCESS;
+}
+
+static
+apr_status_t apache2_temp_dir_set(apreq_handle_t *handle,
+ const char *path)
+{
+ ap_filter_t *f = get_apreq_filter(handle);
+ struct filter_ctx *ctx;
+
+ if (f->ctx == NULL)
+ apreq_filter_make_context(f);
+
+ ctx = f->ctx;
+ // init vs incomplete state?
+ if (ctx->temp_dir == NULL && ctx->bytes_read == 0) {
+ if (path != NULL)
+ ctx->temp_dir = apr_pstrdup(handle->pool, path);
+ return APR_SUCCESS;
+ }
+
+ return APREQ_ERROR_NOTEMPTY;
+}
+
+static
+apr_status_t apache2_temp_dir_get(apreq_handle_t *handle,
+ const char **path)
+{
+ ap_filter_t *f = get_apreq_filter(handle);
+ struct filter_ctx *ctx;
+
+ if (f->ctx == NULL)
+ apreq_filter_make_context(f);
+
+ ctx = f->ctx;
+ *path = ctx->parser ? ctx->parser->temp_dir : ctx->temp_dir;
+ return APR_SUCCESS;
+}
+
+static APREQ_MODULE(apache2, APREQ_APACHE2_MMN);
+
+APREQ_DECLARE(apreq_handle_t *) apreq_handle_apache2(request_rec *r)
+{
+ struct apache2_handle *req =
+ ap_get_module_config(r->request_config, &apreq_module);
+
+ if (req != NULL) {
+ get_apreq_filter(&req->handle);
+ return &req->handle;
+ }
+
+ req = apr_palloc(r->pool, sizeof *req);
+ ap_set_module_config(r->request_config, &apreq_module, req);
+
+ req->handle.module = &apache2_module;
+ req->handle.pool = r->pool;
+ req->handle.bucket_alloc = r->connection->bucket_alloc;
+ req->r = r;
+
+ req->args_status = req->jar_status = APR_EINIT;
+ req->args = req->jar = NULL;
+
+ req->f = NULL;
+
+ get_apreq_filter(&req->handle);
+ return &req->handle;
+
+}