On 12/01/2008 06:12 AM, [EMAIL PROTECTED] wrote:
> Author: pquerna
> Date: Sun Nov 30 21:12:22 2008
> New Revision: 721965
> 
> URL: http://svn.apache.org/viewvc?rev=721965&view=rev
> Log:
> Add a new module, mod_ratelimit, originally written at Joost, which can rate 
> limit the outgoing bandwidth to a client.
> 
> Added:
>     httpd/httpd/trunk/modules/filters/mod_ratelimit.c   (with props)
>     httpd/httpd/trunk/modules/filters/mod_ratelimit.h   (with props)
> Modified:
>     httpd/httpd/trunk/modules/filters/config.m4

I guess you missed a change entry here.

> +static apr_status_t
> +rate_limit_filter(ap_filter_t *f, apr_bucket_brigade *input_bb)
> +{
> +    apr_status_t rv = APR_SUCCESS;
> +    rl_ctx_t *ctx = f->ctx;
> +    apr_bucket *fb;
> +    int do_sleep = 0;
> +    apr_bucket_alloc_t *ba = f->r->connection->bucket_alloc;
> +    apr_bucket_brigade *bb = input_bb;
> +
> +    if (f->c->aborted) {
> +        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, "rl: conn aborted");
> +        apr_brigade_cleanup(bb);
> +        return APR_ECONNABORTED;
> +    }
> +
> +    if (ctx == NULL) {
> +
> +        /* no subrequests. */
> +        if (f->r->main != NULL) {
> +            ap_remove_output_filter(f);
> +            return ap_pass_brigade(f->next, bb);
> +        }
> +
> +        const char *rl = apr_table_get(f->r->subprocess_env, "rate-limit");
> +
> +        if (rl == NULL) {
> +            ap_remove_output_filter(f);
> +            return ap_pass_brigade(f->next, bb);
> +        }
> +
> +        /* first run, init stuff */
> +        ctx = apr_palloc(f->r->pool, sizeof(rl_ctx_t));
> +        f->ctx = ctx;
> +        ctx->speed = 0;
> +        ctx->state = RATE_LIMIT;
> +
> +        /* rl is in kilo bytes / second  */
> +        ctx->speed = atoi(rl) * 1024;
> +
> +        if (ctx->speed == 0) {
> +            /* remove ourselves */
> +            ap_remove_output_filter(f);
> +            return ap_pass_brigade(f->next, bb);
> +        }
> +
> +        /* calculate how many bytes / interval we want to send */
> +        /* speed is bytes / second, so, how many  (speed / 1000 % interval) 
> */
> +        ctx->chunk_size = (ctx->speed / (1000 / RATE_INTERVAL_MS));
> +        ctx->tmpbb = apr_brigade_create(f->r->pool, ba);
> +        ctx->holdingbb = apr_brigade_create(f->r->pool, ba);
> +    }
> +
> +    while (ctx->state != RATE_ERROR &&
> +           (!APR_BRIGADE_EMPTY(bb) || !APR_BRIGADE_EMPTY(ctx->holdingbb))) {
> +        apr_bucket *e;
> +
> +        if (!APR_BRIGADE_EMPTY(ctx->holdingbb)) {
> +            APR_BRIGADE_CONCAT(bb, ctx->holdingbb);
> +            apr_brigade_cleanup(ctx->holdingbb);
> +        }
> +
> +        while (ctx->state == RATE_FULLSPEED && !APR_BRIGADE_EMPTY(bb)) {
> +            /* Find where we 'stop' going full speed. */
> +            for (e = APR_BRIGADE_FIRST(bb);
> +                 e != APR_BRIGADE_SENTINEL(bb); e = APR_BUCKET_NEXT(e)) {
> +                if (RL_BUCKET_IS_END(e)) {
> +                    apr_bucket *f;
> +                    f = APR_RING_LAST(&bb->list);
> +                    APR_RING_UNSPLICE(e, f, link);
> +                    APR_RING_SPLICE_TAIL(&ctx->holdingbb->list, e, f,
> +                                         apr_bucket, link);
> +                    ctx->state = RATE_LIMIT;
> +                    break;
> +                }
> +            }
> +
> +            if (f->c->aborted) {
> +                apr_brigade_cleanup(bb);
> +                ctx->state = RATE_ERROR;
> +                break;
> +            }
> +
> +            fb = apr_bucket_flush_create(ba);
> +            APR_BRIGADE_INSERT_TAIL(bb, fb);
> +            rv = ap_pass_brigade(f->next, bb);
> +
> +            if (rv != APR_SUCCESS) {
> +                ctx->state = RATE_ERROR;
> +                ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r,
> +                              "rl: full speed brigade pass failed.");
> +            }
> +        }
> +
> +        while (ctx->state == RATE_LIMIT && !APR_BRIGADE_EMPTY(bb)) {
> +            for (e = APR_BRIGADE_FIRST(bb);
> +                 e != APR_BRIGADE_SENTINEL(bb); e = APR_BUCKET_NEXT(e)) {
> +                if (RL_BUCKET_IS_START(e)) {
> +                    apr_bucket *f;
> +                    f = APR_RING_LAST(&bb->list);
> +                    APR_RING_UNSPLICE(e, f, link);
> +                    APR_RING_SPLICE_TAIL(&ctx->holdingbb->list, e, f,
> +                                         apr_bucket, link);
> +                    ctx->state = RATE_FULLSPEED;
> +                    break;
> +                }
> +            }
> +
> +            while (!APR_BRIGADE_EMPTY(bb)) {
> +                apr_bucket *stop_point;
> +                apr_off_t len = 0;
> +
> +                if (f->c->aborted) {
> +                    apr_brigade_cleanup(bb);
> +                    ctx->state = RATE_ERROR;

Shouldn't we do a break or continue here?

> +                }
> +
> +                if (do_sleep) {
> +                    apr_sleep(RATE_INTERVAL_MS * 1000);
> +                }
> +                else {
> +                    do_sleep = 1;
> +                }
> +
> +                apr_brigade_length(bb, 1, &len);
> +
> +                rv = apr_brigade_partition(bb, ctx->chunk_size, &stop_point);
> +                if (rv != APR_SUCCESS && rv != APR_INCOMPLETE) {
> +                    ctx->state = RATE_ERROR;
> +                    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r,
> +                                  "rl: partition failed.");
> +                    break;
> +                }
> +
> +                if (stop_point != APR_BRIGADE_SENTINEL(bb)) {
> +                    apr_bucket *f;
> +                    apr_bucket *e = APR_BUCKET_PREV(stop_point);
> +                    f = APR_RING_FIRST(&bb->list);
> +                    APR_RING_UNSPLICE(f, e, link);
> +                    APR_RING_SPLICE_HEAD(&ctx->tmpbb->list, f, e, apr_bucket,
> +                                         link);
> +                }
> +                else {
> +                    APR_BRIGADE_CONCAT(ctx->tmpbb, bb);
> +                }
> +
> +                fb = apr_bucket_flush_create(ba);
> +
> +                APR_BRIGADE_INSERT_TAIL(ctx->tmpbb, fb);
> +
> +#if 0
> +                brigade_dump(f->r, ctx->tmpbb);
> +                brigade_dump(f->r, bb);
> +#endif
> +
> +                rv = ap_pass_brigade(f->next, ctx->tmpbb);
> +                apr_brigade_cleanup(ctx->tmpbb);
> +
> +                if (rv != APR_SUCCESS) {
> +                    ctx->state = RATE_ERROR;
> +                    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r,
> +                                  "rl: brigade pass failed.");
> +                    break;
> +                }
> +            }
> +        }
> +    }
> +
> +    return rv;
> +}
> +

Regards

RĂ¼diger

Reply via email to