Commit 1d73ae0f authored by Philip M. Gollucci's avatar Philip M. Gollucci
Browse files

import apache 2.x module portion of apreq

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1200457 13f79535-47bb-0310-9956-ffa450edef68
parent 5d1e1a4f
Loading
Loading
Loading
Loading
+41 −0
Original line number Diff line number Diff line
TEST_CONFIG_SCRIPT = package Apache::TestMM; filter_args(); generate_script("t/TEST")
mod_apreq2_la_LDFLAGS = -export-dynamic -module -avoid-version \
                        `@APREQ_CONFIG@ --link-libtool --libs` @APR_LTFLAGS@
mod_apreq2_la_SOURCES = apreq_private_apache2.h handle.c filter.c

pkgcfgdir = `@APACHE2_APXS@ -q SYSCONFDIR`
pkgincludedir = `@APACHE2_APXS@ -q INCLUDEDIR`/@APREQ_LIBNAME@
pkglibdir = `@APACHE2_APXS@ -q LIBEXECDIR`

AM_CPPFLAGS = @APACHE2_INCLUDES@ @APR_INCLUDES@

if BUILD_HTTPD

# XXX FIXME: static builds don't work anymore
# mod_apreq2 needs to be built from httpd-2.X, e.g.
#
# % cd ../httpd-2.X;
# % ./configure --with-module=filters:../httpd-apreq-2/module/apache2/mod_apreq2.c ...
#
# See the INSTALL file for details.

@APACHE2_HTTPD@:
	cd @APACHE2_SRC@ && $(MAKE)

all-local: @APACHE2_HTTPD@

else

pkginclude_HEADERS = apreq_module_apache2.h
pkglib_LTLIBRARIES = mod_apreq2.la

install-exec-local :
	@echo "----------------------------------------------------------------------"
	@echo "Before you can use mod_apreq2, you must ensure that an appropriate"
	@echo "\"LoadModule\" line appears in your webserver's config file:"
	@echo "$(pkgcfgdir)/httpd.conf"
	@echo 
	@echo "LoadModule apreq_module    $(pkglibdir)/mod_apreq2.so"
	@echo "----------------------------------------------------------------------"

endif
+182 −0
Original line number Diff line number Diff line
/*
**  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.
*/

#ifndef APREQ_APACHE2_H
#define APREQ_APACHE2_H

#include "apreq_module.h"
#include "apr_optional.h"
#include <httpd.h>

