]> granicus.if.org Git - apache/blob - modules/filters/mod_reqtimeout.c
Rename ap_casecmpstr[n]() to ap_cstr_casecmp[n](), update with APR doxygen
[apache] / modules / filters / mod_reqtimeout.c
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include "httpd.h"
18 #include "http_config.h"
19 #include "http_request.h"
20 #include "http_connection.h"
21 #include "http_protocol.h"
22 #include "http_log.h"
23 #include "http_core.h"
24 #include "util_filter.h"
25 #define APR_WANT_STRFUNC
26 #include "apr_strings.h"
27 #include "apr_version.h"
28
29 module AP_MODULE_DECLARE_DATA reqtimeout_module;
30
31 #define UNSET                            -1
32 #define MRT_DEFAULT_HEADER_TIMEOUT       20
33 #define MRT_DEFAULT_HEADER_MAX_TIMEOUT   40
34 #define MRT_DEFAULT_HEADER_MIN_RATE      500
35 #define MRT_DEFAULT_BODY_TIMEOUT         20
36 #define MRT_DEFAULT_BODY_MAX_TIMEOUT     0
37 #define MRT_DEFAULT_BODY_MIN_RATE        500
38
39 typedef struct
40 {
41     int header_timeout;     /* timeout for reading the req hdrs in secs */
42     int header_max_timeout; /* max timeout for req hdrs in secs */
43     int header_min_rate;    /* min rate for reading req hdrs in bytes/s */
44     apr_time_t header_rate_factor;
45     int body_timeout;       /* timeout for reading the req body in secs */
46     int body_max_timeout;   /* max timeout for req body in secs */
47     int body_min_rate;      /* min rate for reading req body in bytes/s */
48     apr_time_t body_rate_factor;
49 } reqtimeout_srv_cfg;
50
51 /* this struct is used both as conn_config and as filter context */
52 typedef struct
53 {
54     apr_time_t timeout_at;
55     apr_time_t max_timeout_at;
56     int min_rate;
57     int new_timeout;
58     int new_max_timeout;
59     int in_keep_alive;
60     char *type;
61     apr_socket_t *socket;
62     apr_time_t rate_factor;
63     apr_bucket_brigade *tmpbb;
64 } reqtimeout_con_cfg;
65
66 static const char *const reqtimeout_filter_name = "reqtimeout";
67 static int default_header_rate_factor;
68 static int default_body_rate_factor;
69
70 static void extend_timeout(reqtimeout_con_cfg *ccfg, apr_bucket_brigade *bb)
71 {
72     apr_off_t len;
73     apr_time_t new_timeout_at;
74
75     if (apr_brigade_length(bb, 0, &len) != APR_SUCCESS || len <= 0)
76         return;
77
78     new_timeout_at = ccfg->timeout_at + len * ccfg->rate_factor;
79     if (ccfg->max_timeout_at > 0 && new_timeout_at > ccfg->max_timeout_at) {
80         ccfg->timeout_at = ccfg->max_timeout_at;
81     }
82     else {
83         ccfg->timeout_at = new_timeout_at;
84     }
85 }
86
87 static apr_status_t check_time_left(reqtimeout_con_cfg *ccfg,
88                                     apr_time_t *time_left_p,
89                                     apr_time_t now)
90 {
91     if (!now)
92         now = apr_time_now();
93     *time_left_p = ccfg->timeout_at - now;
94     if (*time_left_p <= 0)
95         return APR_TIMEUP;
96
97     if (*time_left_p < apr_time_from_sec(1)) {
98         *time_left_p = apr_time_from_sec(1);
99     }
100     return APR_SUCCESS;
101 }
102
103 static apr_status_t have_lf_or_eos(apr_bucket_brigade *bb)
104 {
105     apr_bucket *b = APR_BRIGADE_LAST(bb);
106
107     for ( ; b != APR_BRIGADE_SENTINEL(bb) ; b = APR_BUCKET_PREV(b) ) {
108         const char *str;
109         apr_size_t len;
110         apr_status_t rv;
111
112         if (APR_BUCKET_IS_EOS(b))
113             return APR_SUCCESS;
114
115         if (APR_BUCKET_IS_METADATA(b))
116             continue;
117
118         rv = apr_bucket_read(b, &str, &len, APR_BLOCK_READ);
119         if (rv != APR_SUCCESS)
120             return rv;
121
122         if (len == 0)
123             continue;
124
125         if (str[len-1] == APR_ASCII_LF)
126             return APR_SUCCESS;
127     }
128     return APR_INCOMPLETE;
129 }
130
131 /*
132  * Append bbIn to bbOut and merge small buckets, to avoid DoS by high memory
133  * usage
134  */
135 static apr_status_t brigade_append(apr_bucket_brigade *bbOut, apr_bucket_brigade *bbIn)
136 {
137     while (!APR_BRIGADE_EMPTY(bbIn)) {
138         apr_bucket *e = APR_BRIGADE_FIRST(bbIn);
139         const char *str;
140         apr_size_t len;
141         apr_status_t rv;
142
143         rv = apr_bucket_read(e, &str, &len, APR_BLOCK_READ);
144         if (rv != APR_SUCCESS) {
145             return rv;
146         }
147
148         APR_BUCKET_REMOVE(e);
149         if (APR_BUCKET_IS_METADATA(e) || len > APR_BUCKET_BUFF_SIZE/4) {
150             APR_BRIGADE_INSERT_TAIL(bbOut, e);
151         }
152         else {
153             if (len > 0) {
154                 rv = apr_brigade_write(bbOut, NULL, NULL, str, len);
155                 if (rv != APR_SUCCESS) {
156                     apr_bucket_destroy(e);
157                     return rv;
158                 }
159             }
160             apr_bucket_destroy(e);
161         }
162     }
163     return APR_SUCCESS;
164 }
165
166
167 #define MIN(x,y) ((x) < (y) ? (x) : (y))
168 static apr_status_t reqtimeout_filter(ap_filter_t *f,
169                                       apr_bucket_brigade *bb,
170                                       ap_input_mode_t mode,
171                                       apr_read_type_e block,
172                                       apr_off_t readbytes)
173 {
174     apr_time_t time_left;
175     apr_time_t now = 0;
176     apr_status_t rv;
177     apr_interval_time_t saved_sock_timeout = UNSET;
178     reqtimeout_con_cfg *ccfg = f->ctx;
179
180     if (ccfg->in_keep_alive) {
181         /* For this read[_request line()], wait for the first byte using the
182          * normal keep-alive timeout (hence don't take this expected idle time
183          * into account to setup the connection expiry below).
184          */
185         ccfg->in_keep_alive = 0;
186         rv = ap_get_brigade(f->next, bb, AP_MODE_SPECULATIVE, block, 1);
187         if (rv != APR_SUCCESS || APR_BRIGADE_EMPTY(bb)) {
188             return rv;
189         }
190         apr_brigade_cleanup(bb);
191     }
192
193     if (ccfg->new_timeout > 0) {
194         /* set new timeout */
195         now = apr_time_now();
196         ccfg->timeout_at = now + apr_time_from_sec(ccfg->new_timeout);
197         ccfg->new_timeout = 0;
198         if (ccfg->new_max_timeout > 0) {
199             ccfg->max_timeout_at = now + apr_time_from_sec(ccfg->new_max_timeout);
200             ccfg->new_max_timeout = 0;
201         }
202     }
203     else if (ccfg->timeout_at == 0) {
204         /* no timeout set, or in between requests */
205         return ap_get_brigade(f->next, bb, mode, block, readbytes);
206     }
207
208     if (!ccfg->socket) {
209         ccfg->socket = ap_get_conn_socket(f->c);
210     }
211
212     rv = check_time_left(ccfg, &time_left, now);
213     if (rv != APR_SUCCESS)
214         goto out;
215
216     if (block == APR_NONBLOCK_READ || mode == AP_MODE_INIT
217         || mode == AP_MODE_EATCRLF) {
218         rv = ap_get_brigade(f->next, bb, mode, block, readbytes);
219         if (ccfg->min_rate > 0 && rv == APR_SUCCESS) {
220             extend_timeout(ccfg, bb);
221         }
222         return rv;
223     }
224
225     rv = apr_socket_timeout_get(ccfg->socket, &saved_sock_timeout);
226     AP_DEBUG_ASSERT(rv == APR_SUCCESS);
227
228     rv = apr_socket_timeout_set(ccfg->socket, MIN(time_left, saved_sock_timeout));
229     AP_DEBUG_ASSERT(rv == APR_SUCCESS);
230
231     if (mode == AP_MODE_GETLINE) {
232         /*
233          * For a blocking AP_MODE_GETLINE read, apr_brigade_split_line()
234          * would loop until a whole line has been read. As this would make it
235          * impossible to enforce a total timeout, we only do non-blocking
236          * reads.
237          */
238         apr_off_t remaining = HUGE_STRING_LEN;
239         do {
240             apr_off_t bblen;
241 #if APR_MAJOR_VERSION < 2
242             apr_int32_t nsds;
243             apr_interval_time_t poll_timeout;
244             apr_pollfd_t pollset;
245 #endif
246
247             rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE, APR_NONBLOCK_READ, remaining);
248             if (rv != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(rv)) {
249                 break;
250             }
251
252             if (!APR_BRIGADE_EMPTY(bb)) {
253                 if (ccfg->min_rate > 0) {
254                     extend_timeout(ccfg, bb);
255                 }
256
257                 rv = have_lf_or_eos(bb);
258                 if (rv != APR_INCOMPLETE) {
259                     break;
260                 }
261
262                 rv = apr_brigade_length(bb, 1, &bblen);
263                 if (rv != APR_SUCCESS) {
264                     break;
265                 }
266                 remaining -= bblen;
267                 if (remaining <= 0) {
268                     break;
269                 }
270
271                 /* Haven't got a whole line yet, save what we have ... */
272                 if (!ccfg->tmpbb) {
273                     ccfg->tmpbb = apr_brigade_create(f->c->pool, f->c->bucket_alloc);
274                 }
275                 rv = brigade_append(ccfg->tmpbb, bb);
276                 if (rv != APR_SUCCESS)
277                     break;
278             }
279
280             /* ... and wait for more */
281 #if APR_MAJOR_VERSION < 2
282             pollset.p = f->c->pool;
283             pollset.desc_type = APR_POLL_SOCKET;
284             pollset.reqevents = APR_POLLIN|APR_POLLHUP;
285             pollset.desc.s = ccfg->socket;
286             apr_socket_timeout_get(ccfg->socket, &poll_timeout);
287             rv = apr_poll(&pollset, 1, &nsds, poll_timeout);
288 #else
289             rv = apr_socket_wait(ccfg->socket, APR_WAIT_READ);
290 #endif
291             if (rv != APR_SUCCESS)
292                 break;
293
294             rv = check_time_left(ccfg, &time_left, 0);
295             if (rv != APR_SUCCESS)
296                 break;
297
298             rv = apr_socket_timeout_set(ccfg->socket,
299                                    MIN(time_left, saved_sock_timeout));
300             AP_DEBUG_ASSERT(rv == APR_SUCCESS);
301
302         } while (1);
303
304         if (ccfg->tmpbb)
305             APR_BRIGADE_PREPEND(bb, ccfg->tmpbb);
306
307     }
308     else { /* mode != AP_MODE_GETLINE */
309         rv = ap_get_brigade(f->next, bb, mode, block, readbytes);
310         /* Don't extend the timeout in speculative mode, wait for
311          * the real (relevant) bytes to be asked later, within the
312          * currently alloted time.
313          */
314         if (ccfg->min_rate > 0 && rv == APR_SUCCESS
315                 && mode != AP_MODE_SPECULATIVE) {
316             extend_timeout(ccfg, bb);
317         }
318     }
319
320     apr_socket_timeout_set(ccfg->socket, saved_sock_timeout);
321
322 out:
323     if (APR_STATUS_IS_TIMEUP(rv)) {
324         ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, f->c, APLOGNO(01382)
325                       "Request %s read timeout", ccfg->type);
326         /*
327          * If we allow a normal lingering close, the client may keep this
328          * process/thread busy for another 30s (MAX_SECS_TO_LINGER).
329          * Therefore we tell ap_lingering_close() to shorten this period to
330          * 2s (SECONDS_TO_LINGER).
331          */
332         apr_table_setn(f->c->notes, "short-lingering-close", "1");
333
334         /*
335          * Also, we must not allow keep-alive requests, as
336          * ap_finalize_protocol() may ignore our error status (if the timeout
337          * happened on a request body that is discarded).
338          */
339         f->c->keepalive = AP_CONN_CLOSE;
340     }
341     return rv;
342 }
343
344 static apr_status_t reqtimeout_eor(ap_filter_t *f, apr_bucket_brigade *bb)
345 {
346     if (!APR_BRIGADE_EMPTY(bb) && AP_BUCKET_IS_EOR(APR_BRIGADE_LAST(bb))) {
347         reqtimeout_con_cfg *ccfg = f->ctx;
348         ccfg->timeout_at = 0;
349     }
350     return ap_pass_brigade(f->next, bb);
351 }
352
353 static int reqtimeout_init(conn_rec *c)
354 {
355     reqtimeout_con_cfg *ccfg;
356     reqtimeout_srv_cfg *cfg;
357
358     cfg = ap_get_module_config(c->base_server->module_config,
359                                &reqtimeout_module);
360     AP_DEBUG_ASSERT(cfg != NULL);
361     if (cfg->header_timeout == 0 && cfg->body_timeout == 0) {
362         /* disabled for this vhost */
363         return DECLINED;
364     }
365
366     ccfg = ap_get_module_config(c->conn_config, &reqtimeout_module);
367     if (ccfg == NULL) {
368         ccfg = apr_pcalloc(c->pool, sizeof(reqtimeout_con_cfg));
369         ap_set_module_config(c->conn_config, &reqtimeout_module, ccfg);
370         ap_add_output_filter(reqtimeout_filter_name, ccfg, NULL, c);
371         ap_add_input_filter(reqtimeout_filter_name, ccfg, NULL, c);
372     }
373
374     /* we are not handling the connection, we just do initialization */
375     return DECLINED;
376 }
377
378 static void reqtimeout_before_header(request_rec *r, conn_rec *c)
379 {
380     reqtimeout_srv_cfg *cfg;
381     reqtimeout_con_cfg *ccfg =
382         ap_get_module_config(c->conn_config, &reqtimeout_module);
383
384     if (ccfg == NULL) {
385         /* not configured for this connection */
386         return;
387     }
388
389     cfg = ap_get_module_config(c->base_server->module_config,
390                                &reqtimeout_module);
391     AP_DEBUG_ASSERT(cfg != NULL);
392
393     /* (Re)set the state for this new request, but ccfg->socket and
394      * ccfg->tmpbb which have the lifetime of the connection.
395      */
396     ccfg->timeout_at = 0;
397     ccfg->max_timeout_at = 0;
398     ccfg->in_keep_alive = (c->keepalives > 0);
399     ccfg->type = "header";
400     if (cfg->header_timeout != UNSET) {
401         ccfg->new_timeout     = cfg->header_timeout;
402         ccfg->new_max_timeout = cfg->header_max_timeout;
403         ccfg->min_rate        = cfg->header_min_rate;
404         ccfg->rate_factor     = cfg->header_rate_factor;
405     }
406     else {
407         ccfg->new_timeout     = MRT_DEFAULT_HEADER_TIMEOUT;
408         ccfg->new_max_timeout = MRT_DEFAULT_HEADER_MAX_TIMEOUT;
409         ccfg->min_rate        = MRT_DEFAULT_HEADER_MIN_RATE;
410         ccfg->rate_factor     = default_header_rate_factor;
411     }
412 }
413
414 static int reqtimeout_before_body(request_rec *r)
415 {
416     reqtimeout_srv_cfg *cfg;
417     reqtimeout_con_cfg *ccfg =
418         ap_get_module_config(r->connection->conn_config, &reqtimeout_module);
419
420     if (ccfg == NULL || r->method_number == M_CONNECT) {
421         /* either disabled for this connection or a CONNECT request */
422         return OK;
423     }
424     cfg = ap_get_module_config(r->connection->base_server->module_config,
425                                &reqtimeout_module);
426     AP_DEBUG_ASSERT(cfg != NULL);
427
428     ccfg->timeout_at = 0;
429     ccfg->max_timeout_at = 0;
430     ccfg->type = "body";
431     if (cfg->body_timeout != UNSET) {
432         ccfg->new_timeout     = cfg->body_timeout;
433         ccfg->new_max_timeout = cfg->body_max_timeout;
434         ccfg->min_rate        = cfg->body_min_rate;
435         ccfg->rate_factor     = cfg->body_rate_factor;
436     }
437     else {
438         ccfg->new_timeout     = MRT_DEFAULT_BODY_TIMEOUT;
439         ccfg->new_max_timeout = MRT_DEFAULT_BODY_MAX_TIMEOUT;
440         ccfg->min_rate        = MRT_DEFAULT_BODY_MIN_RATE;
441         ccfg->rate_factor     = default_body_rate_factor;
442     }
443     return OK;
444 }
445
446 static void *reqtimeout_create_srv_config(apr_pool_t *p, server_rec *s)
447 {
448     reqtimeout_srv_cfg *cfg = apr_pcalloc(p, sizeof(reqtimeout_srv_cfg));
449
450     cfg->header_timeout = UNSET;
451     cfg->header_max_timeout = UNSET;
452     cfg->header_min_rate = UNSET;
453     cfg->body_timeout = UNSET;
454     cfg->body_max_timeout = UNSET;
455     cfg->body_min_rate = UNSET;
456
457     return cfg;
458 }
459
460 #define MERGE_INT(cfg, b, a, val) cfg->val = (a->val == UNSET) ? b->val : a->val;
461 static void *reqtimeout_merge_srv_config(apr_pool_t *p, void *base_, void *add_)
462 {
463     reqtimeout_srv_cfg *base = base_;
464     reqtimeout_srv_cfg *add  = add_;
465     reqtimeout_srv_cfg *cfg  = apr_pcalloc(p, sizeof(reqtimeout_srv_cfg));
466
467     MERGE_INT(cfg, base, add, header_timeout);
468     MERGE_INT(cfg, base, add, header_max_timeout);
469     MERGE_INT(cfg, base, add, header_min_rate);
470     MERGE_INT(cfg, base, add, body_timeout);
471     MERGE_INT(cfg, base, add, body_max_timeout);
472     MERGE_INT(cfg, base, add, body_min_rate);
473
474     cfg->header_rate_factor = (cfg->header_min_rate == UNSET) ?
475                               base->header_rate_factor : add->header_rate_factor;
476     cfg->body_rate_factor = (cfg->body_min_rate == UNSET) ?
477                             base->body_rate_factor : add->body_rate_factor;
478     return cfg;
479 }
480
481 static const char *parse_int(apr_pool_t *p, const char *arg, int *val)
482 {
483     char *endptr;
484     *val = strtol(arg, &endptr, 10);
485
486     if (arg == endptr) {
487         return apr_psprintf(p, "Value '%s' not numerical", endptr);
488     }
489     if (*endptr != '\0') {
490         return apr_psprintf(p, "Cannot parse '%s'", endptr);
491     }
492     if (*val < 0) {
493         return "Value must be non-negative";
494     }
495     return NULL;
496 }
497
498 static const char *set_reqtimeout_param(reqtimeout_srv_cfg *conf,
499                                       apr_pool_t *p,
500                                       const char *key,
501                                       const char *val)
502 {
503     const char *ret = NULL;
504     char *rate_str = NULL, *initial_str, *max_str = NULL;
505     int rate = 0, initial = 0, max = 0;
506     enum { PARAM_HEADER, PARAM_BODY } type;
507
508     if (!strcasecmp(key, "header")) {
509         type = PARAM_HEADER;
510     }
511     else if (!strcasecmp(key, "body")) {
512         type = PARAM_BODY;
513     }
514     else {
515         return "Unknown RequestReadTimeout parameter";
516     }
517
518     if ((rate_str = ap_strcasestr(val, ",minrate="))) {
519         initial_str = apr_pstrndup(p, val, rate_str - val);
520         rate_str += strlen(",minrate=");
521         ret = parse_int(p, rate_str, &rate);
522         if (ret)
523             return ret;
524
525         if (rate == 0)
526             return "Minimum data rate must be larger than 0";
527
528         if ((max_str = strchr(initial_str, '-'))) {
529             *max_str++ = '\0';
530             ret = parse_int(p, max_str, &max);
531             if (ret)
532                 return ret;
533         }
534
535         ret = parse_int(p, initial_str, &initial);
536     }
537     else {
538         if (ap_strchr_c(val, '-'))
539             return "Must set MinRate option if using timeout range";
540         ret = parse_int(p, val, &initial);
541     }
542
543     if (ret)
544         return ret;
545
546     if (max && initial >= max) {
547         return "Maximum timeout must be larger than initial timeout";
548     }
549
550     if (type == PARAM_HEADER) {
551         conf->header_timeout = initial;
552         conf->header_max_timeout = max;
553         conf->header_min_rate = rate;
554         if (rate)
555             conf->header_rate_factor = apr_time_from_sec(1) / rate;
556     }
557     else {
558         conf->body_timeout = initial;
559         conf->body_max_timeout = max;
560         conf->body_min_rate = rate;
561         if (rate)
562             conf->body_rate_factor = apr_time_from_sec(1) / rate;
563     }
564     return ret;
565 }
566
567 static const char *set_reqtimeouts(cmd_parms *cmd, void *mconfig,
568                                    const char *arg)
569 {
570     reqtimeout_srv_cfg *conf =
571     ap_get_module_config(cmd->server->module_config,
572                          &reqtimeout_module);
573
574     while (*arg) {
575         char *word, *val;
576         const char *err;
577
578         word = ap_getword_conf(cmd->temp_pool, &arg);
579         val = strchr(word, '=');
580         if (!val) {
581             return "Invalid RequestReadTimeout parameter. Parameter must be "
582             "in the form 'key=value'";
583         }
584         else
585             *val++ = '\0';
586
587         err = set_reqtimeout_param(conf, cmd->pool, word, val);
588
589         if (err)
590             return apr_psprintf(cmd->temp_pool, "RequestReadTimeout: %s=%s: %s",
591                                word, val, err);
592     }
593
594     return NULL;
595
596 }
597
598 static void reqtimeout_hooks(apr_pool_t *pool)
599 {
600     /*
601      * mod_ssl is AP_FTYPE_CONNECTION + 5 and mod_reqtimeout needs to
602      * be called before mod_ssl. Otherwise repeated reads during the ssl
603      * handshake can prevent the timeout from triggering.
604      */
605     ap_register_input_filter(reqtimeout_filter_name, reqtimeout_filter, NULL,
606                              AP_FTYPE_CONNECTION + 8);
607
608     /*
609      * We need to pause timeout detection in between requests, for
610      * speculative and non-blocking reads, so between each outgoing EOR
611      * and the next pre_read_request call.
612      */
613     ap_register_output_filter(reqtimeout_filter_name, reqtimeout_eor, NULL,
614                               AP_FTYPE_CONNECTION);
615
616     /*
617      * mod_reqtimeout needs to be called before ap_process_http_request (which
618      * is run at APR_HOOK_REALLY_LAST) but after all other protocol modules.
619      * This ensures that it only influences normal http connections and not
620      * e.g. mod_ftp. Also, if mod_reqtimeout used the pre_connection hook, it
621      * would be inserted on mod_proxy's backend connections.
622      */
623     ap_hook_process_connection(reqtimeout_init, NULL, NULL, APR_HOOK_LAST);
624
625     ap_hook_pre_read_request(reqtimeout_before_header, NULL, NULL,
626                              APR_HOOK_MIDDLE);
627     ap_hook_post_read_request(reqtimeout_before_body, NULL, NULL,
628                               APR_HOOK_MIDDLE);
629
630 #if MRT_DEFAULT_HEADER_MIN_RATE > 0
631     default_header_rate_factor = apr_time_from_sec(1) / MRT_DEFAULT_HEADER_MIN_RATE;
632 #endif
633 #if MRT_DEFAULT_BODY_MIN_RATE > 0
634     default_body_rate_factor = apr_time_from_sec(1) / MRT_DEFAULT_BODY_MIN_RATE;
635 #endif
636 }
637
638 static const command_rec reqtimeout_cmds[] = {
639     AP_INIT_RAW_ARGS("RequestReadTimeout", set_reqtimeouts, NULL, RSRC_CONF,
640                      "Set various timeout parameters for reading request "
641                      "headers and body"),
642     {NULL}
643 };
644
645 AP_DECLARE_MODULE(reqtimeout) = {
646     STANDARD20_MODULE_STUFF,
647     NULL,                           /* create per-dir config structures */
648     NULL,                           /* merge  per-dir config structures */
649     reqtimeout_create_srv_config,   /* create per-server config structures */
650     reqtimeout_merge_srv_config,    /* merge per-server config structures */
651     reqtimeout_cmds,                /* table of config file commands */
652     reqtimeout_hooks
653 };