module AP_MODULE_DECLARE_DATA reqtimeout_module;
#define UNSET -1
-#define MRT_DEFAULT_HEADER_TIMEOUT 20
-#define MRT_DEFAULT_HEADER_MAX_TIMEOUT 40
-#define MRT_DEFAULT_HEADER_MIN_RATE 500
-#define MRT_DEFAULT_BODY_TIMEOUT 20
-#define MRT_DEFAULT_BODY_MAX_TIMEOUT 0
-#define MRT_DEFAULT_BODY_MIN_RATE 500
+#define MRT_DEFAULT_header_TIMEOUT 20
+#define MRT_DEFAULT_header_MAX_TIMEOUT 40
+#define MRT_DEFAULT_header_MIN_RATE 500
+#define MRT_DEFAULT_body_TIMEOUT 20
+#define MRT_DEFAULT_body_MAX_TIMEOUT 0
+#define MRT_DEFAULT_body_MIN_RATE 500
typedef struct
{
- int header_timeout; /* timeout for reading the req hdrs in secs */
- int header_max_timeout; /* max timeout for req hdrs in secs */
- int header_min_rate; /* min rate for reading req hdrs in bytes/s */
- apr_time_t header_rate_factor;
- int body_timeout; /* timeout for reading the req body in secs */
- int body_max_timeout; /* max timeout for req body in secs */
- int body_min_rate; /* min rate for reading req body in bytes/s */
- apr_time_t body_rate_factor;
+ int timeout; /* timeout in secs */
+ int max_timeout; /* max timeout in secs */
+ int min_rate; /* min rate in bytes/s */
+ apr_time_t rate_factor; /* scale factor (#usecs per min_rate) */
+} reqtimeout_stage_t;
+
+typedef struct
+{
+ reqtimeout_stage_t header; /* Reading the HTTP header */
+ reqtimeout_stage_t body; /* Reading the HTTP body */
} reqtimeout_srv_cfg;
/* this struct is used both as conn_config and as filter context */
{
apr_time_t timeout_at;
apr_time_t max_timeout_at;
- int min_rate;
- int new_timeout;
- int new_max_timeout;
+ reqtimeout_stage_t cur_stage;
int in_keep_alive;
char *type;
apr_socket_t *socket;
- apr_time_t rate_factor;
apr_bucket_brigade *tmpbb;
} reqtimeout_con_cfg;
if (apr_brigade_length(bb, 0, &len) != APR_SUCCESS || len <= 0)
return;
- new_timeout_at = ccfg->timeout_at + len * ccfg->rate_factor;
+ new_timeout_at = ccfg->timeout_at + len * ccfg->cur_stage.rate_factor;
if (ccfg->max_timeout_at > 0 && new_timeout_at > ccfg->max_timeout_at) {
ccfg->timeout_at = ccfg->max_timeout_at;
}
apr_brigade_cleanup(bb);
}
- if (ccfg->new_timeout > 0) {
+ if (ccfg->cur_stage.timeout > 0) {
/* set new timeout */
now = apr_time_now();
- ccfg->timeout_at = now + apr_time_from_sec(ccfg->new_timeout);
- ccfg->new_timeout = 0;
- if (ccfg->new_max_timeout > 0) {
- ccfg->max_timeout_at = now + apr_time_from_sec(ccfg->new_max_timeout);
- ccfg->new_max_timeout = 0;
+ ccfg->timeout_at = now + apr_time_from_sec(ccfg->cur_stage.timeout);
+ ccfg->cur_stage.timeout = 0;
+ if (ccfg->cur_stage.max_timeout > 0) {
+ ccfg->max_timeout_at = now + apr_time_from_sec(ccfg->cur_stage.max_timeout);
+ ccfg->cur_stage.max_timeout = 0;
}
}
else if (ccfg->timeout_at == 0) {
if (block == APR_NONBLOCK_READ || mode == AP_MODE_INIT
|| mode == AP_MODE_EATCRLF) {
rv = ap_get_brigade(f->next, bb, mode, block, readbytes);
- if (ccfg->min_rate > 0 && rv == APR_SUCCESS) {
+ if (ccfg->cur_stage.rate_factor > 0 && rv == APR_SUCCESS) {
extend_timeout(ccfg, bb);
}
return rv;
}
if (!APR_BRIGADE_EMPTY(bb)) {
- if (ccfg->min_rate > 0) {
+ if (ccfg->cur_stage.rate_factor > 0) {
extend_timeout(ccfg, bb);
}
* the real (relevant) bytes to be asked later, within the
* currently alloted time.
*/
- if (ccfg->min_rate > 0 && rv == APR_SUCCESS
+ if (ccfg->cur_stage.rate_factor > 0 && rv == APR_SUCCESS
&& mode != AP_MODE_SPECULATIVE) {
extend_timeout(ccfg, bb);
}
return ap_pass_brigade(f->next, bb);
}
+#define INIT_STAGE(cfg, ccfg, stage) do { \
+ if (cfg->stage.timeout != UNSET) { \
+ ccfg->cur_stage.timeout = cfg->stage.timeout; \
+ ccfg->cur_stage.max_timeout = cfg->stage.max_timeout; \
+ ccfg->cur_stage.rate_factor = cfg->stage.rate_factor; \
+ } \
+ else { \
+ ccfg->cur_stage.timeout = MRT_DEFAULT_##stage##_TIMEOUT; \
+ ccfg->cur_stage.max_timeout = MRT_DEFAULT_##stage##_MAX_TIMEOUT; \
+ ccfg->cur_stage.rate_factor = default_##stage##_rate_factor; \
+ } \
+ ccfg->type = #stage; \
+} while (0)
+
static int reqtimeout_init(conn_rec *c)
{
reqtimeout_con_cfg *ccfg;
cfg = ap_get_module_config(c->base_server->module_config,
&reqtimeout_module);
AP_DEBUG_ASSERT(cfg != NULL);
- if (cfg->header_timeout == 0 && cfg->body_timeout == 0) {
+
+ if (cfg->header.timeout == 0 && cfg->body.timeout == 0) {
/* disabled for this vhost */
return DECLINED;
}
ccfg->timeout_at = 0;
ccfg->max_timeout_at = 0;
ccfg->in_keep_alive = (c->keepalives > 0);
- ccfg->type = "header";
- if (cfg->header_timeout != UNSET) {
- ccfg->new_timeout = cfg->header_timeout;
- ccfg->new_max_timeout = cfg->header_max_timeout;
- ccfg->min_rate = cfg->header_min_rate;
- ccfg->rate_factor = cfg->header_rate_factor;
- }
- else {
- ccfg->new_timeout = MRT_DEFAULT_HEADER_TIMEOUT;
- ccfg->new_max_timeout = MRT_DEFAULT_HEADER_MAX_TIMEOUT;
- ccfg->min_rate = MRT_DEFAULT_HEADER_MIN_RATE;
- ccfg->rate_factor = default_header_rate_factor;
- }
+ INIT_STAGE(cfg, ccfg, header);
}
static int reqtimeout_before_body(request_rec *r)
ccfg->type = "body";
if (r->method_number == M_CONNECT) {
/* disabled for a CONNECT request */
- ccfg->new_timeout = 0;
- }
- else if (cfg->body_timeout != UNSET) {
- ccfg->new_timeout = cfg->body_timeout;
- ccfg->new_max_timeout = cfg->body_max_timeout;
- ccfg->min_rate = cfg->body_min_rate;
- ccfg->rate_factor = cfg->body_rate_factor;
+ ccfg->cur_stage.timeout = 0;
}
else {
- ccfg->new_timeout = MRT_DEFAULT_BODY_TIMEOUT;
- ccfg->new_max_timeout = MRT_DEFAULT_BODY_MAX_TIMEOUT;
- ccfg->min_rate = MRT_DEFAULT_BODY_MIN_RATE;
- ccfg->rate_factor = default_body_rate_factor;
+ INIT_STAGE(cfg, ccfg, body);
}
return OK;
}
+#define UNSET_STAGE(cfg, stage) do { \
+ cfg->stage.timeout = UNSET; \
+ cfg->stage.max_timeout = UNSET; \
+ cfg->stage.min_rate = UNSET; \
+} while (0)
+
static void *reqtimeout_create_srv_config(apr_pool_t *p, server_rec *s)
{
reqtimeout_srv_cfg *cfg = apr_pcalloc(p, sizeof(reqtimeout_srv_cfg));
- cfg->header_timeout = UNSET;
- cfg->header_max_timeout = UNSET;
- cfg->header_min_rate = UNSET;
- cfg->body_timeout = UNSET;
- cfg->body_max_timeout = UNSET;
- cfg->body_min_rate = UNSET;
+ UNSET_STAGE(cfg, header);
+ UNSET_STAGE(cfg, body);
return cfg;
}
-#define MERGE_INT(cfg, b, a, val) cfg->val = (a->val == UNSET) ? b->val : a->val;
+#define MERGE_INT(cfg, b, a, val) \
+ cfg->val = (a->val == UNSET) ? b->val : a->val
+#define MERGE_STAGE(cfg, b, a, stage) do { \
+ MERGE_INT(cfg, base, add, stage.timeout); \
+ MERGE_INT(cfg, base, add, stage.max_timeout); \
+ MERGE_INT(cfg, base, add, stage.min_rate); \
+ cfg->stage.rate_factor = (cfg->stage.min_rate == UNSET) \
+ ? base->stage.rate_factor \
+ : add->stage.rate_factor; \
+} while (0)
+
static void *reqtimeout_merge_srv_config(apr_pool_t *p, void *base_, void *add_)
{
reqtimeout_srv_cfg *base = base_;
reqtimeout_srv_cfg *add = add_;
reqtimeout_srv_cfg *cfg = apr_pcalloc(p, sizeof(reqtimeout_srv_cfg));
- MERGE_INT(cfg, base, add, header_timeout);
- MERGE_INT(cfg, base, add, header_max_timeout);
- MERGE_INT(cfg, base, add, header_min_rate);
- MERGE_INT(cfg, base, add, body_timeout);
- MERGE_INT(cfg, base, add, body_max_timeout);
- MERGE_INT(cfg, base, add, body_min_rate);
-
- cfg->header_rate_factor = (cfg->header_min_rate == UNSET) ?
- base->header_rate_factor : add->header_rate_factor;
- cfg->body_rate_factor = (cfg->body_min_rate == UNSET) ?
- base->body_rate_factor : add->body_rate_factor;
+ MERGE_STAGE(cfg, base, add, header);
+ MERGE_STAGE(cfg, base, add, body);
+
return cfg;
}
{
const char *ret = NULL;
char *rate_str = NULL, *initial_str, *max_str = NULL;
- int rate = 0, initial = 0, max = 0;
- enum { PARAM_HEADER, PARAM_BODY } type;
+ reqtimeout_stage_t *stage;
if (!strcasecmp(key, "header")) {
- type = PARAM_HEADER;
+ stage = &conf->header;
}
else if (!strcasecmp(key, "body")) {
- type = PARAM_BODY;
+ stage = &conf->body;
}
else {
return "Unknown RequestReadTimeout parameter";
}
+ memset(stage, 0, sizeof(*stage));
+
if ((rate_str = ap_strcasestr(val, ",minrate="))) {
initial_str = apr_pstrndup(p, val, rate_str - val);
rate_str += strlen(",minrate=");
- ret = parse_int(p, rate_str, &rate);
+ ret = parse_int(p, rate_str, &stage->min_rate);
if (ret)
return ret;
- if (rate == 0)
+ if (stage->min_rate == 0)
return "Minimum data rate must be larger than 0";
if ((max_str = strchr(initial_str, '-'))) {
*max_str++ = '\0';
- ret = parse_int(p, max_str, &max);
+ ret = parse_int(p, max_str, &stage->max_timeout);
if (ret)
return ret;
}
- ret = parse_int(p, initial_str, &initial);
+ ret = parse_int(p, initial_str, &stage->timeout);
}
else {
if (ap_strchr_c(val, '-'))
return "Must set MinRate option if using timeout range";
- ret = parse_int(p, val, &initial);
+ ret = parse_int(p, val, &stage->timeout);
}
-
if (ret)
return ret;
- if (max && initial >= max) {
+ if (stage->max_timeout && stage->timeout >= stage->max_timeout) {
return "Maximum timeout must be larger than initial timeout";
}
- if (type == PARAM_HEADER) {
- conf->header_timeout = initial;
- conf->header_max_timeout = max;
- conf->header_min_rate = rate;
- if (rate)
- conf->header_rate_factor = apr_time_from_sec(1) / rate;
+ if (stage->min_rate) {
+ stage->rate_factor = apr_time_from_sec(1) / stage->min_rate;
}
- else {
- conf->body_timeout = initial;
- conf->body_max_timeout = max;
- conf->body_min_rate = rate;
- if (rate)
- conf->body_rate_factor = apr_time_from_sec(1) / rate;
- }
- return ret;
+
+ return NULL;
}
static const char *set_reqtimeouts(cmd_parms *cmd, void *mconfig,
APR_HOOK_MIDDLE);
#if MRT_DEFAULT_HEADER_MIN_RATE > 0
- default_header_rate_factor = apr_time_from_sec(1) / MRT_DEFAULT_HEADER_MIN_RATE;
+ default_header_rate_factor = apr_time_from_sec(1) /
+ MRT_DEFAULT_HEADER_MIN_RATE;
#endif
#if MRT_DEFAULT_BODY_MIN_RATE > 0
- default_body_rate_factor = apr_time_from_sec(1) / MRT_DEFAULT_BODY_MIN_RATE;
+ default_body_rate_factor = apr_time_from_sec(1) /
+ MRT_DEFAULT_BODY_MIN_RATE;
#endif
}