mod_request.c revision c033603eabef0ba88287fa7f0e22c5e1c6564f90
/* 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
*
*
* 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.
*/
/*
* mod_request.c --- HTTP routines to set aside or process request bodies.
*/
#include "apr.h"
#include "apr_strings.h"
#include "apr_buckets.h"
#include "apr_lib.h"
#include "ap_config.h"
#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "http_log.h" /* For errors detected in basic auth common
* support code... */
#include "http_request.h"
#include "mod_request.h"
/* Handles for core filters */
ap_filter_t *f,
int http_error)
{
apr_bucket *e;
f->c->bucket_alloc);
e = apr_bucket_eos_create(f->c->bucket_alloc);
}
typedef struct keep_body_filter_ctx {
/**
* This is the KEEP_BODY_INPUT filter for HTTP requests, for times when the
* body should be set aside for future use by other modules.
*/
{
apr_bucket *e;
if (!ctx) {
const char *lenp;
/* must we step out of the way? */
}
/* fail fast if the content length exceeds keep body */
if (lenp) {
* string (excluding leading space) (the endstr checks)
* and a negative number. */
"Invalid Content-Length");
return bail_out_on_error(b, f, HTTP_REQUEST_ENTITY_TOO_LARGE);
}
/* If we have a limit in effect and we know the C-L ahead of
* time, stop it here if it is invalid.
*/
"Requested content-length of %" APR_OFF_T_FMT
" is larger than the configured limit"
return bail_out_on_error(b, f, HTTP_REQUEST_ENTITY_TOO_LARGE);
}
}
}
/* get the brigade from upstream, and read it in to get its length */
if (rv == APR_SUCCESS) {
}
/* does the length take us over the limit? */
if (f->r->kept_body) {
apr_brigade_cleanup(f->r->kept_body);
}
"Requested content-length of %" APR_OFF_T_FMT
" is larger than the configured limit"
return bail_out_on_error(b, f, HTTP_REQUEST_ENTITY_TOO_LARGE);
}
/* pass any errors downstream */
if (rv != APR_SUCCESS) {
if (f->r->kept_body) {
apr_brigade_cleanup(f->r->kept_body);
}
return rv;
}
/* all is well, set aside the buckets */
for (bucket = APR_BRIGADE_FIRST(b);
bucket != APR_BRIGADE_SENTINEL(b);
{
apr_bucket_copy(bucket, &e);
APR_BRIGADE_INSERT_TAIL(f->r->kept_body, e);
}
return APR_SUCCESS;
}
typedef struct kept_body_filter_ctx {
/**
* Initialisation of filter to handle a kept body on subrequests.
*
* If a body is to be reinserted into a subrequest, any chunking will have
* been removed from the body during storage. We need to change the request
* from Transfer-Encoding: chunked to an explicit Content-Length.
*/
static int kept_body_filter_init(ap_filter_t *f) {
request_rec *r = f->r;
if (kept_body) {
}
return OK;
}
/**
* Filter to handle a kept body on subrequests.
*
* If a body has been previously kept by the request, and if a subrequest wants
* to re-insert the body into the request, this input filter makes it happen.
*/
{
request_rec *r = f->r;
/* just get out of the way of things we don't want. */
}
/* set up the context if it does not already exist */
if (!ctx) {
}
/* kept_body is finished, send next filter */
}
/* send all of the kept_body, but no more */
}
/* send part of the kept_body */
return rv;
}
return rv;
}
do {
const char *str;
/* As above; this should not fail since the bucket has
* a known length, but just to be sure, this takes
* care of uncopyable buckets that do somehow manage
* to slip through. */
/* XXX: check for failure? */
}
return APR_SUCCESS;
}
/* form parsing stuff */
typedef enum {
/**
* Read the body and parse any form found, which must be of the
* type application/x-www-form-urlencoded.
*
* strings with a maximum length of HUGE_STRING_LEN, and the
* values as bucket brigades. This allows values to be arbitrarily
* large.
*
* All url-encoding is removed from both the names and the values
* on the fly. The names are interpreted as strings, while the
* values are interpreted as blocks of binary data, that may
* contain the 0 character.
*
* In order to ensure that resource limits are not exceeded, a
* maximum size must be provided. If the sum of the lengths of
* the names and the values exceed this size, this function
* will return HTTP_REQUEST_ENTITY_TOO_LARGE.
*
* An optional number of parameters can be provided, if the number
* of parameters provided exceeds this amount, this function will
* return HTTP_REQUEST_ENTITY_TOO_LARGE. If this value is negative,
* no limit is imposed, and the number of parameters is in turn
* constrained by the size parameter above.
*
* This function honours any kept_body configuration, and the
* original raw request body will be saved to the kept_body brigade
* if so configured, just as ap_discard_request_body does.
*
* NOTE: File upload is not yet supported, but can be without change
* to the function call.
*/
{
int seen_eos = 0;
const char *ct;
apr_size_t offset = 0;
char hi = 0;
char low = 0;
/* sanity check - we only support forms for now */
return ap_discard_request_body(r);
}
if (!f) {
f = r->input_filters;
}
do {
if (rv != APR_SUCCESS) {
}
const char *data;
if (last) {
}
if (APR_BUCKET_IS_EOS(bucket)) {
seen_eos = 1;
break;
}
continue;
}
if (rv != APR_SUCCESS) {
return HTTP_BAD_REQUEST;
}
char c = *data++;
if ('+' == c) {
c = ' ';
}
else if ('&' == c) {
}
if ('%' == c) {
continue;
}
if (FORM_PERCENTA == percent) {
if (c >= 'a') {
}
else if (c >= 'A') {
}
else if (c >= '0') {
hi = c - '0';
}
continue;
}
if (FORM_PERCENTB == percent) {
if (c >= 'a') {
}
else if (c >= 'A') {
}
else if (c >= '0') {
low = c - '0';
}
}
switch (state) {
case FORM_AMP:
if (pair) {
}
offset = 0;
num--;
break;
case FORM_NAME:
if (offset < HUGE_STRING_LEN) {
if ('=' == c) {
offset = 0;
state = FORM_VALUE;
}
else {
size--;
}
}
else {
state = FORM_ABORT;
}
break;
case FORM_VALUE:
if (offset >= HUGE_STRING_LEN) {
offset = 0;
}
size--;
break;
default:
break;
}
}
}
} while (!seen_eos);
return HTTP_REQUEST_ENTITY_TOO_LARGE;
}
}
return OK;
}
/**
* Check whether this filter is not already present.
*/
{
ap_filter_t * f = r->input_filters;
while (f) {
return 1;
}
f = f->next;
}
return 0;
}
/**
* Insert filter hook.
*
* Add the KEEP_BODY filter to the request, if the admin wants to keep
* the body using the KeptBodySize directive.
*
* As a precaution, any pre-existing instances of either the kept_body or
* keep_body filters will be removed before the filter is added.
*
* @param r The request
*/
static void ap_request_insert_filter(request_rec * r)
{
if (r->kept_body) {
NULL, r, r->connection);
}
}
NULL, r, r->connection);
}
}
}
/**
* Remove the kept_body and keep body filters from this specific request.
*/
static void ap_request_remove_filter(request_rec * r)
{
ap_filter_t * f = r->input_filters;
while (f) {
}
f = f->next;
}
}
{
return (void *) new;
}
{
return new;
}
const char *arg)
{
return "KeptBodySize must be a size in bytes, or zero.";
}
return NULL;
}
static const command_rec request_cmds[] = {
"Maximum size of request bodies kept aside for use by filters"),
{ NULL }
};
static void register_hooks(apr_pool_t *p)
{
}
create_request_dir_config, /* create per-directory config structure */
merge_request_dir_config, /* merge per-directory config structures */
NULL, /* create per-server config structure */
NULL, /* merge per-server config structures */
request_cmds, /* command apr_table_t */
register_hooks /* register hooks */
};