From: Jim Jagielski Date: Wed, 23 Nov 2016 12:15:01 +0000 (+0000) Subject: Allow for initual burst at full speed X-Git-Tag: 2.5.0-alpha~982 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=49bd2dd3e525097758cbbd7a037ad31afdb075f6;p=apache Allow for initual burst at full speed git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1770951 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index bed19108d2..ccb724486f 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,10 @@ -*- coding: utf-8 -*- Changes with Apache 2.5.0 + *) mod_ratelimit: Allow for initial "burst" amount at full speed before + throttling: PR 60145 [Andy Valencia , + Jim Jagielski] + *) event: Allow to use the whole allocated scoreboard (up to ServerLimit slots) to avoid scoreboard full errors when some processes are finishing gracefully. Also, make gracefully finishing processes close all diff --git a/docs/log-message-tags/next-number b/docs/log-message-tags/next-number index 294337bb64..98233cb68e 100644 --- a/docs/log-message-tags/next-number +++ b/docs/log-message-tags/next-number @@ -1 +1 @@ -3485 +3486 diff --git a/docs/manual/mod/mod_ratelimit.xml b/docs/manual/mod/mod_ratelimit.xml index affb2daf26..8adfb8acb3 100644 --- a/docs/manual/mod/mod_ratelimit.xml +++ b/docs/manual/mod/mod_ratelimit.xml @@ -37,11 +37,17 @@ the document to validate. --> The connection speed to be simulated is specified, in KiB/s, using the environment variable rate-limit.

+

Optionally, an initial amount of burst data, in KiB, may be +configured to be passed at full speed before throttling to the +specified rate limit. This value is optional, and is set using +the environment variable rate-initial-burst.

+ Example Configuration <Location "/downloads"> SetOutputFilter RATE_LIMIT SetEnv rate-limit 400 + SetEnv rate-initial-burst 512 </Location> diff --git a/modules/filters/mod_ratelimit.c b/modules/filters/mod_ratelimit.c index a2e9bd0197..0f95e80a73 100644 --- a/modules/filters/mod_ratelimit.c +++ b/modules/filters/mod_ratelimit.c @@ -35,12 +35,13 @@ typedef struct rl_ctx_t { int speed; int chunk_size; + int burst; rl_state_e state; apr_bucket_brigade *tmpbb; apr_bucket_brigade *holdingbb; } rl_ctx_t; -#if 0 +#if defined(RLFDEBUG) static void brigade_dump(request_rec *r, apr_bucket_brigade *bb) { apr_bucket *e; @@ -53,7 +54,7 @@ static void brigade_dump(request_rec *r, apr_bucket_brigade *bb) } } -#endif +#endif /* RLFDEBUG */ static apr_status_t rate_limit_filter(ap_filter_t *f, apr_bucket_brigade *input_bb) @@ -71,10 +72,12 @@ rate_limit_filter(ap_filter_t *f, apr_bucket_brigade *input_bb) return APR_ECONNABORTED; } + /* Set up our rl_ctx_t on first use */ if (ctx == NULL) { const char *rl = NULL; int ratelimit; + int burst = 0; /* no subrequests. */ if (f->r->main != NULL) { @@ -82,6 +85,7 @@ rate_limit_filter(ap_filter_t *f, apr_bucket_brigade *input_bb) return ap_pass_brigade(f->next, bb); } + /* Configuration: rate limit */ rl = apr_table_get(f->r->subprocess_env, "rate-limit"); if (rl == NULL) { @@ -97,11 +101,21 @@ rate_limit_filter(ap_filter_t *f, apr_bucket_brigade *input_bb) return ap_pass_brigade(f->next, bb); } - /* first run, init stuff */ + /* Configuration: optional initial burst */ + rl = apr_table_get(f->r->subprocess_env, "rate-initial-burst"); + if (rl != NULL) { + burst = atoi(rl) * 1024; + if (burst <= 0) { + burst = 0; + } + } + + /* Set up our context */ ctx = apr_palloc(f->r->pool, sizeof(rl_ctx_t)); f->ctx = ctx; ctx->state = RATE_LIMIT; ctx->speed = ratelimit; + ctx->burst = burst; /* calculate how many bytes / interval we want to send */ /* speed is bytes / second, so, how many (speed / 1000 % interval) */ @@ -183,7 +197,15 @@ rate_limit_filter(ap_filter_t *f, apr_bucket_brigade *input_bb) apr_brigade_length(bb, 1, &len); - rv = apr_brigade_partition(bb, ctx->chunk_size, &stop_point); + /* + * Pull next chunk of data; the initial amount is our + * burst allotment (if any) plus a chunk. All subsequent + * iterations are just chunks with whatever remaining + * burst amounts we have left (in case not done in the + * first bucket). + */ + rv = apr_brigade_partition(bb, + ctx->chunk_size + ctx->burst, &stop_point); if (rv != APR_SUCCESS && rv != APR_INCOMPLETE) { ctx->state = RATE_ERROR; ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, APLOGNO(01456) @@ -207,15 +229,33 @@ rate_limit_filter(ap_filter_t *f, apr_bucket_brigade *input_bb) APR_BRIGADE_INSERT_TAIL(ctx->tmpbb, fb); -#if 0 + /* + * Adjust the burst amount depending on how much + * we've done up to now. + */ + if (ctx->burst) { + len = ctx->burst; + apr_brigade_length(ctx->tmpbb, 1, &len); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, + APLOGNO(03485) "rl: burst %d; len %"APR_OFF_T_FMT, ctx->burst, len); + if (len < ctx->burst) { + ctx->burst -= len; + } + else { + ctx->burst = 0; + } + } + +#if defined(RLFDEBUG) brigade_dump(f->r, ctx->tmpbb); brigade_dump(f->r, bb); -#endif +#endif /* RLFDEBUG */ rv = ap_pass_brigade(f->next, ctx->tmpbb); apr_brigade_cleanup(ctx->tmpbb); if (rv != APR_SUCCESS) { + /* Most often, user disconnects from stream */ ctx->state = RATE_ERROR; ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, f->r, APLOGNO(01457) "rl: brigade pass failed.");