On Thu, Sep 11, 2014 at 5:20 PM, Graham Leggett <minf...@sharp.fm> wrote:
> On 11 Sep 2014, at 2:12 PM, Yann Ylavic <ylavic....@gmail.com> wrote:
>
> I would still do the flush check anyway - the ap_filter_reinstate_brigade() 
> has
> no idea what happened outside this function, we may have flushed buckets,
> we may not have and setaside those flush buckets for whatever reason, and
> we want the behaviour to remain predictable.

Makes sense, however ap_core_output_filter() and
ssl_io_filter_output() *know* that their buffered_bb does not contain
such bucket (exclusively filled by ap_filter_setaside_brigade()), in
that the original code (still in svn) is more optimal since it does
not walk through the same brigade multiple times (doing an immediate
nonblocking write with what it has and returning).

To avoid that and the buffered_bb lifetime issue discussed with
Rüdiger, I'm thinking of an opaque type (a struct containing the
buffered_bb and the deferred_pool) that could be passed to both
ap_filter_setaside_brigade() and ap_filter_reinstate_brigade().
That way no user could modify the content of the brigade or play with
the pool (opaque), so that ap_filter_setaside_brigade() could
create/clear the deferred_pool as it wish, and
ap_filter_reinstate_brigade() could be sure buffered_bb contains only
suitable buckets.

Something like :

# util_filter.h
typedef struct ap_filter_aside ap_filter_aside_t;

AP_DECLARE(apr_status_t) ap_filter_setaside_brigade(ap_filter_t *f,
                                                    apr_filter_aside_t **aside,
                                                    apr_bucket_brigade *bb);

AP_DECLARE(int) ap_filter_reinstate_brigade(ap_filter_t *f,
                                            apr_filter_aside_t *aside,
                                            apr_bucket_brigade *bb,
                                            apr_bucket **flush_upto);

# util_filter.c
struct ap_filter_aside
{
    apr_bucket_brigade *bb;
    apr_pool_t *pool;
};

AP_DECLARE(apr_status_t) ap_filter_setaside_brigade(ap_filter_t *f,
                                                    ap_filter_aside_t **aside,
                                                    apr_pool_t
**deferred_write_pool,
                                                    apr_bucket_brigade *bb)
{
    if (!APR_BRIGADE_EMPTY(bb)) {
        if (!*aside) {
            *aside = apr_palloc(f->c->pool, sizeof **aside);
            apr_pool_create(&(*aside)->pool, f->c->pool);
            apr_pool_tag((*aside)->pool, "deferred_write");
            (*aside)->bb = NULL;
        }
        if (!(*aside)->bb || APR_BRIGADE_EMPTY((*aside)->bb))) {
            f->c->data_in_output_filters++;
        }
        if (bb != (*aside)->bb) {
            return ap_save_brigade(f, &(*aside)->bb, &bb, (*aside)->pool);
        }
    }
    else if ((*aside)->pool) {
        /*
         * There are no more requests in the pipeline. We can just clear the
         * pool.
         */
        ((*aside)->bb = NULL;
        apr_pool_clear((*aside)->pool);
    }
    return APR_SUCCESS;
}

AP_DECLARE(int) ap_filter_reinstate_brigade(ap_filter_t *f,
                                            ap_filter_aside_t *aside,
                                            apr_bucket_brigade *bb,
                                            apr_bucket **flush_upto)
{
    apr_bucket *bucket, *next;
    apr_size_t bytes_in_brigade, non_file_bytes_in_brigade;
    int eor_buckets_in_brigade, morphing_bucket_in_brigade;
    int loglevel = ap_get_conn_module_loglevel(f->c, APLOG_MODULE_INDEX);
    int empty = APR_BRIGADE_EMPTY(bb);

    if ((*aside)->bb && !APR_BRIGADE_EMPTY((*aside)->bb)) {
        APR_BRIGADE_PREPEND(bb, (*aside)->bb);
        f->c->data_in_output_filters--;
        if (empty) {
            return 1;
        }
    }

    ...
}

# core_filters and mod_ssl
use a ap_filter_aside_t* instead of buffered_bb and deferred_write_pool.

WDYT?

Reply via email to