Blame modules/filters/mod_reqtimeout.c

Packit 90a5c9
/* Licensed to the Apache Software Foundation (ASF) under one or more
Packit 90a5c9
 * contributor license agreements.  See the NOTICE file distributed with
Packit 90a5c9
 * this work for additional information regarding copyright ownership.
Packit 90a5c9
 * The ASF licenses this file to You under the Apache License, Version 2.0
Packit 90a5c9
 * (the "License"); you may not use this file except in compliance with
Packit 90a5c9
 * the License.  You may obtain a copy of the License at
Packit 90a5c9
 *
Packit 90a5c9
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit 90a5c9
 *
Packit 90a5c9
 * Unless required by applicable law or agreed to in writing, software
Packit 90a5c9
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 90a5c9
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 90a5c9
 * See the License for the specific language governing permissions and
Packit 90a5c9
 * limitations under the License.
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
#include "httpd.h"
Packit 90a5c9
#include "http_config.h"
Packit 90a5c9
#include "http_request.h"
Packit 90a5c9
#include "http_connection.h"
Packit 90a5c9
#include "http_protocol.h"
Packit 90a5c9
#include "http_log.h"
Packit 90a5c9
#include "http_core.h"
Packit 90a5c9
#include "util_filter.h"
Packit 90a5c9
#define APR_WANT_STRFUNC
Packit 90a5c9
#include "apr_strings.h"
Packit 90a5c9
#include "apr_version.h"
Packit 90a5c9
Packit 90a5c9
module AP_MODULE_DECLARE_DATA reqtimeout_module;
Packit 90a5c9
Packit 90a5c9
#define UNSET                            -1
Packit 90a5c9
#define MRT_DEFAULT_HEADER_TIMEOUT       20
Packit 90a5c9
#define MRT_DEFAULT_HEADER_MAX_TIMEOUT   40
Packit 90a5c9
#define MRT_DEFAULT_HEADER_MIN_RATE      500
Packit 90a5c9
#define MRT_DEFAULT_BODY_TIMEOUT         20
Packit 90a5c9
#define MRT_DEFAULT_BODY_MAX_TIMEOUT     0
Packit 90a5c9
#define MRT_DEFAULT_BODY_MIN_RATE        500
Packit 90a5c9
Packit 90a5c9
typedef struct
Packit 90a5c9
{
Packit 90a5c9
    int header_timeout;     /* timeout for reading the req hdrs in secs */
Packit 90a5c9
    int header_max_timeout; /* max timeout for req hdrs in secs */
Packit 90a5c9
    int header_min_rate;    /* min rate for reading req hdrs in bytes/s */
Packit 90a5c9
    apr_time_t header_rate_factor;
Packit 90a5c9
    int body_timeout;       /* timeout for reading the req body in secs */
Packit 90a5c9
    int body_max_timeout;   /* max timeout for req body in secs */
Packit 90a5c9
    int body_min_rate;      /* min rate for reading req body in bytes/s */
Packit 90a5c9
    apr_time_t body_rate_factor;
