1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include "http_config.h"
19 #include "http_request.h"
20 #include "http_connection.h"
21 #include "http_protocol.h"
23 #include "http_core.h"
24 #include "util_filter.h"
25 #define APR_WANT_STRFUNC
26 #include "apr_strings.h"
27 #include "apr_support.h"
29 module AP_MODULE_DECLARE_DATA reqtimeout_module;
33 int header_timeout; /* timeout for reading the req hdrs in secs */
34 int header_max_timeout; /* max timeout for req hdrs in secs */
35 int header_min_rate; /* min rate for reading req hdrs in bytes/s */
36 apr_time_t header_rate_factor;
37 int body_timeout; /* timeout for reading the req body in secs */
38 int body_max_timeout; /* max timeout for req body in secs */
39 int body_min_rate; /* timeout for reading the req body in secs */
40 apr_time_t body_rate_factor;
43 /* this struct is used both as conn_config and as filter context */
46 apr_time_t timeout_at;
47 apr_time_t max_timeout_at;
54 apr_time_t rate_factor;
55 apr_bucket_brigade *tmpbb;
58 static const char *const reqtimeout_filter_name = "reqtimeout";
60 static void extend_timeout(reqtimeout_con_cfg *ccfg, apr_bucket_brigade *bb)
63 apr_time_t new_timeout_at;
65 if (apr_brigade_length(bb, 0, &len) != APR_SUCCESS || len <= 0)
68 new_timeout_at = ccfg->timeout_at + len * ccfg->rate_factor;
69 if (ccfg->max_timeout_at > 0 && new_timeout_at > ccfg->max_timeout_at) {
70 ccfg->timeout_at = ccfg->max_timeout_at;
73 ccfg->timeout_at = new_timeout_at;
77 static apr_status_t check_time_left(reqtimeout_con_cfg *ccfg,
78 apr_time_t *time_left_p)
80 *time_left_p = ccfg->timeout_at - apr_time_now();
81 if (*time_left_p <= 0)
84 if (*time_left_p < apr_time_from_sec(1)) {
85 *time_left_p = apr_time_from_sec(1);
90 static apr_status_t have_lf_or_eos(apr_bucket_brigade *bb)
92 apr_bucket *b = APR_BRIGADE_LAST(bb);
94 for ( ; b != APR_BRIGADE_SENTINEL(bb) ; b = APR_BUCKET_PREV(b) ) {
99 if (APR_BUCKET_IS_EOS(b))
102 if (APR_BUCKET_IS_METADATA(b))
105 rv = apr_bucket_read(b, &str, &len, APR_BLOCK_READ);
106 if (rv != APR_SUCCESS)
112 if (str[len-1] == APR_ASCII_LF)
115 return APR_INCOMPLETE;
119 #define MIN(x,y) ((x) < (y) ? (x) : (y))
120 static apr_status_t reqtimeout_filter(ap_filter_t *f,
121 apr_bucket_brigade *bb,
122 ap_input_mode_t mode,
123 apr_read_type_e block,
126 apr_time_t time_left;
129 apr_interval_time_t saved_sock_timeout = -1;
130 reqtimeout_con_cfg *ccfg = f->ctx;
132 if (ccfg->in_keep_alive) {
133 /* For this read, the normal keep-alive timeout must be used */
134 ccfg->in_keep_alive = 0;
135 return ap_get_brigade(f->next, bb, mode, block, readbytes);
138 now = apr_time_now();
139 if (ccfg->new_timeout > 0) {
140 /* set new timeout */
141 ccfg->timeout_at = now + apr_time_from_sec(ccfg->new_timeout);
142 ccfg->new_timeout = 0;
143 if (ccfg->new_max_timeout > 0) {
144 ccfg->max_timeout_at = now + apr_time_from_sec(ccfg->new_max_timeout);
145 ccfg->new_max_timeout = 0;
148 else if (ccfg->timeout_at == 0) {
150 return ap_get_brigade(f->next, bb, mode, block, readbytes);
154 core_net_rec *net_rec;
155 ap_filter_t *core_in = f->next;
157 while (core_in && core_in->frec != ap_core_input_filter_handle)
158 core_in = core_in->next;
161 ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, f->c,
162 "mod_reqtimeout: Can't get socket "
163 "handle from core_input_filter");
164 ap_remove_input_filter(f);
165 return ap_get_brigade(f->next, bb, mode, block, readbytes);
167 net_rec = core_in->ctx;
168 ccfg->socket = net_rec->client_socket;
171 rv = check_time_left(ccfg, &time_left);
172 if (rv != APR_SUCCESS)
175 if (block == APR_NONBLOCK_READ || mode == AP_MODE_INIT
176 || mode == AP_MODE_EATCRLF) {
177 rv = ap_get_brigade(f->next, bb, mode, block, readbytes);
178 if (ccfg->min_rate > 0 && rv == APR_SUCCESS) {
179 extend_timeout(ccfg, bb);
184 rv = apr_socket_timeout_get(ccfg->socket, &saved_sock_timeout);
185 AP_DEBUG_ASSERT(rv == APR_SUCCESS);
187 rv = apr_socket_timeout_set(ccfg->socket, MIN(time_left, saved_sock_timeout));
188 AP_DEBUG_ASSERT(rv == APR_SUCCESS);
190 if (mode == AP_MODE_GETLINE) {
192 * For a blocking AP_MODE_GETLINE read, apr_brigade_split_line()
193 * would loop until a whole line has been read. As this would make it
194 * impossible to enforce a total timeout, we only do non-blocking
197 apr_size_t remaining = HUGE_STRING_LEN;
201 rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE, APR_NONBLOCK_READ, remaining);
202 if (APR_STATUS_IS_EAGAIN(rv)) {
205 else if (rv != APR_SUCCESS) {
209 if (!APR_BRIGADE_EMPTY(bb)) {
210 rv = have_lf_or_eos(bb);
211 if (rv != APR_INCOMPLETE) {
215 if (ccfg->min_rate > 0) {
216 extend_timeout(ccfg, bb);
219 rv = apr_brigade_length(bb, 1, &bblen);
220 if (rv != APR_SUCCESS) {
224 if (remaining <= 0) {
228 /* Haven't got a whole line yet, save what we have ... */
230 ccfg->tmpbb = apr_brigade_create(f->c->pool, f->c->bucket_alloc);
232 APR_BRIGADE_CONCAT(ccfg->tmpbb, bb);
235 /* ... and wait for more */
236 rv = apr_wait_for_io_or_timeout(NULL, ccfg->socket, 1);
237 if (rv != APR_SUCCESS)
240 rv = check_time_left(ccfg, &time_left);
241 if (rv != APR_SUCCESS)
244 rv = apr_socket_timeout_set(ccfg->socket,
245 MIN(time_left, saved_sock_timeout));
246 AP_DEBUG_ASSERT(rv == APR_SUCCESS);
251 APR_BRIGADE_PREPEND(bb, ccfg->tmpbb);
255 /* mode != AP_MODE_GETLINE */
256 rv = ap_get_brigade(f->next, bb, mode, block, readbytes);
257 if (ccfg->min_rate > 0 && rv == APR_SUCCESS) {
258 extend_timeout(ccfg, bb);
262 apr_socket_timeout_set(ccfg->socket, saved_sock_timeout);
265 if (rv == APR_TIMEUP) {
266 ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, f->c,
267 "Request %s read timeout", ccfg->type);
269 * If we allow lingering close, the client may keep this
270 * process/thread busy for another 30s (MAX_SECS_TO_LINGER).
271 * Therefore we have to abort the connection. The downside is
272 * that the client will most likely not receive the error
280 static int reqtimeout_init(conn_rec *c)
282 reqtimeout_con_cfg *ccfg;
283 reqtimeout_srv_cfg *cfg;
285 cfg = ap_get_module_config(c->base_server->module_config,
287 AP_DEBUG_ASSERT(cfg != NULL);
288 if (cfg->header_timeout <= 0 && cfg->body_timeout <= 0) {
289 /* not configured for this vhost */
293 ccfg = apr_pcalloc(c->pool, sizeof(reqtimeout_con_cfg));
294 ccfg->new_timeout = cfg->header_timeout;
295 ccfg->new_max_timeout = cfg->header_max_timeout;
296 ccfg->type = "header";
297 ccfg->min_rate = cfg->header_min_rate;
298 ccfg->rate_factor = cfg->header_rate_factor;
299 ap_set_module_config(c->conn_config, &reqtimeout_module, ccfg);
301 ap_add_input_filter("reqtimeout", ccfg, NULL, c);
302 /* we are not handling the connection, we just do initialization */
306 static int reqtimeout_after_headers(request_rec *r)
308 reqtimeout_srv_cfg *cfg;
309 reqtimeout_con_cfg *ccfg =
310 ap_get_module_config(r->connection->conn_config, &reqtimeout_module);
313 /* not configured for this connection */
317 cfg = ap_get_module_config(r->connection->base_server->module_config,
319 AP_DEBUG_ASSERT(cfg != NULL);
321 ccfg->timeout_at = 0;
322 ccfg->max_timeout_at = 0;
323 ccfg->new_timeout = cfg->body_timeout;
324 ccfg->new_max_timeout = cfg->body_max_timeout;
325 ccfg->min_rate = cfg->body_min_rate;
326 ccfg->rate_factor = cfg->body_rate_factor;
332 static int reqtimeout_after_body(request_rec *r)
334 reqtimeout_srv_cfg *cfg;
335 reqtimeout_con_cfg *ccfg =
336 ap_get_module_config(r->connection->conn_config, &reqtimeout_module);
339 /* not configured for this connection */
343 cfg = ap_get_module_config(r->connection->base_server->module_config,
345 AP_DEBUG_ASSERT(cfg != NULL);
347 ccfg->timeout_at = 0;
348 ccfg->max_timeout_at = 0;
349 ccfg->in_keep_alive = 1;
350 ccfg->new_timeout = cfg->header_timeout;
351 ccfg->new_max_timeout = cfg->header_max_timeout;
352 ccfg->min_rate = cfg->header_min_rate;
353 ccfg->rate_factor = cfg->header_rate_factor;
355 ccfg->type = "header";
360 static void *reqtimeout_create_srv_config(apr_pool_t *p, server_rec *s)
362 reqtimeout_srv_cfg *cfg = apr_pcalloc(p, sizeof(reqtimeout_srv_cfg));
364 cfg->header_timeout = -1;
365 cfg->header_max_timeout = -1;
366 cfg->header_min_rate = -1;
367 cfg->body_timeout = -1;
368 cfg->body_max_timeout = -1;
369 cfg->body_min_rate = -1;
374 #define MERGE_INT(cfg, b, a, val) cfg->val = (a->val == -1) ? b->val : a->val;
375 static void *reqtimeout_merge_srv_config(apr_pool_t *p, void *base_, void *add_)
377 reqtimeout_srv_cfg *base = base_;
378 reqtimeout_srv_cfg *add = add_;
379 reqtimeout_srv_cfg *cfg = apr_pcalloc(p, sizeof(reqtimeout_srv_cfg));
381 MERGE_INT(cfg, base, add, header_timeout);
382 MERGE_INT(cfg, base, add, header_max_timeout);
383 MERGE_INT(cfg, base, add, header_min_rate);
384 MERGE_INT(cfg, base, add, body_timeout);
385 MERGE_INT(cfg, base, add, body_max_timeout);
386 MERGE_INT(cfg, base, add, body_min_rate);
388 cfg->header_rate_factor = (cfg->header_min_rate == -1) ? base->header_rate_factor :
389 add->header_rate_factor;
390 cfg->body_rate_factor = (cfg->body_min_rate == -1) ? base->body_rate_factor :
391 add->body_rate_factor;
396 static const char *parse_int(apr_pool_t *p, const char *arg, int *val) {
398 *val = strtol(arg, &endptr, 10);
401 return apr_psprintf(p, "Value '%s' not numerical", endptr);
403 if (*endptr != '\0') {
404 return apr_psprintf(p, "Cannot parse '%s'", endptr);
407 return "Value must be non-negative";
412 static const char *set_reqtimeout_param(reqtimeout_srv_cfg *conf,
417 const char *ret = NULL;
418 char *rate_str = NULL, *initial_str, *max_str = NULL;
419 int rate = 0, initial = 0, max = 0;
420 enum { PARAM_HEADER, PARAM_BODY } type;
422 if (!strcasecmp(key, "header")) {
425 else if (!strcasecmp(key, "body")) {
429 return "Unknown RequestReadTimeout parameter";
432 if ((rate_str = ap_strcasestr(val, ",minrate="))) {
433 initial_str = apr_pstrndup(p, val, rate_str - val);
434 rate_str += strlen(",minrate=");
435 ret = parse_int(p, rate_str, &rate);
440 return "Minimum data rate must be larger than 0";
442 if ((max_str = strchr(initial_str, '-'))) {
444 ret = parse_int(p, max_str, &max);
449 ret = parse_int(p, initial_str, &initial);
452 if (ap_strchr_c(val, '-'))
453 return "Must set MinRate option if using timeout range";
454 ret = parse_int(p, val, &initial);
460 if (max && initial >= max) {
461 return "Maximum timeout must be larger than initial timeout";
464 if (type == PARAM_HEADER) {
465 conf->header_timeout = initial;
466 conf->header_max_timeout = max;
467 conf->header_min_rate = rate;
469 conf->header_rate_factor = apr_time_from_sec(1) / rate;
472 conf->body_timeout = initial;
473 conf->body_max_timeout = max;
474 conf->body_min_rate = rate;
476 conf->body_rate_factor = apr_time_from_sec(1) / rate;
481 static const char *set_reqtimeouts(cmd_parms *cmd, void *mconfig,
484 reqtimeout_srv_cfg *conf =
485 ap_get_module_config(cmd->server->module_config,
492 word = ap_getword_conf(cmd->pool, &arg);
493 val = strchr(word, '=');
495 return "Invalid RequestReadTimeout parameter. Parameter must be "
496 "in the form 'key=value'";
501 err = set_reqtimeout_param(conf, cmd->pool, word, val);
504 return apr_psprintf(cmd->temp_pool, "RequestReadTimeout: %s=%s: %s",
512 static void reqtimeout_hooks(apr_pool_t *pool)
515 * mod_ssl is AP_FTYPE_CONNECTION + 5 and mod_reqtimeout needs to
516 * be called before mod_ssl. Otherwise repeated reads during the ssl
517 * handshake can prevent the timeout from triggering.
519 ap_register_input_filter(reqtimeout_filter_name, reqtimeout_filter, NULL,
520 AP_FTYPE_CONNECTION + 8);
523 * mod_reqtimeout needs to be called before ap_process_http_request (which
524 * is run at APR_HOOK_REALLY_LAST) but after all other protocol modules.
525 * This ensures that it only influences normal http connections and not
526 * e.g. mod_ftp. Also, if mod_reqtimeout used the pre_connection hook, it
527 * would be inserted on mod_proxy's backend connections.
529 ap_hook_process_connection(reqtimeout_init, NULL, NULL, APR_HOOK_LAST);
531 ap_hook_post_read_request(reqtimeout_after_headers, NULL, NULL,
533 ap_hook_log_transaction(reqtimeout_after_body, NULL, NULL,
537 static const command_rec reqtimeout_cmds[] = {
538 AP_INIT_RAW_ARGS("RequestReadTimeout", set_reqtimeouts, NULL, RSRC_CONF,
539 "Set various timeout parameters for reading request "
544 module AP_MODULE_DECLARE_DATA reqtimeout_module = {
545 STANDARD20_MODULE_STUFF,
546 NULL, /* create per-dir config structures */
547 NULL, /* merge per-dir config structures */
548 reqtimeout_create_srv_config, /* create per-server config structures */
549 reqtimeout_merge_srv_config, /* merge per-server config structures */
550 reqtimeout_cmds, /* table of config file commands */