Commit 4eb2b239 authored by Jim Jagielski's avatar Jim Jagielski
Browse files

Merge r1770951, r1774008, r1774068, r1774069 from trunk:

Allow for initual burst at full speed


Some "error" reporting if we overflow


rate limit notes


xhtml

Reviewed/backported by: jim


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1774071 13f79535-47bb-0310-9956-ffa450edef68
parent 8ffa177e
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -33,6 +33,10 @@ Changes with Apache 2.4.24
     pollution by malicious clients, upstream servers or faulty modules.
     [Stefan Fritsch, Eric Covener, Yann Ylavic]

  *) mod_ratelimit: Allow for initial "burst" amount at full speed before
     throttling: PR 60145 [Andy Valencia <ajv-etradanalhos vsta.org>,
     Jim Jagielski]

  *) mod_socache_memcache: Provide memcache stats to mod_status.
     [Jim Jagielski]

+1 −12
Original line number Diff line number Diff line
@@ -34,7 +34,7 @@ Release history:
    [NOTE that x.{odd}.z versions are strictly Alpha/Beta releases,
          while x.{even}.z versions are Stable/GA releases.]

    2.4.24  : In development.
    2.4.24  : In development. Jim proposes to T&R ~ Dec 15th.
    2.4.23  : Tagged on June 30, 2016. Released on July 05, 2016.
    2.4.22  : Tagged on June 20, 2016, not released.
    2.4.21  : Tagged on June 16, 2016, not released.
@@ -119,17 +119,6 @@ RELEASE SHOWSTOPPERS:
PATCHES ACCEPTED TO BACKPORT FROM TRUNK:
  [ start all new proposals below, under PATCHES PROPOSED. ]

  *) mod_ratelimit: Allow for initial burst of data before we start
     throttling: PR 60145
     trunk patches:
       https://svn.apache.org/r1770951
       https://svn.apache.org/r1774008
     2.4.x patch: trunk works modulo CHANGES and next-number
     +1: jim, ylavic, elukey
     jailletc36: compatibility note missing in the XML file
     jim:        Will address during commit
     elukey: the maximum value limitation for the new burst feature needs
             to be documented.

PATCHES PROPOSED TO BACKPORT FROM TRUNK:
  [ New proposals should be added at the end of the list ]
+13 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ the document to validate. -->
<status>Extension</status>
<sourcefile>mod_ratelimit.c</sourcefile>
<identifier>ratelimit_module</identifier>
<compatibility><code>rate-initial-burst</code> available in httpd 2.4.24 and later.</compatibility>

<summary>

@@ -37,13 +38,25 @@ the document to validate. -->
The connection speed to be simulated is specified, in KiB/s, using the environment
variable <code>rate-limit</code>.</p>

<p>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 <code>rate-initial-burst</code>.</p>

<example><title>Example Configuration</title>
<highlight language="config">
&lt;Location "/downloads"&gt;
    SetOutputFilter RATE_LIMIT
    SetEnv rate-limit 400 
    SetEnv rate-initial-burst 512
&lt;/Location&gt;
</highlight>

<note type="warning">
If the value specified for <code>rate-limit</code> causes integer overflow, the rate-limited will be disabled.
If the value specified for <code>rate-limit-burst</code> causes integer overflow, the burst will be disabled.
</note>

</example>

</summary>
+50 −6
Original line number Diff line number Diff line
@@ -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) {
@@ -93,15 +97,29 @@ rate_limit_filter(ap_filter_t *f, apr_bucket_brigade *input_bb)
        ratelimit = atoi(rl) * 1024;
        if (ratelimit <= 0) {
            /* remove ourselves */
            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r,
                          APLOGNO(03488) "rl: disabling: rate-limit = %s (too high?)", rl);
            ap_remove_output_filter(f);
            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) {
               ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r,
                             APLOGNO(03489) "rl: disabling burst: rate-initial-burst = %s (too high?)", rl);
               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 +201,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 +233,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.");