mod_reqtimeout.c revision 5efbcd7a870805de12f785ea1ef1c189e8255ba1
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf/* Licensed to the Apache Software Foundation (ASF) under one or more
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf * contributor license agreements. See the NOTICE file distributed with
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf * this work for additional information regarding copyright ownership.
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf * The ASF licenses this file to You under the Apache License, Version 2.0
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf * (the "License"); you may not use this file except in compliance with
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf * the License. You may obtain a copy of the License at
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf * Unless required by applicable law or agreed to in writing, software
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf * distributed under the License is distributed on an "AS IS" BASIS,
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf * See the License for the specific language governing permissions and
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf * limitations under the License.
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsftypedef struct
3231873d0ba8064a1c90307d810a83ec01a88675jim int header_timeout; /* timeout for reading the req hdrs in secs */
3231873d0ba8064a1c90307d810a83ec01a88675jim int header_max_timeout; /* max timeout for req hdrs in secs */
3231873d0ba8064a1c90307d810a83ec01a88675jim int header_min_rate; /* min rate for reading req hdrs in bytes/s */
3231873d0ba8064a1c90307d810a83ec01a88675jim int body_timeout; /* timeout for reading the req body in secs */
3231873d0ba8064a1c90307d810a83ec01a88675jim int body_max_timeout; /* max timeout for req body in secs */
b92eb1e3d9db61b8795f08da9f0dff400ee258a8rjung int body_min_rate; /* min rate for reading req body in bytes/s */
14e5a8cc15b1dcc26ad5420973304e53a9e5406bsf/* this struct is used both as conn_config and as filter context */
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsftypedef struct
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsfstatic const char *const reqtimeout_filter_name = "reqtimeout";
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsfstatic void extend_timeout(reqtimeout_con_cfg *ccfg, apr_bucket_brigade *bb)
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf if (apr_brigade_length(bb, 0, &len) != APR_SUCCESS || len <= 0)
a4a16f26af59370661ea5f890502cf32146e0947jim new_timeout_at = ccfg->timeout_at + len * ccfg->rate_factor;
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf if (ccfg->max_timeout_at > 0 && new_timeout_at > ccfg->max_timeout_at) {
8973e168b1301132b47bc718d75013ee35f49c2csfstatic apr_status_t check_time_left(reqtimeout_con_cfg *ccfg,
8973e168b1301132b47bc718d75013ee35f49c2csfstatic apr_status_t have_lf_or_eos(apr_bucket_brigade *bb)
8973e168b1301132b47bc718d75013ee35f49c2csf for ( ; b != APR_BRIGADE_SENTINEL(bb) ; b = APR_BUCKET_PREV(b) ) {
11e076839c8d5a82d55e710194d0daac51390dbdsf const char *str;
0ed19acadd3d3dd593759173d87d2243e97914e2sf * Append bbIn to bbOut and merge small buckets, to avoid DoS by high memory
0ed19acadd3d3dd593759173d87d2243e97914e2sfstatic apr_status_t brigade_append(apr_bucket_brigade *bbOut, apr_bucket_brigade *bbIn)
0ed19acadd3d3dd593759173d87d2243e97914e2sf const char *str;
0ed19acadd3d3dd593759173d87d2243e97914e2sf if (APR_BUCKET_IS_METADATA(e) || len > APR_BUCKET_BUFF_SIZE/4) {
0ed19acadd3d3dd593759173d87d2243e97914e2sf if (len > 0) {
0e93b5235b6c7a5e03eee9c9000eab0751904dcaylavic#define MIN(x,y) ((x) < (y) ? (x) : (y))
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf /* For this read, the normal keep-alive timeout must be used */
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf return ap_get_brigade(f->next, bb, mode, block, readbytes);
d90ed788b710beb887eb410a34bf8b035d611280covener if (block == APR_NONBLOCK_READ && mode == AP_MODE_SPECULATIVE) {
d90ed788b710beb887eb410a34bf8b035d611280covener /* The source of these above us in the core is check_pipeline(), which
d90ed788b710beb887eb410a34bf8b035d611280covener * is between requests but before this filter knows to reset timeouts
d90ed788b710beb887eb410a34bf8b035d611280covener * during log_transaction(). If they appear elsewhere, just don't
d90ed788b710beb887eb410a34bf8b035d611280covener * check or extend the time since they won't block and we'll see the
d90ed788b710beb887eb410a34bf8b035d611280covener * bytes again later
d90ed788b710beb887eb410a34bf8b035d611280covener return ap_get_brigade(f->next, bb, mode, block, readbytes);
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf /* set new timeout */
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf ccfg->timeout_at = now + apr_time_from_sec(ccfg->new_timeout);
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf ccfg->max_timeout_at = now + apr_time_from_sec(ccfg->new_max_timeout);
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf /* no timeout set */
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf return ap_get_brigade(f->next, bb, mode, block, readbytes);
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf rv = ap_get_brigade(f->next, bb, mode, block, readbytes);
14e5a8cc15b1dcc26ad5420973304e53a9e5406bsf rv = apr_socket_timeout_get(ccfg->socket, &saved_sock_timeout);
0e93b5235b6c7a5e03eee9c9000eab0751904dcaylavic rv = apr_socket_timeout_set(ccfg->socket, MIN(time_left, saved_sock_timeout));
8973e168b1301132b47bc718d75013ee35f49c2csf * For a blocking AP_MODE_GETLINE read, apr_brigade_split_line()
8973e168b1301132b47bc718d75013ee35f49c2csf * would loop until a whole line has been read. As this would make it
8973e168b1301132b47bc718d75013ee35f49c2csf * impossible to enforce a total timeout, we only do non-blocking
8973e168b1301132b47bc718d75013ee35f49c2csf rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE, APR_NONBLOCK_READ, remaining);
8973e168b1301132b47bc718d75013ee35f49c2csf if (remaining <= 0) {
8973e168b1301132b47bc718d75013ee35f49c2csf /* Haven't got a whole line yet, save what we have ... */
8973e168b1301132b47bc718d75013ee35f49c2csf ccfg->tmpbb = apr_brigade_create(f->c->pool, f->c->bucket_alloc);
8973e168b1301132b47bc718d75013ee35f49c2csf /* ... and wait for more */
8973e168b1301132b47bc718d75013ee35f49c2csf } while (1);
8973e168b1301132b47bc718d75013ee35f49c2csf /* mode != AP_MODE_GETLINE */
8973e168b1301132b47bc718d75013ee35f49c2csf rv = ap_get_brigade(f->next, bb, mode, block, readbytes);
5efbcd7a870805de12f785ea1ef1c189e8255ba1ylavic /* Don't extend the timeout in speculative mode, wait for
5efbcd7a870805de12f785ea1ef1c189e8255ba1ylavic * the real (relevant) bytes to be asked later, within the
5efbcd7a870805de12f785ea1ef1c189e8255ba1ylavic * currently alloted time.
0e93b5235b6c7a5e03eee9c9000eab0751904dcaylavic apr_socket_timeout_set(ccfg->socket, saved_sock_timeout);
185aa71728867671e105178b4c66fbc22b65ae26sf ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, f->c, APLOGNO(01382)
5eabcf9bc3875db35c0e62a2202d63768c10168dsf * If we allow a normal lingering close, the client may keep this
8973e168b1301132b47bc718d75013ee35f49c2csf * process/thread busy for another 30s (MAX_SECS_TO_LINGER).
5eabcf9bc3875db35c0e62a2202d63768c10168dsf * Therefore we tell ap_lingering_close() to shorten this period to
5eabcf9bc3875db35c0e62a2202d63768c10168dsf * 2s (SECONDS_TO_LINGER).
5eabcf9bc3875db35c0e62a2202d63768c10168dsf apr_table_setn(f->c->notes, "short-lingering-close", "1");
b3e63c395d671f14a096d7e888dbfd2caf93a663sf * Also, we must not allow keep-alive requests, as
b3e63c395d671f14a096d7e888dbfd2caf93a663sf * ap_finalize_protocol() may ignore our error status (if the timeout
b3e63c395d671f14a096d7e888dbfd2caf93a663sf * happened on a request body that is discarded).
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf cfg = ap_get_module_config(c->base_server->module_config,
69a4ff202d78f0a69cd87cb0edc9964b2db4946csf if (cfg->header_timeout == 0 && cfg->body_timeout == 0) {
69a4ff202d78f0a69cd87cb0edc9964b2db4946csf /* disabled for this vhost */
6029353b43240f5fa7feede018ce0e3ab1600c96covener ccfg = ap_get_module_config(c->conn_config, &reqtimeout_module);
6029353b43240f5fa7feede018ce0e3ab1600c96covener ccfg = apr_pcalloc(c->pool, sizeof(reqtimeout_con_cfg));
6029353b43240f5fa7feede018ce0e3ab1600c96covener ap_set_module_config(c->conn_config, &reqtimeout_module, ccfg);
6029353b43240f5fa7feede018ce0e3ab1600c96covener ap_add_input_filter(reqtimeout_filter_name, ccfg, NULL, c);
6029353b43240f5fa7feede018ce0e3ab1600c96covener /* subsequent request under event-like MPM */
14e5a8cc15b1dcc26ad5420973304e53a9e5406bsf /* we are not handling the connection, we just do initialization */
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf ap_get_module_config(r->connection->conn_config, &reqtimeout_module);
69a4ff202d78f0a69cd87cb0edc9964b2db4946csf /* either disabled for this connection or a CONNECT request */
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf cfg = ap_get_module_config(r->connection->base_server->module_config,
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf ap_get_module_config(r->connection->conn_config, &reqtimeout_module);
14e5a8cc15b1dcc26ad5420973304e53a9e5406bsf /* not configured for this connection */
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf cfg = ap_get_module_config(r->connection->base_server->module_config,
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsfstatic void *reqtimeout_create_srv_config(apr_pool_t *p, server_rec *s)
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf reqtimeout_srv_cfg *cfg = apr_pcalloc(p, sizeof(reqtimeout_srv_cfg));
69a4ff202d78f0a69cd87cb0edc9964b2db4946csf#define MERGE_INT(cfg, b, a, val) cfg->val = (a->val == UNSET) ? b->val : a->val;
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsfstatic void *reqtimeout_merge_srv_config(apr_pool_t *p, void *base_, void *add_)
a4a16f26af59370661ea5f890502cf32146e0947jim reqtimeout_srv_cfg *cfg = apr_pcalloc(p, sizeof(reqtimeout_srv_cfg));
69a4ff202d78f0a69cd87cb0edc9964b2db4946csf cfg->header_rate_factor = (cfg->header_min_rate == UNSET) ?
53ead8d24845d1e32907c37d1fe58ecf79fb9e50sfstatic const char *parse_int(apr_pool_t *p, const char *arg, int *val) {
53ead8d24845d1e32907c37d1fe58ecf79fb9e50sf return apr_psprintf(p, "Value '%s' not numerical", endptr);
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf if (*val < 0) {
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf return "Value must be non-negative";
3231873d0ba8064a1c90307d810a83ec01a88675jimstatic const char *set_reqtimeout_param(reqtimeout_srv_cfg *conf,
3231873d0ba8064a1c90307d810a83ec01a88675jim const char *key,
3231873d0ba8064a1c90307d810a83ec01a88675jim const char *val)
53ead8d24845d1e32907c37d1fe58ecf79fb9e50sf return "Unknown RequestReadTimeout parameter";
53ead8d24845d1e32907c37d1fe58ecf79fb9e50sf return "Minimum data rate must be larger than 0";
53ead8d24845d1e32907c37d1fe58ecf79fb9e50sf return "Must set MinRate option if using timeout range";
53ead8d24845d1e32907c37d1fe58ecf79fb9e50sf return "Maximum timeout must be larger than initial timeout";
3231873d0ba8064a1c90307d810a83ec01a88675jimstatic const char *set_reqtimeouts(cmd_parms *cmd, void *mconfig,
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf const char *arg)
3231873d0ba8064a1c90307d810a83ec01a88675jim while (*arg) {
3231873d0ba8064a1c90307d810a83ec01a88675jim const char *err;
53ead8d24845d1e32907c37d1fe58ecf79fb9e50sf return "Invalid RequestReadTimeout parameter. Parameter must be "
3231873d0ba8064a1c90307d810a83ec01a88675jim "in the form 'key=value'";
53ead8d24845d1e32907c37d1fe58ecf79fb9e50sf return apr_psprintf(cmd->temp_pool, "RequestReadTimeout: %s=%s: %s",
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf * mod_ssl is AP_FTYPE_CONNECTION + 5 and mod_reqtimeout needs to
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf * be called before mod_ssl. Otherwise repeated reads during the ssl
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf * handshake can prevent the timeout from triggering.
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf ap_register_input_filter(reqtimeout_filter_name, reqtimeout_filter, NULL,
14e5a8cc15b1dcc26ad5420973304e53a9e5406bsf * mod_reqtimeout needs to be called before ap_process_http_request (which
14e5a8cc15b1dcc26ad5420973304e53a9e5406bsf * is run at APR_HOOK_REALLY_LAST) but after all other protocol modules.
14e5a8cc15b1dcc26ad5420973304e53a9e5406bsf * This ensures that it only influences normal http connections and not
14e5a8cc15b1dcc26ad5420973304e53a9e5406bsf * e.g. mod_ftp. Also, if mod_reqtimeout used the pre_connection hook, it
14e5a8cc15b1dcc26ad5420973304e53a9e5406bsf * would be inserted on mod_proxy's backend connections.
14e5a8cc15b1dcc26ad5420973304e53a9e5406bsf ap_hook_process_connection(reqtimeout_init, NULL, NULL, APR_HOOK_LAST);
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf ap_hook_post_read_request(reqtimeout_after_headers, NULL, NULL,
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf ap_hook_log_transaction(reqtimeout_after_body, NULL, NULL,
69a4ff202d78f0a69cd87cb0edc9964b2db4946csf default_header_rate_factor = apr_time_from_sec(1) / MRT_DEFAULT_HEADER_MIN_RATE;
69a4ff202d78f0a69cd87cb0edc9964b2db4946csf default_body_rate_factor = apr_time_from_sec(1) / MRT_DEFAULT_BODY_MIN_RATE;
53ead8d24845d1e32907c37d1fe58ecf79fb9e50sf AP_INIT_RAW_ARGS("RequestReadTimeout", set_reqtimeouts, NULL, RSRC_CONF,
53ead8d24845d1e32907c37d1fe58ecf79fb9e50sf "Set various timeout parameters for reading request "
53ead8d24845d1e32907c37d1fe58ecf79fb9e50sf "headers and body"),
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf reqtimeout_create_srv_config, /* create per-server config structures */
4dee28b6fc8fff5efde4e7821aeb6defed3fb84dsf reqtimeout_merge_srv_config, /* merge per-server config structures */