Commit d81c1d51 authored by Graham Leggett's avatar Graham Leggett
Browse files

Refactor the input buffer filter to use a context as the output buffer

filter does. Ensure that the filter properly handles the non blocking
case, both when a downstream filter returns EAGAIN, or if a filter
returns APR_SUCCESS and an empty brigade.


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@718082 13f79535-47bb-0310-9956-ffa450edef68
parent 71feebd1
Loading
Loading
Loading
Loading
+69 −49
Original line number Diff line number Diff line
@@ -43,7 +43,10 @@ typedef struct buffer_conf {

typedef struct buffer_ctx {
    apr_bucket_brigade *bb;
    apr_bucket_brigade *tmp;
    buffer_conf *conf;
    apr_off_t remaining;
    int seen_eos;
} buffer_ctx;

/**
@@ -158,11 +161,9 @@ static apr_status_t buffer_out_filter(ap_filter_t *f, apr_bucket_brigade *bb) {
 */
static apr_status_t buffer_in_filter(ap_filter_t *f, apr_bucket_brigade *bb,
        ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes) {
    apr_bucket *e;
    apr_bucket_brigade *tmp;
    apr_bucket *e, *after;
    apr_status_t rv;
    buffer_conf *c;
    apr_off_t remaining;
    buffer_ctx *ctx = f->ctx;

    /* buffer on main requests only */
    if (!ap_is_initial_req(f->r)) {
@@ -170,65 +171,84 @@ static apr_status_t buffer_in_filter(ap_filter_t *f, apr_bucket_brigade *bb,
        return ap_get_brigade(f->next, bb, mode, block, readbytes);
    }

    /* first time in? create a context */
    if (!ctx) {
        ctx = f->ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
        ctx->bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
        ctx->tmp = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
        ctx->conf = ap_get_module_config(f->r->per_dir_config, &buffer_module);
    }

    /* just get out of the way of things we don't want. */
    if (mode != AP_MODE_READBYTES) {
        return ap_get_brigade(f->next, bb, mode, block, readbytes);
    }

    c = ap_get_module_config(f->r->per_dir_config, &buffer_module);

    tmp = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
    /* if our buffer is empty, read off the network until the buffer is full */
    if (APR_BRIGADE_EMPTY(ctx->bb)) {
        ctx->remaining = ctx->conf->size;

    remaining = readbytes;
    while (remaining > 0) {
        while (!ctx->seen_eos && ctx->remaining > 0) {
            const char *data;
        apr_off_t len;
        apr_size_t size;
            apr_size_t size = 0;

        rv = ap_get_brigade(f->next, tmp, mode, block, remaining);
            rv = ap_get_brigade(f->next, ctx->tmp, mode, block, ctx->remaining);

        /* if an error was received, bail out now */
        if (rv != APR_SUCCESS) {
            APR_BRIGADE_CONCAT(bb, tmp);
            /* if an error was received, bail out now. If the error is
             * EAGAIN and we have not yet seen an EOS, we will definitely
             * be called again, at which point we will send our buffered
             * data. Instead of sending EAGAIN, some filters return an
             * empty brigade instead when data is not yet available. In
             * this case, pass through the APR_SUCCESS and emulate the
             * underlying filter.
             */
            if (rv != APR_SUCCESS || APR_BRIGADE_EMPTY(ctx->tmp)) {
                return rv;
            }

        apr_brigade_length(tmp, 1, &len);
        remaining -= len;

        for (e = APR_BRIGADE_FIRST(tmp); e != APR_BRIGADE_SENTINEL(tmp); e
                = APR_BUCKET_NEXT(e)) {
            for (e = APR_BRIGADE_FIRST(ctx->tmp); e != APR_BRIGADE_SENTINEL(
                    ctx->tmp); e = APR_BUCKET_NEXT(e)) {

                /* if we see an EOS, we are done */
                if (APR_BUCKET_IS_EOS(e)) {
                    APR_BUCKET_REMOVE(e);
                APR_BRIGADE_INSERT_TAIL(bb, e);
                remaining = 0;
                    APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
                    ctx->seen_eos = 1;
                    break;
                }

            /* pass flush buckets through */
            if (APR_BUCKET_IS_FLUSH(e)) {
                /* pass flush and metadata buckets through */
                if (APR_BUCKET_IS_FLUSH(e) || APR_BUCKET_IS_METADATA(e)) {
                    APR_BUCKET_REMOVE(e);
                    APR_BRIGADE_INSERT_TAIL(bb, e);
                    continue;
                }

            /* pass metadata buckets through */
            if (APR_BUCKET_IS_METADATA(e)) {
                APR_BUCKET_REMOVE(e);
                APR_BRIGADE_INSERT_TAIL(bb, e);
                continue;
            }

            /* read */
                /* read the bucket in, pack it into the buffer */
                if (APR_SUCCESS == (rv = apr_bucket_read(e, &data, &size,
                        APR_BLOCK_READ))) {
                apr_brigade_write(bb, NULL, NULL, data, size);
                    apr_brigade_write(ctx->bb, NULL, NULL, data, size);
                    ctx->remaining -= size;
                    apr_bucket_delete(e);
                } else {
                    return rv;
                }

            }
        apr_brigade_cleanup(tmp);
        }
    }

    /* give the caller the data they asked for from the buffer */
    apr_brigade_partition(ctx->bb, readbytes, &after);
    e = APR_BRIGADE_FIRST(ctx->bb);
    while (e != after) {
        if (APR_BUCKET_IS_EOS(e)) {
            /* last bucket read, step out of the way */
            ap_remove_input_filter(f);
        }
        APR_BUCKET_REMOVE(e);
        APR_BRIGADE_INSERT_TAIL(bb, e);
        e = APR_BRIGADE_FIRST(ctx->bb);
    }

    return APR_SUCCESS;