--- modules/filters/mod_reqtimeout.c (revision 1651300) +++ modules/filters/mod_reqtimeout.c (working copy) @@ -48,6 +48,14 @@ apr_time_t body_rate_factor; } reqtimeout_srv_cfg; +typedef struct +{ + 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; +} reqtimeout_dir_cfg; + /* this struct is used both as conn_config and as filter context */ typedef struct { @@ -348,12 +356,12 @@ static int reqtimeout_init(conn_rec *c) { reqtimeout_con_cfg *ccfg; - reqtimeout_srv_cfg *cfg; + reqtimeout_srv_cfg *scfg; - cfg = ap_get_module_config(c->base_server->module_config, + scfg = 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) { + AP_DEBUG_ASSERT(scfg != NULL); + if (scfg->header_timeout == 0 && scfg->body_timeout == 0) { /* disabled for this vhost */ return DECLINED; } @@ -370,11 +378,11 @@ } 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; + if (scfg->header_timeout != UNSET) { + ccfg->new_timeout = scfg->header_timeout; + ccfg->new_max_timeout = scfg->header_max_timeout; + ccfg->min_rate = scfg->header_min_rate; + ccfg->rate_factor = scfg->header_rate_factor; } else { ccfg->new_timeout = MRT_DEFAULT_HEADER_TIMEOUT; @@ -389,27 +397,36 @@ static int reqtimeout_after_headers(request_rec *r) { - reqtimeout_srv_cfg *cfg; + reqtimeout_srv_cfg *scfg; reqtimeout_con_cfg *ccfg = ap_get_module_config(r->connection->conn_config, &reqtimeout_module); + reqtimeout_dir_cfg *dcfg= + ap_get_module_config(r->per_dir_config, &reqtimeout_module); if (ccfg == NULL || r->method_number == M_CONNECT) { /* either disabled for this connection or a CONNECT request */ return OK; } - cfg = ap_get_module_config(r->connection->base_server->module_config, - &reqtimeout_module); - AP_DEBUG_ASSERT(cfg != NULL); + scfg = ap_get_module_config(r->connection->base_server->module_config, + &reqtimeout_module); + AP_DEBUG_ASSERT(scfg != NULL); ccfg->timeout_at = 0; ccfg->max_timeout_at = 0; ccfg->type = "body"; - 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; + + if (dcfg->body_timeout != UNSET) { + ccfg->new_timeout = dcfg->body_timeout; + ccfg->new_max_timeout = dcfg->body_max_timeout; + ccfg->min_rate = dcfg->body_min_rate; + ccfg->rate_factor = dcfg->body_rate_factor; } + else if (scfg->body_timeout != UNSET) { + ccfg->new_timeout = scfg->body_timeout; + ccfg->new_max_timeout = scfg->body_max_timeout; + ccfg->min_rate = scfg->body_min_rate; + ccfg->rate_factor = scfg->body_rate_factor; + } else { ccfg->new_timeout = MRT_DEFAULT_BODY_TIMEOUT; ccfg->new_max_timeout = MRT_DEFAULT_BODY_MAX_TIMEOUT; @@ -421,7 +438,7 @@ static int reqtimeout_after_body(request_rec *r) { - reqtimeout_srv_cfg *cfg; + reqtimeout_srv_cfg *scfg; reqtimeout_con_cfg *ccfg = ap_get_module_config(r->connection->conn_config, &reqtimeout_module); @@ -430,19 +447,19 @@ return OK; } - cfg = ap_get_module_config(r->connection->base_server->module_config, + scfg = ap_get_module_config(r->connection->base_server->module_config, &reqtimeout_module); - AP_DEBUG_ASSERT(cfg != NULL); + AP_DEBUG_ASSERT(scfg != NULL); ccfg->timeout_at = 0; ccfg->max_timeout_at = 0; ccfg->in_keep_alive = 1; ccfg->type = "header"; if (ccfg->new_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; + ccfg->new_timeout = scfg->header_timeout; + ccfg->new_max_timeout = scfg->header_max_timeout; + ccfg->min_rate = scfg->header_min_rate; + ccfg->rate_factor = scfg->header_rate_factor; } else { ccfg->new_timeout = MRT_DEFAULT_HEADER_TIMEOUT; @@ -456,39 +473,66 @@ static void *reqtimeout_create_srv_config(apr_pool_t *p, server_rec *s) { - reqtimeout_srv_cfg *cfg = apr_pcalloc(p, sizeof(reqtimeout_srv_cfg)); + reqtimeout_srv_cfg *scfg = 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; + scfg->header_timeout = UNSET; + scfg->header_max_timeout = UNSET; + scfg->header_min_rate = UNSET; + scfg->body_timeout = UNSET; + scfg->body_max_timeout = UNSET; + scfg->body_min_rate = UNSET; - return cfg; + return scfg; } +static void *reqtimeout_create_dir_config(apr_pool_t *p, char *dir) +{ + reqtimeout_dir_cfg *dcfg = apr_pcalloc(p, sizeof(reqtimeout_dir_cfg)); + + dcfg->body_timeout = UNSET; + dcfg->body_max_timeout = UNSET; + dcfg->body_min_rate = UNSET; + + return dcfg; +} + #define MERGE_INT(cfg, b, a, val) cfg->val = (a->val == UNSET) ? b->val : a->val; 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)); + reqtimeout_srv_cfg *scfg = 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); + MERGE_INT(scfg, base, add, header_timeout); + MERGE_INT(scfg, base, add, header_max_timeout); + MERGE_INT(scfg, base, add, header_min_rate); + MERGE_INT(scfg, base, add, body_timeout); + MERGE_INT(scfg, base, add, body_max_timeout); + MERGE_INT(scfg, 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; - return cfg; + scfg->header_rate_factor = (scfg->header_min_rate == UNSET) ? + base->header_rate_factor : add->header_rate_factor; + scfg->body_rate_factor = (scfg->body_min_rate == UNSET) ? + base->body_rate_factor : add->body_rate_factor; + return scfg; } +static void *reqtimeout_merge_dir_config(apr_pool_t *p, void *base_, void *add_) +{ + reqtimeout_dir_cfg *base = base_; + reqtimeout_dir_cfg *add = add_; + reqtimeout_dir_cfg *dcfg = apr_pcalloc(p, sizeof(reqtimeout_dir_cfg)); + + MERGE_INT(dcfg, base, add, body_timeout); + MERGE_INT(dcfg, base, add, body_max_timeout); + MERGE_INT(dcfg, base, add, body_min_rate); + + dcfg->body_rate_factor = (dcfg->body_min_rate == UNSET) ? + base->body_rate_factor : add->body_rate_factor; + + return dcfg; +} + static const char *parse_int(apr_pool_t *p, const char *arg, int *val) { char *endptr; *val = strtol(arg, &endptr, 10); @@ -505,30 +549,16 @@ return NULL; } -static const char *set_reqtimeout_param(reqtimeout_srv_cfg *conf, - apr_pool_t *p, - const char *key, - const char *val) +static const char *parse_setting(apr_pool_t *p, const char *val, int *initial, + int *max, int *rate) { 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; - if (!strcasecmp(key, "header")) { - type = PARAM_HEADER; - } - else if (!strcasecmp(key, "body")) { - type = PARAM_BODY; - } - else { - return "Unknown RequestReadTimeout parameter"; - } - 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, rate); if (ret) return ret; @@ -537,47 +567,102 @@ if ((max_str = strchr(initial_str, '-'))) { *max_str++ = '\0'; - ret = parse_int(p, max_str, &max); + ret = parse_int(p, max_str, max); if (ret) return ret; } - ret = parse_int(p, initial_str, &initial); + ret = parse_int(p, initial_str, initial); } 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, initial); } if (ret) return ret; - if (max && initial >= max) { + if (*max && *initial >= *max) { return "Maximum timeout must be larger than initial timeout"; } + return NULL; +} + +static const char *set_reqtimeout_srv_param(reqtimeout_srv_cfg *scfg, + apr_pool_t *p, + const char *key, + const char *val) +{ + const char *ret = NULL; + int rate = 0, initial = 0, max = 0; + enum { PARAM_HEADER, PARAM_BODY } type; + + if (!strcasecmp(key, "header")) { + type = PARAM_HEADER; + } + else if (!strcasecmp(key, "body")) { + type = PARAM_BODY; + } + else { + return "Unknown RequestReadTimeout parameter"; + } + + ret = parse_setting(p, val, &initial, &max, &rate); + if (ret) + return ret; + if (type == PARAM_HEADER) { - conf->header_timeout = initial; - conf->header_max_timeout = max; - conf->header_min_rate = rate; + scfg->header_timeout = initial; + scfg->header_max_timeout = max; + scfg->header_min_rate = rate; if (rate) - conf->header_rate_factor = apr_time_from_sec(1) / rate; + scfg->header_rate_factor = apr_time_from_sec(1) / rate; } else { - conf->body_timeout = initial; - conf->body_max_timeout = max; - conf->body_min_rate = rate; + scfg->body_timeout = initial; + scfg->body_max_timeout = max; + scfg->body_min_rate = rate; if (rate) - conf->body_rate_factor = apr_time_from_sec(1) / rate; + scfg->body_rate_factor = apr_time_from_sec(1) / rate; } + return ret; } +static const char *set_reqtimeout_dir_param(reqtimeout_dir_cfg *dcfg, + apr_pool_t *p, + const char *key, + const char *val) +{ + const char *ret = NULL; + int rate = 0, initial = 0, max = 0; + + if (!strcasecmp(key, "header")) { + return "Header timeout can not be changed within a location or directory"; + } + else if (strcasecmp(key, "body")) { + return "Unknown RequestReadTimeout parameter"; + } + + ret = parse_setting(p, val, &initial, &max, &rate); + if (ret) + return ret; + + dcfg->body_timeout = initial; + dcfg->body_max_timeout = max; + dcfg->body_min_rate = rate; + if (rate) + dcfg->body_rate_factor = apr_time_from_sec(1) / rate; + + return ret; +} + static const char *set_reqtimeouts(cmd_parms *cmd, void *mconfig, const char *arg) { - reqtimeout_srv_cfg *conf = + reqtimeout_srv_cfg *scfg = ap_get_module_config(cmd->server->module_config, &reqtimeout_module); @@ -594,7 +679,12 @@ else *val++ = '\0'; - err = set_reqtimeout_param(conf, cmd->pool, word, val); + if (cmd->path == NULL) { + err = set_reqtimeout_srv_param(scfg, cmd->pool, word, val); + } + else { + err = set_reqtimeout_dir_param(mconfig, cmd->pool, word, val); + } if (err) return apr_psprintf(cmd->temp_pool, "RequestReadTimeout: %s=%s: %s", @@ -624,8 +714,8 @@ */ ap_hook_process_connection(reqtimeout_init, NULL, NULL, APR_HOOK_LAST); - ap_hook_post_read_request(reqtimeout_after_headers, NULL, NULL, - APR_HOOK_MIDDLE); + ap_hook_post_perdir_config(reqtimeout_after_headers, NULL, NULL, + APR_HOOK_MIDDLE); ap_hook_log_transaction(reqtimeout_after_body, NULL, NULL, APR_HOOK_MIDDLE); @@ -638,7 +728,7 @@ } static const command_rec reqtimeout_cmds[] = { - AP_INIT_RAW_ARGS("RequestReadTimeout", set_reqtimeouts, NULL, RSRC_CONF, + AP_INIT_RAW_ARGS("RequestReadTimeout", set_reqtimeouts, NULL, ACCESS_CONF|RSRC_CONF, "Set various timeout parameters for reading request " "headers and body"), {NULL} @@ -646,8 +736,8 @@ AP_DECLARE_MODULE(reqtimeout) = { STANDARD20_MODULE_STUFF, - NULL, /* create per-dir config structures */ - NULL, /* merge per-dir config structures */ + reqtimeout_create_dir_config, /* create per-dir config structures */ + reqtimeout_merge_dir_config, /* merge per-dir config structures */ reqtimeout_create_srv_config, /* create per-server config structures */ reqtimeout_merge_srv_config, /* merge per-server config structures */ reqtimeout_cmds, /* table of config file commands */