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.");