#ifdef  __cplusplus
 extern "C" {
#endif


/**
 * @defgroup mod_apreq2 Apache 2.X Filter Module
 * @ingroup apreq_module
 * @brief mod_apreq2 - DSO that ties libapreq2 to Apache HTTPD 2.X.
 *
 * mod_apreq2 provides the "APREQ2" 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_apreq2 in Apache 2.X</h2>
 *
 * The installation process triggered by
 * <code>% make install</code>
 * <em>will not modify your webserver's config file</em>. Hence,
 * be sure you activate it on startup by adding a LoadModule directive
 * to your webserver config; e.g.
 *
 * @code
 *
 *     LoadModule apreq_module    modules/mod_apreq2.so
 *
 * @endcode
 *
 * The mod_apreq2 filter is named "apreq2", and may be used in Apache's
 * input filter directives, e.g.
 * @code
 *
 *     AddInputFilter apreq2         # or
 *     SetInputFilter apreq2
 *
 * @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 invokes
 * apreq_handle_apache2() <em>before the content handler ultimately reads
 * from the input filter chain</em>.  It is important to realize that no
 * matter how the input filters are initially arranged, the APREQ2 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_apreq2</CAPTION>
 *   <TR>
 *     <TH>Directive</TH>
 *     <TH>Context</TH>
 *     <TH>Default</TH><TH>Description</TH>
 *   </TR>
 *   <TR class="odd">
 *     <TD>APREQ2_ReadLimit</TD>
 *     <TD>directory</TD>
 *     <TD> #APREQ_DEFAULT_READ_LIMIT </TD>
 *     <TD> Maximum number of bytes mod_apreq2 will send off to libapreq2
 *          for parsing. mod_apreq2 will log this event and subsequently
 *          remove itself from the filter chain.
 *     </TD>
 *   </TR>
 *   <TR>
 *     <TD>APREQ2_BrigadeLimit</TD>
 *     <TD>directory</TD>
 *     <TD>#APREQ_DEFAULT_BRIGADE_LIMIT</TD>
 *     <TD> Maximum number of bytes mod_apreq2 will let accumulate
 *          within the heap-buckets in a brigade.  Excess data will be
 *          spooled to an appended file bucket.
 *     </TD>
 *   </TR>
 *   <TR class="odd">
 *     <TD>APREQ2_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 APREQ2_BrigadeLimit 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".
 *   XXX apreq parser registration in post_config
 * </PRE>
 *
 * @{
 */
/**
 * Create an apreq handle which communicates with an Apache 2.X
 * request_rec.
 */
APREQ_DECLARE(apreq_handle_t *) apreq_handle_apache2(request_rec *r);

/**
 *
 *      
 */
#ifdef WIN32
typedef __declspec(dllexport) apreq_handle_t *
(__stdcall apr_OFN_apreq_handle_apache2_t) (request_rec *r);
#else
APR_DECLARE_OPTIONAL_FN(APREQ_DECLARE(apreq_handle_t *),
                        apreq_handle_apache2, (request_rec *r));
#endif

/**
 * The mod_apreq2 filter is named "apreq2", and may be used in Apache's
 * input filter directives, e.g.
 * @code
 *
 *     AddInputFilter apreq2         # or
 *     SetInputFilter apreq2
 * @endcode
 * See above
 */
#define APREQ_FILTER_NAME "apreq2"

/**
 * The Apache2 Module Magic Number for use in the Apache 2.x module structures
 * This gets bumped if changes in th4e API will break third party applications
 * using this apache2 module
 * @see APREQ_MODULE
 */
#define APREQ_APACHE2_MMN 20101207

/** @} */

#ifdef __cplusplus
 }
#endif

#endif
+56 −0
Original line number Diff line number Diff line
extern module AP_MODULE_DECLARE_DATA apreq_module;

struct dir_config {
    const char         *temp_dir;
    apr_uint64_t        read_limit;
    apr_size_t          brigade_limit;
};

/* The "warehouse", stored in r->request_config */
struct apache2_handle {
    apreq_handle_t      handle;
    request_rec        *r;
    apr_table_t        *jar, *args;
    apr_status_t        jar_status, args_status;
    ap_filter_t        *f;
};

/* Tracks the apreq filter state */
struct filter_ctx {
    apr_bucket_brigade *bb;    /* input brigade that's passed to the parser */
    apr_bucket_brigade *bbtmp; /* temporary copy of bb, destined for the spool */
    apr_bucket_brigade *spool; /* copied prefetch data for downstream filters */
    apreq_parser_t     *parser;
    apreq_hook_t       *hook_queue;
    apreq_hook_t       *find_param;
    apr_table_t        *body;
    apr_status_t        body_status;
    apr_status_t        filter_error;
    apr_uint64_t        bytes_read;     /* Total bytes read into this filter. */
    apr_uint64_t        read_limit;     /* Max bytes the filter may show to parser */
    apr_size_t          brigade_limit;
    const char         *temp_dir;
};

apr_status_t apreq_filter_prefetch(ap_filter_t *f, apr_off_t readbytes);
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);

void apreq_filter_make_context(ap_filter_t *f);
void apreq_filter_init_context(ap_filter_t *f);

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;
    }
}

modules/apreq/filter.c

0 → 100644
+540 −0

File added.

Preview size limit exceeded, changes collapsed.

modules/apreq/handle.c

0 → 100644
+441 −0
Original line number Diff line number Diff line
/*
**  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 "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;
    apreq_hook_find_param_ctx_t *hook_ctx;

    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 */
        hook_ctx = apr_palloc(handle->pool, sizeof *hook_ctx);

        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;
        h->ctx = hook_ctx;
        ctx->parser->hook = h;
        h->ctx = hook_ctx;
        hook_ctx->name = name;
        hook_ctx->param = NULL;
        hook_ctx->prev = ctx->parser->hook;

        do {
            apreq_filter_prefetch(f, APREQ_DEFAULT_READ_BLOCK_SIZE);
            if (hook_ctx->param != NULL)
                return hook_ctx->param;
        } 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;

}