Packit 90a5c9
} reqtimeout_srv_cfg;
Packit 90a5c9
Packit 90a5c9
/* this struct is used both as conn_config and as filter context */
Packit 90a5c9
typedef struct
Packit 90a5c9
{
Packit 90a5c9
    apr_time_t timeout_at;
Packit 90a5c9
    apr_time_t max_timeout_at;
Packit 90a5c9
    int min_rate;
Packit 90a5c9
    int new_timeout;
Packit 90a5c9
    int new_max_timeout;
Packit 90a5c9
    int in_keep_alive;
Packit 90a5c9
    char *type;
Packit 90a5c9
    apr_socket_t *socket;
Packit 90a5c9
    apr_time_t rate_factor;
Packit 90a5c9
    apr_bucket_brigade *tmpbb;
Packit 90a5c9
} reqtimeout_con_cfg;
Packit 90a5c9
Packit 90a5c9
static const char *const reqtimeout_filter_name = "reqtimeout";
Packit 90a5c9
static int default_header_rate_factor;
Packit 90a5c9
static int default_body_rate_factor;
Packit 90a5c9
Packit 90a5c9
static void extend_timeout(reqtimeout_con_cfg *ccfg, apr_bucket_brigade *bb)
Packit 90a5c9
{
Packit 90a5c9
    apr_off_t len;
Packit 90a5c9
    apr_time_t new_timeout_at;
Packit 90a5c9
Packit 90a5c9
    if (apr_brigade_length(bb, 0, &len) != APR_SUCCESS || len <= 0)
Packit 90a5c9
        return;
Packit 90a5c9
Packit 90a5c9
    new_timeout_at = ccfg->timeout_at + len * ccfg->rate_factor;
Packit 90a5c9
    if (ccfg->max_timeout_at > 0 && new_timeout_at > ccfg->max_timeout_at) {
Packit 90a5c9
        ccfg->timeout_at = ccfg->max_timeout_at;
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        ccfg->timeout_at = new_timeout_at;
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t check_time_left(reqtimeout_con_cfg *ccfg,
Packit 90a5c9
                                    apr_time_t *time_left_p,
Packit 90a5c9
                                    apr_time_t now)
Packit 90a5c9
{
Packit 90a5c9
    if (!now)
Packit 90a5c9
        now = apr_time_now();
Packit 90a5c9
    *time_left_p = ccfg->timeout_at - now;
Packit 90a5c9
    if (*time_left_p <= 0)
Packit 90a5c9
        return APR_TIMEUP;
Packit 90a5c9
Packit 90a5c9
    if (*time_left_p < apr_time_from_sec(1)) {
Packit 90a5c9
        *time_left_p = apr_time_from_sec(1);
Packit 90a5c9
    }
Packit 90a5c9
    return APR_SUCCESS;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t have_lf_or_eos(apr_bucket_brigade *bb)
Packit 90a5c9
{
Packit 90a5c9
    apr_bucket *b = APR_BRIGADE_LAST(bb);
Packit 90a5c9
Packit 90a5c9
    for ( ; b != APR_BRIGADE_SENTINEL(bb) ; b = APR_BUCKET_PREV(b) ) {
Packit 90a5c9
        const char *str;
Packit 90a5c9
        apr_size_t len;
Packit 90a5c9
        apr_status_t rv;
Packit 90a5c9
Packit 90a5c9
        if (APR_BUCKET_IS_EOS(b))
Packit 90a5c9
            return APR_SUCCESS;
Packit 90a5c9
Packit 90a5c9
        if (APR_BUCKET_IS_METADATA(b))
Packit 90a5c9
            continue;
Packit 90a5c9
Packit 90a5c9
        rv = apr_bucket_read(b, &str, &len, APR_BLOCK_READ);
Packit 90a5c9
        if (rv != APR_SUCCESS)
Packit 90a5c9
            return rv;
Packit 90a5c9
Packit 90a5c9
        if (len == 0)
Packit 90a5c9
            continue;
Packit 90a5c9
Packit 90a5c9
        if (str[len-1] == APR_ASCII_LF)
Packit 90a5c9
            return APR_SUCCESS;
Packit 90a5c9
    }
Packit 90a5c9
    return APR_INCOMPLETE;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * Append bbIn to bbOut and merge small buckets, to avoid DoS by high memory
Packit 90a5c9
 * usage
Packit 90a5c9
 */
Packit 90a5c9
static apr_status_t brigade_append(apr_bucket_brigade *bbOut, apr_bucket_brigade *bbIn)
Packit 90a5c9
{
Packit 90a5c9
    while (!APR_BRIGADE_EMPTY(bbIn)) {
Packit 90a5c9
        apr_bucket *e = APR_BRIGADE_FIRST(bbIn);
Packit 90a5c9
        const char *str;
Packit 90a5c9
        apr_size_t len;
Packit 90a5c9
        apr_status_t rv;
Packit 90a5c9
Packit 90a5c9
        rv = apr_bucket_read(e, &str, &len, APR_BLOCK_READ);
Packit 90a5c9
        if (rv != APR_SUCCESS) {
Packit 90a5c9
            return rv;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        APR_BUCKET_REMOVE(e);
Packit 90a5c9
        if (APR_BUCKET_IS_METADATA(e) || len > APR_BUCKET_BUFF_SIZE/4) {
Packit 90a5c9
            APR_BRIGADE_INSERT_TAIL(bbOut, e);
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            if (len > 0) {
Packit 90a5c9
                rv = apr_brigade_write(bbOut, NULL, NULL, str, len);
Packit 90a5c9
                if (rv != APR_SUCCESS) {
Packit 90a5c9
                    apr_bucket_destroy(e);
Packit 90a5c9
                    return rv;
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
            apr_bucket_destroy(e);
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    return APR_SUCCESS;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
#define MIN(x,y) ((x) < (y) ? (x) : (y))
Packit 90a5c9
static apr_status_t reqtimeout_filter(ap_filter_t *f,
Packit 90a5c9
                                      apr_bucket_brigade *bb,
Packit 90a5c9
                                      ap_input_mode_t mode,
Packit 90a5c9
                                      apr_read_type_e block,
Packit 90a5c9
                                      apr_off_t readbytes)
Packit 90a5c9
{
Packit 90a5c9
    apr_time_t time_left;
Packit 90a5c9
    apr_time_t now = 0;
Packit 90a5c9
    apr_status_t rv;
Packit 90a5c9
    apr_interval_time_t saved_sock_timeout = UNSET;
Packit 90a5c9
    reqtimeout_con_cfg *ccfg = f->ctx;
Packit 90a5c9
Packit 90a5c9
    if (ccfg->in_keep_alive) {
Packit 90a5c9
        /* For this read[_request line()], wait for the first byte using the
Packit 90a5c9
         * normal keep-alive timeout (hence don't take this expected idle time
Packit 90a5c9
         * into account to setup the connection expiry below).
Packit 90a5c9
         */
Packit 90a5c9
        ccfg->in_keep_alive = 0;
Packit 90a5c9
        rv = ap_get_brigade(f->next, bb, AP_MODE_SPECULATIVE, block, 1);
Packit 90a5c9
        if (rv != APR_SUCCESS || APR_BRIGADE_EMPTY(bb)) {
Packit 90a5c9
            return rv;
Packit 90a5c9
        }
Packit 90a5c9
        apr_brigade_cleanup(bb);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (ccfg->new_timeout > 0) {
Packit 90a5c9
        /* set new timeout */
Packit 90a5c9
        now = apr_time_now();
Packit 90a5c9
        ccfg->timeout_at = now + apr_time_from_sec(ccfg->new_timeout);
Packit 90a5c9
        ccfg->new_timeout = 0;
Packit 90a5c9
        if (ccfg->new_max_timeout > 0) {
Packit 90a5c9
            ccfg->max_timeout_at = now + apr_time_from_sec(ccfg->new_max_timeout);
Packit 90a5c9
            ccfg->new_max_timeout = 0;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    else if (ccfg->timeout_at == 0) {
Packit 90a5c9
        /* no timeout set, or in between requests */
Packit 90a5c9
        return ap_get_brigade(f->next, bb, mode, block, readbytes);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (!ccfg->socket) {
Packit 90a5c9
        ccfg->socket = ap_get_conn_socket(f->c);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    rv = check_time_left(ccfg, &time_left, now);
Packit 90a5c9
    if (rv != APR_SUCCESS)
Packit 90a5c9
        goto out;
Packit 90a5c9
Packit 90a5c9
    if (block == APR_NONBLOCK_READ || mode == AP_MODE_INIT
Packit 90a5c9
        || mode == AP_MODE_EATCRLF) {
Packit 90a5c9
        rv = ap_get_brigade(f->next, bb, mode, block, readbytes);
Packit 90a5c9
        if (ccfg->min_rate > 0 && rv == APR_SUCCESS) {
Packit 90a5c9
            extend_timeout(ccfg, bb);
Packit 90a5c9
        }
Packit 90a5c9
        return rv;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    rv = apr_socket_timeout_get(ccfg->socket, &saved_sock_timeout);
Packit 90a5c9
    AP_DEBUG_ASSERT(rv == APR_SUCCESS);
Packit 90a5c9
Packit 90a5c9
    rv = apr_socket_timeout_set(ccfg->socket, MIN(time_left, saved_sock_timeout));
Packit 90a5c9
    AP_DEBUG_ASSERT(rv == APR_SUCCESS);
Packit 90a5c9
Packit 90a5c9
    if (mode == AP_MODE_GETLINE) {
Packit 90a5c9
        /*
Packit 90a5c9
         * For a blocking AP_MODE_GETLINE read, apr_brigade_split_line()
Packit 90a5c9
         * would loop until a whole line has been read. As this would make it
Packit 90a5c9
         * impossible to enforce a total timeout, we only do non-blocking
Packit 90a5c9
         * reads.
Packit 90a5c9
         */
Packit 90a5c9
        apr_off_t remaining = HUGE_STRING_LEN;
Packit 90a5c9
        do {
Packit 90a5c9
            apr_off_t bblen;
Packit 90a5c9
#if APR_MAJOR_VERSION < 2
Packit 90a5c9
            apr_int32_t nsds;
Packit 90a5c9
            apr_interval_time_t poll_timeout;
Packit 90a5c9
            apr_pollfd_t pollset;
Packit 90a5c9
#endif
Packit 90a5c9
Packit 90a5c9
            rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE, APR_NONBLOCK_READ, remaining);
Packit 90a5c9
            if (rv != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(rv)) {
Packit 90a5c9
                break;
Packit 90a5c9
            }
Packit 90a5c9
Packit 90a5c9
            if (!APR_BRIGADE_EMPTY(bb)) {
Packit 90a5c9
                if (ccfg->min_rate > 0) {
Packit 90a5c9
                    extend_timeout(ccfg, bb);
Packit 90a5c9
                }
Packit 90a5c9
Packit 90a5c9
                rv = have_lf_or_eos(bb);
Packit 90a5c9
                if (rv != APR_INCOMPLETE) {
Packit 90a5c9
                    break;
Packit 90a5c9
                }
Packit 90a5c9
Packit 90a5c9
                rv = apr_brigade_length(bb, 1, &bblen);
Packit 90a5c9
                if (rv != APR_SUCCESS) {
Packit 90a5c9
                    break;
Packit 90a5c9
                }
Packit 90a5c9
                remaining -= bblen;
Packit 90a5c9
                if (remaining <= 0) {
Packit 90a5c9
                    break;
Packit 90a5c9
                }
Packit 90a5c9
Packit 90a5c9
                /* Haven't got a whole line yet, save what we have ... */
Packit 90a5c9
                if (!ccfg->tmpbb) {
Packit 90a5c9
                    ccfg->tmpbb = apr_brigade_create(f->c->pool, f->c->bucket_alloc);
Packit 90a5c9
                }
Packit 90a5c9
                rv = brigade_append(ccfg->tmpbb, bb);
Packit 90a5c9
                if (rv != APR_SUCCESS)
Packit 90a5c9
                    break;
Packit 90a5c9
            }
Packit 90a5c9
Packit 90a5c9
            /* ... and wait for more */
Packit 90a5c9
#if APR_MAJOR_VERSION < 2
Packit 90a5c9
            pollset.p = f->c->pool;
Packit 90a5c9
            pollset.desc_type = APR_POLL_SOCKET;
Packit 90a5c9
            pollset.reqevents = APR_POLLIN|APR_POLLHUP;
Packit 90a5c9
            pollset.desc.s = ccfg->socket;
Packit 90a5c9
            apr_socket_timeout_get(ccfg->socket, &poll_timeout);
Packit 90a5c9
            rv = apr_poll(&pollset, 1, &nsds, poll_timeout);
Packit 90a5c9
#else
Packit 90a5c9
            rv = apr_socket_wait(ccfg->socket, APR_WAIT_READ);
Packit 90a5c9
#endif
Packit 90a5c9
            if (rv != APR_SUCCESS)
Packit 90a5c9
                break;
Packit 90a5c9
Packit 90a5c9
            rv = check_time_left(ccfg, &time_left, 0);
Packit 90a5c9
            if (rv != APR_SUCCESS)
Packit 90a5c9
                break;
Packit 90a5c9
Packit 90a5c9
            rv = apr_socket_timeout_set(ccfg->socket,
Packit 90a5c9
                                   MIN(time_left, saved_sock_timeout));
Packit 90a5c9
            AP_DEBUG_ASSERT(rv == APR_SUCCESS);
Packit 90a5c9
Packit 90a5c9
        } while (1);
Packit 90a5c9
Packit 90a5c9
        if (ccfg->tmpbb)
Packit 90a5c9
            APR_BRIGADE_PREPEND(bb, ccfg->tmpbb);
Packit 90a5c9
Packit 90a5c9
    }
Packit 90a5c9
    else { /* mode != AP_MODE_GETLINE */
Packit 90a5c9
        rv = ap_get_brigade(f->next, bb, mode, block, readbytes);
Packit 90a5c9
        /* Don't extend the timeout in speculative mode, wait for
Packit 90a5c9
         * the real (relevant) bytes to be asked later, within the
Packit 90a5c9
         * currently alloted time.
Packit 90a5c9
         */
Packit 90a5c9
        if (ccfg->min_rate > 0 && rv == APR_SUCCESS
Packit 90a5c9
                && mode != AP_MODE_SPECULATIVE) {
Packit 90a5c9
            extend_timeout(ccfg, bb);
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    apr_socket_timeout_set(ccfg->socket, saved_sock_timeout);
Packit 90a5c9
Packit 90a5c9
out:
Packit 90a5c9
    if (APR_STATUS_IS_TIMEUP(rv)) {
Packit 90a5c9
        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, f->c, APLOGNO(01382)
Packit 90a5c9
                      "Request %s read timeout", ccfg->type);
Packit 90a5c9
        /*
Packit 90a5c9
         * If we allow a normal lingering close, the client may keep this
Packit 90a5c9
         * process/thread busy for another 30s (MAX_SECS_TO_LINGER).
Packit 90a5c9
         * Therefore we tell ap_lingering_close() to shorten this period to
Packit 90a5c9
         * 2s (SECONDS_TO_LINGER).
Packit 90a5c9
         */
Packit 90a5c9
        apr_table_setn(f->c->notes, "short-lingering-close", "1");
Packit 90a5c9
Packit 90a5c9
        /*
Packit 90a5c9
         * Also, we must not allow keep-alive requests, as
Packit 90a5c9
         * ap_finalize_protocol() may ignore our error status (if the timeout
Packit 90a5c9
         * happened on a request body that is discarded).
Packit 90a5c9
         */
Packit 90a5c9
        f->c->keepalive = AP_CONN_CLOSE;
Packit 90a5c9
    }
Packit 90a5c9
    return rv;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t reqtimeout_eor(ap_filter_t *f, apr_bucket_brigade *bb)
Packit 90a5c9
{
Packit 90a5c9
    if (!APR_BRIGADE_EMPTY(bb) && AP_BUCKET_IS_EOR(APR_BRIGADE_LAST(bb))) {
Packit 90a5c9
        reqtimeout_con_cfg *ccfg = f->ctx;
Packit 90a5c9
        ccfg->timeout_at = 0;
Packit 90a5c9
    }
Packit 90a5c9
    return ap_pass_brigade(f->next, bb);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static int reqtimeout_init(conn_rec *c)
Packit 90a5c9
{
Packit 90a5c9
    reqtimeout_con_cfg *ccfg;
Packit 90a5c9
    reqtimeout_srv_cfg *cfg;
Packit 90a5c9
Packit 90a5c9
    cfg = ap_get_module_config(c->base_server->module_config,
Packit 90a5c9
                               &reqtimeout_module);
Packit 90a5c9
    AP_DEBUG_ASSERT(cfg != NULL);
Packit 90a5c9
    if (cfg->header_timeout == 0 && cfg->body_timeout == 0) {
Packit 90a5c9
        /* disabled for this vhost */
Packit 90a5c9
        return DECLINED;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    ccfg = ap_get_module_config(c->conn_config, &reqtimeout_module);
Packit 90a5c9
    if (ccfg == NULL) {
Packit 90a5c9
        ccfg = apr_pcalloc(c->pool, sizeof(reqtimeout_con_cfg));
Packit 90a5c9
        ap_set_module_config(c->conn_config, &reqtimeout_module, ccfg);
Packit 90a5c9
        ap_add_output_filter(reqtimeout_filter_name, ccfg, NULL, c);
Packit 90a5c9
        ap_add_input_filter(reqtimeout_filter_name, ccfg, NULL, c);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* we are not handling the connection, we just do initialization */
Packit 90a5c9
    return DECLINED;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void reqtimeout_before_header(request_rec *r, conn_rec *c)
Packit 90a5c9
{
Packit 90a5c9
    reqtimeout_srv_cfg *cfg;
Packit 90a5c9
    reqtimeout_con_cfg *ccfg =
Packit 90a5c9
        ap_get_module_config(c->conn_config, &reqtimeout_module);
Packit 90a5c9
Packit 90a5c9
    if (ccfg == NULL) {
Packit 90a5c9
        /* not configured for this connection */
Packit 90a5c9
        return;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    cfg = ap_get_module_config(c->base_server->module_config,
Packit 90a5c9
                               &reqtimeout_module);
Packit 90a5c9
    AP_DEBUG_ASSERT(cfg != NULL);
Packit 90a5c9
Packit 90a5c9
    /* (Re)set the state for this new request, but ccfg->socket and
Packit 90a5c9
     * ccfg->tmpbb which have the lifetime of the connection.
Packit 90a5c9
     */
Packit 90a5c9
    ccfg->timeout_at = 0;
Packit 90a5c9
    ccfg->max_timeout_at = 0;
Packit 90a5c9
    ccfg->in_keep_alive = (c->keepalives > 0);
Packit 90a5c9
    ccfg->type = "header";
Packit 90a5c9
    if (cfg->header_timeout != UNSET) {
Packit 90a5c9
        ccfg->new_timeout     = cfg->header_timeout;
Packit 90a5c9
        ccfg->new_max_timeout = cfg->header_max_timeout;
Packit 90a5c9
        ccfg->min_rate        = cfg->header_min_rate;
Packit 90a5c9
        ccfg->rate_factor     = cfg->header_rate_factor;
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        ccfg->new_timeout     = MRT_DEFAULT_HEADER_TIMEOUT;
Packit 90a5c9
        ccfg->new_max_timeout = MRT_DEFAULT_HEADER_MAX_TIMEOUT;
Packit 90a5c9
        ccfg->min_rate        = MRT_DEFAULT_HEADER_MIN_RATE;
Packit 90a5c9
        ccfg->rate_factor     = default_header_rate_factor;
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static int reqtimeout_before_body(request_rec *r)
Packit 90a5c9
{
Packit 90a5c9
    reqtimeout_srv_cfg *cfg;
Packit 90a5c9
    reqtimeout_con_cfg *ccfg =
Packit 90a5c9
        ap_get_module_config(r->connection->conn_config, &reqtimeout_module);
Packit 90a5c9
Packit 90a5c9
    if (ccfg == NULL) {
Packit 90a5c9
        /* not configured for this connection */
Packit 90a5c9
        return OK;
Packit 90a5c9
    }
Packit 90a5c9
    cfg = ap_get_module_config(r->connection->base_server->module_config,
Packit 90a5c9
                               &reqtimeout_module);
Packit 90a5c9
    AP_DEBUG_ASSERT(cfg != NULL);
Packit 90a5c9
Packit 90a5c9
    ccfg->timeout_at = 0;
Packit 90a5c9
    ccfg->max_timeout_at = 0;
Packit 90a5c9
    ccfg->type = "body";
Packit 90a5c9
    if (r->method_number == M_CONNECT) {
Packit 90a5c9
        /* disabled for a CONNECT request */
Packit 90a5c9
        ccfg->new_timeout     = 0;
Packit 90a5c9
    }
Packit 90a5c9
    else if (cfg->body_timeout != UNSET) {
Packit 90a5c9
        ccfg->new_timeout     = cfg->body_timeout;
Packit 90a5c9
        ccfg->new_max_timeout = cfg->body_max_timeout;
Packit 90a5c9
        ccfg->min_rate        = cfg->body_min_rate;
Packit 90a5c9
        ccfg->rate_factor     = cfg->body_rate_factor;
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        ccfg->new_timeout     = MRT_DEFAULT_BODY_TIMEOUT;
Packit 90a5c9
        ccfg->new_max_timeout = MRT_DEFAULT_BODY_MAX_TIMEOUT;
Packit 90a5c9
        ccfg->min_rate        = MRT_DEFAULT_BODY_MIN_RATE;
Packit 90a5c9
        ccfg->rate_factor     = default_body_rate_factor;
Packit 90a5c9
    }
Packit 90a5c9
    return OK;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void *reqtimeout_create_srv_config(apr_pool_t *p, server_rec *s)
Packit 90a5c9
{
Packit 90a5c9
    reqtimeout_srv_cfg *cfg = apr_pcalloc(p, sizeof(reqtimeout_srv_cfg));
Packit 90a5c9
Packit 90a5c9
    cfg->header_timeout = UNSET;
Packit 90a5c9
    cfg->header_max_timeout = UNSET;
Packit 90a5c9
    cfg->header_min_rate = UNSET;
Packit 90a5c9
    cfg->body_timeout = UNSET;
Packit 90a5c9
    cfg->body_max_timeout = UNSET;
Packit 90a5c9
    cfg->body_min_rate = UNSET;
Packit 90a5c9
Packit 90a5c9
    return cfg;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
#define MERGE_INT(cfg, b, a, val) cfg->val = (a->val == UNSET) ? b->val : a->val;
Packit 90a5c9
static void *reqtimeout_merge_srv_config(apr_pool_t *p, void *base_, void *add_)
Packit 90a5c9
{
Packit 90a5c9
    reqtimeout_srv_cfg *base = base_;
Packit 90a5c9
    reqtimeout_srv_cfg *add  = add_;
Packit 90a5c9
    reqtimeout_srv_cfg *cfg  = apr_pcalloc(p, sizeof(reqtimeout_srv_cfg));
Packit 90a5c9
Packit 90a5c9
    MERGE_INT(cfg, base, add, header_timeout);
Packit 90a5c9
    MERGE_INT(cfg, base, add, header_max_timeout);
Packit 90a5c9
    MERGE_INT(cfg, base, add, header_min_rate);
Packit 90a5c9
    MERGE_INT(cfg, base, add, body_timeout);
Packit 90a5c9
    MERGE_INT(cfg, base, add, body_max_timeout);
Packit 90a5c9
    MERGE_INT(cfg, base, add, body_min_rate);
Packit 90a5c9
Packit 90a5c9
    cfg->header_rate_factor = (cfg->header_min_rate == UNSET) ?
Packit 90a5c9
                              base->header_rate_factor : add->header_rate_factor;
Packit 90a5c9
    cfg->body_rate_factor = (cfg->body_min_rate == UNSET) ?
Packit 90a5c9
                            base->body_rate_factor : add->body_rate_factor;
Packit 90a5c9
    return cfg;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char *parse_int(apr_pool_t *p, const char *arg, int *val)
Packit 90a5c9
{
Packit 90a5c9
    char *endptr;
Packit 90a5c9
    *val = strtol(arg, &endptr, 10);
Packit 90a5c9
Packit 90a5c9
    if (arg == endptr) {
Packit 90a5c9
        return apr_psprintf(p, "Value '%s' not numerical", endptr);
Packit 90a5c9
    }
Packit 90a5c9
    if (*endptr != '\0') {
Packit 90a5c9
        return apr_psprintf(p, "Cannot parse '%s'", endptr);
Packit 90a5c9
    }
Packit 90a5c9
    if (*val < 0) {
Packit 90a5c9
        return "Value must be non-negative";
Packit 90a5c9
    }
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char *set_reqtimeout_param(reqtimeout_srv_cfg *conf,
Packit 90a5c9
                                      apr_pool_t *p,
Packit 90a5c9
                                      const char *key,
Packit 90a5c9
                                      const char *val)
Packit 90a5c9
{
Packit 90a5c9
    const char *ret = NULL;
Packit 90a5c9
    char *rate_str = NULL, *initial_str, *max_str = NULL;
Packit 90a5c9
    int rate = 0, initial = 0, max = 0;
Packit 90a5c9
    enum { PARAM_HEADER, PARAM_BODY } type;
Packit 90a5c9
Packit 90a5c9
    if (!strcasecmp(key, "header")) {
Packit 90a5c9
        type = PARAM_HEADER;
Packit 90a5c9
    }
Packit 90a5c9
    else if (!strcasecmp(key, "body")) {
Packit 90a5c9
        type = PARAM_BODY;
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        return "Unknown RequestReadTimeout parameter";
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if ((rate_str = ap_strcasestr(val, ",minrate="))) {
Packit 90a5c9
        initial_str = apr_pstrndup(p, val, rate_str - val);
Packit 90a5c9
        rate_str += strlen(",minrate=");
Packit 90a5c9
        ret = parse_int(p, rate_str, &rate;;
Packit 90a5c9
        if (ret)
Packit 90a5c9
            return ret;
Packit 90a5c9
Packit 90a5c9
        if (rate == 0)
Packit 90a5c9
            return "Minimum data rate must be larger than 0";
Packit 90a5c9
Packit 90a5c9
        if ((max_str = strchr(initial_str, '-'))) {
Packit 90a5c9
            *max_str++ = '\0';
Packit 90a5c9
            ret = parse_int(p, max_str, &max;;
Packit 90a5c9
            if (ret)
Packit 90a5c9
                return ret;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        ret = parse_int(p, initial_str, &initial);
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        if (ap_strchr_c(val, '-'))
Packit 90a5c9
            return "Must set MinRate option if using timeout range";
Packit 90a5c9
        ret = parse_int(p, val, &initial);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (ret)
Packit 90a5c9
        return ret;
Packit 90a5c9
Packit 90a5c9
    if (max && initial >= max) {
Packit 90a5c9
        return "Maximum timeout must be larger than initial timeout";
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (type == PARAM_HEADER) {
Packit 90a5c9
        conf->header_timeout = initial;
Packit 90a5c9
        conf->header_max_timeout = max;
Packit 90a5c9
        conf->header_min_rate = rate;
Packit 90a5c9
        if (rate)
Packit 90a5c9
            conf->header_rate_factor = apr_time_from_sec(1) / rate;
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        conf->body_timeout = initial;
Packit 90a5c9
        conf->body_max_timeout = max;
Packit 90a5c9
        conf->body_min_rate = rate;
Packit 90a5c9
        if (rate)
Packit 90a5c9
            conf->body_rate_factor = apr_time_from_sec(1) / rate;
Packit 90a5c9
    }
Packit 90a5c9
    return ret;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char *set_reqtimeouts(cmd_parms *cmd, void *mconfig,
Packit 90a5c9
                                   const char *arg)
Packit 90a5c9
{
Packit 90a5c9
    reqtimeout_srv_cfg *conf =
Packit 90a5c9
    ap_get_module_config(cmd->server->module_config,
Packit 90a5c9
                         &reqtimeout_module);
Packit 90a5c9
Packit 90a5c9
    while (*arg) {
Packit 90a5c9
        char *word, *val;
Packit 90a5c9
        const char *err;
Packit 90a5c9
Packit 90a5c9
        word = ap_getword_conf(cmd->temp_pool, &arg;;
Packit 90a5c9
        val = strchr(word, '=');
Packit 90a5c9
        if (!val) {
Packit 90a5c9
            return "Invalid RequestReadTimeout parameter. Parameter must be "
Packit 90a5c9
            "in the form 'key=value'";
Packit 90a5c9
        }
Packit 90a5c9
        else
Packit 90a5c9
            *val++ = '\0';
Packit 90a5c9
Packit 90a5c9
        err = set_reqtimeout_param(conf, cmd->pool, word, val);
Packit 90a5c9
Packit 90a5c9
        if (err)
Packit 90a5c9
            return apr_psprintf(cmd->temp_pool, "RequestReadTimeout: %s=%s: %s",
Packit 90a5c9
                               word, val, err);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return NULL;
Packit 90a5c9
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void reqtimeout_hooks(apr_pool_t *pool)
Packit 90a5c9
{
Packit 90a5c9
    /*
Packit 90a5c9
     * mod_ssl is AP_FTYPE_CONNECTION + 5 and mod_reqtimeout needs to
Packit 90a5c9
     * be called before mod_ssl. Otherwise repeated reads during the ssl
Packit 90a5c9
     * handshake can prevent the timeout from triggering.
Packit 90a5c9
     */
Packit 90a5c9
    ap_register_input_filter(reqtimeout_filter_name, reqtimeout_filter, NULL,
Packit 90a5c9
                             AP_FTYPE_CONNECTION + 8);
Packit 90a5c9
Packit 90a5c9
    /*
Packit 90a5c9
     * We need to pause timeout detection in between requests, for
Packit 90a5c9
     * speculative and non-blocking reads, so between each outgoing EOR
Packit 90a5c9
     * and the next pre_read_request call.
Packit 90a5c9
     */
Packit 90a5c9
    ap_register_output_filter(reqtimeout_filter_name, reqtimeout_eor, NULL,
Packit 90a5c9
                              AP_FTYPE_CONNECTION);
Packit 90a5c9
Packit 90a5c9
    /*
Packit 90a5c9
     * mod_reqtimeout needs to be called before ap_process_http_request (which
Packit 90a5c9
     * is run at APR_HOOK_REALLY_LAST) but after all other protocol modules.
Packit 90a5c9
     * This ensures that it only influences normal http connections and not
Packit 90a5c9
     * e.g. mod_ftp. Also, if mod_reqtimeout used the pre_connection hook, it
Packit 90a5c9
     * would be inserted on mod_proxy's backend connections.
Packit 90a5c9
     */
Packit 90a5c9
    ap_hook_process_connection(reqtimeout_init, NULL, NULL, APR_HOOK_LAST);
Packit 90a5c9
Packit 90a5c9
    ap_hook_pre_read_request(reqtimeout_before_header, NULL, NULL,
Packit 90a5c9
                             APR_HOOK_MIDDLE);
Packit 90a5c9
    ap_hook_post_read_request(reqtimeout_before_body, NULL, NULL,
Packit 90a5c9
                              APR_HOOK_MIDDLE);
Packit 90a5c9
Packit 90a5c9
#if MRT_DEFAULT_HEADER_MIN_RATE > 0
Packit 90a5c9
    default_header_rate_factor = apr_time_from_sec(1) / MRT_DEFAULT_HEADER_MIN_RATE;
Packit 90a5c9
#endif
Packit 90a5c9
#if MRT_DEFAULT_BODY_MIN_RATE > 0
Packit 90a5c9
    default_body_rate_factor = apr_time_from_sec(1) / MRT_DEFAULT_BODY_MIN_RATE;
Packit 90a5c9
#endif
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const command_rec reqtimeout_cmds[] = {
Packit 90a5c9
    AP_INIT_RAW_ARGS("RequestReadTimeout", set_reqtimeouts, NULL, RSRC_CONF,
Packit 90a5c9
                     "Set various timeout parameters for reading request "
Packit 90a5c9
                     "headers and body"),
Packit 90a5c9
    {NULL}
Packit 90a5c9
};
Packit 90a5c9
Packit 90a5c9
AP_DECLARE_MODULE(reqtimeout) = {
Packit 90a5c9
    STANDARD20_MODULE_STUFF,
Packit 90a5c9
    NULL,                           /* create per-dir config structures */
Packit 90a5c9
    NULL,                           /* merge  per-dir config structures */
Packit 90a5c9
    reqtimeout_create_srv_config,   /* create per-server config structures */
Packit 90a5c9
    reqtimeout_merge_srv_config,    /* merge per-server config structures */
Packit 90a5c9
    reqtimeout_cmds,                /* table of config file commands */
Packit 90a5c9
    reqtimeout_hooks
Packit 90a5c9
};