Blame buckets/aggregate_buckets.c

Packit 3adb1e
/* ====================================================================
Packit 3adb1e
 *    Licensed to the Apache Software Foundation (ASF) under one
Packit 3adb1e
 *    or more contributor license agreements.  See the NOTICE file
Packit 3adb1e
 *    distributed with this work for additional information
Packit 3adb1e
 *    regarding copyright ownership.  The ASF licenses this file
Packit 3adb1e
 *    to you under the Apache License, Version 2.0 (the
Packit 3adb1e
 *    "License"); you may not use this file except in compliance
Packit 3adb1e
 *    with the License.  You may obtain a copy of the License at
Packit 3adb1e
 *
Packit 3adb1e
 *      http://www.apache.org/licenses/LICENSE-2.0
Packit 3adb1e
 *
Packit 3adb1e
 *    Unless required by applicable law or agreed to in writing,
Packit 3adb1e
 *    software distributed under the License is distributed on an
Packit 3adb1e
 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
Packit 3adb1e
 *    KIND, either express or implied.  See the License for the
Packit 3adb1e
 *    specific language governing permissions and limitations
Packit 3adb1e
 *    under the License.
Packit 3adb1e
 * ====================================================================
Packit 3adb1e
 */
Packit 3adb1e
Packit 3adb1e
#include "serf.h"
Packit 3adb1e
#include "serf_bucket_util.h"
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
/* Should be an APR_RING? */
Packit 3adb1e
typedef struct bucket_list {
Packit 3adb1e
    serf_bucket_t *bucket;
Packit 3adb1e
    struct bucket_list *next;
Packit 3adb1e
} bucket_list_t;
Packit 3adb1e
Packit 3adb1e
typedef struct {
Packit 3adb1e
    bucket_list_t *list; /* active buckets */
Packit 3adb1e
    bucket_list_t *last; /* last bucket of the list */
Packit 3adb1e
    bucket_list_t *done; /* we finished reading this; now pending a destroy */
Packit 3adb1e
Packit 3adb1e
    serf_bucket_aggregate_eof_t hold_open;
Packit 3adb1e
    void *hold_open_baton;
Packit 3adb1e
Packit 3adb1e
    /* Does this bucket own its children? !0 if yes, 0 if not. */
Packit 3adb1e
    int bucket_owner;
Packit 3adb1e
} aggregate_context_t;
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
static void cleanup_aggregate(aggregate_context_t *ctx,
Packit 3adb1e
                              serf_bucket_alloc_t *allocator)
Packit 3adb1e
{
Packit 3adb1e
    bucket_list_t *next_list;
Packit 3adb1e
Packit 3adb1e
    /* If we finished reading a bucket during the previous read, then
Packit 3adb1e
     * we can now toss that bucket.
Packit 3adb1e
     */
Packit 3adb1e
    while (ctx->done != NULL) {
Packit 3adb1e
        next_list = ctx->done->next;
Packit 3adb1e
Packit 3adb1e
        if (ctx->bucket_owner) {
Packit 3adb1e
            serf_bucket_destroy(ctx->done->bucket);
Packit 3adb1e
        }
Packit 3adb1e
        serf_bucket_mem_free(allocator, ctx->done);
Packit 3adb1e
Packit 3adb1e
        ctx->done = next_list;
Packit 3adb1e
    }
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
void serf_bucket_aggregate_cleanup(
Packit 3adb1e
    serf_bucket_t *bucket, serf_bucket_alloc_t *allocator)
Packit 3adb1e
{
Packit 3adb1e
    aggregate_context_t *ctx = bucket->data;
Packit 3adb1e
Packit 3adb1e
    cleanup_aggregate(ctx, allocator);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static aggregate_context_t *create_aggregate(serf_bucket_alloc_t *allocator)
Packit 3adb1e
{
Packit 3adb1e
    aggregate_context_t *ctx;
Packit 3adb1e
Packit 3adb1e
    ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx));
Packit 3adb1e
Packit 3adb1e
    ctx->list = NULL;
Packit 3adb1e
    ctx->last = NULL;
Packit 3adb1e
    ctx->done = NULL;
Packit 3adb1e
    ctx->hold_open = NULL;
Packit 3adb1e
    ctx->hold_open_baton = NULL;
Packit 3adb1e
    ctx->bucket_owner = 1;
Packit 3adb1e
Packit 3adb1e
    return ctx;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
serf_bucket_t *serf_bucket_aggregate_create(
Packit 3adb1e
    serf_bucket_alloc_t *allocator)
Packit 3adb1e
{
Packit 3adb1e
    aggregate_context_t *ctx;
Packit 3adb1e
Packit 3adb1e
    ctx = create_aggregate(allocator);
Packit 3adb1e
Packit 3adb1e
    return serf_bucket_create(&serf_bucket_type_aggregate, allocator, ctx);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
serf_bucket_t *serf__bucket_stream_create(
Packit 3adb1e
    serf_bucket_alloc_t *allocator,
Packit 3adb1e
    serf_bucket_aggregate_eof_t fn,
Packit 3adb1e
    void *baton)
Packit 3adb1e
{
Packit 3adb1e
    serf_bucket_t *bucket = serf_bucket_aggregate_create(allocator);
Packit 3adb1e
    aggregate_context_t *ctx = bucket->data;
Packit 3adb1e
Packit 3adb1e
    serf_bucket_aggregate_hold_open(bucket, fn, baton);
Packit 3adb1e
Packit 3adb1e
    ctx->bucket_owner = 0;
Packit 3adb1e
Packit 3adb1e
    return bucket;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
static void serf_aggregate_destroy_and_data(serf_bucket_t *bucket)
Packit 3adb1e
{
Packit 3adb1e
    aggregate_context_t *ctx = bucket->data;
Packit 3adb1e
    bucket_list_t *next_ctx;
Packit 3adb1e
Packit 3adb1e
    while (ctx->list) {
Packit 3adb1e
        if (ctx->bucket_owner) {
Packit 3adb1e
            serf_bucket_destroy(ctx->list->bucket);
Packit 3adb1e
        }
Packit 3adb1e
        next_ctx = ctx->list->next;
Packit 3adb1e
        serf_bucket_mem_free(bucket->allocator, ctx->list);
Packit 3adb1e
        ctx->list = next_ctx;
Packit 3adb1e
    }
Packit 3adb1e
    cleanup_aggregate(ctx, bucket->allocator);
Packit 3adb1e
Packit 3adb1e
    serf_default_destroy_and_data(bucket);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
void serf_bucket_aggregate_become(serf_bucket_t *bucket)
Packit 3adb1e
{
Packit 3adb1e
    aggregate_context_t *ctx;
Packit 3adb1e
Packit 3adb1e
    ctx = create_aggregate(bucket->allocator);
Packit 3adb1e
Packit 3adb1e
    bucket->type = &serf_bucket_type_aggregate;
Packit 3adb1e
    bucket->data = ctx;
Packit 3adb1e
Packit 3adb1e
    /* The allocator remains the same. */
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
void serf_bucket_aggregate_prepend(
Packit 3adb1e
    serf_bucket_t *aggregate_bucket,
Packit 3adb1e
    serf_bucket_t *prepend_bucket)
Packit 3adb1e
{
Packit 3adb1e
    aggregate_context_t *ctx = aggregate_bucket->data;
Packit 3adb1e
    bucket_list_t *new_list;
Packit 3adb1e
Packit 3adb1e
    new_list = serf_bucket_mem_alloc(aggregate_bucket->allocator,
Packit 3adb1e
                                     sizeof(*new_list));
Packit 3adb1e
    new_list->bucket = prepend_bucket;
Packit 3adb1e
    new_list->next = ctx->list;
Packit 3adb1e
Packit 3adb1e
    ctx->list = new_list;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
void serf_bucket_aggregate_append(
Packit 3adb1e
    serf_bucket_t *aggregate_bucket,
Packit 3adb1e
    serf_bucket_t *append_bucket)
Packit 3adb1e
{
Packit 3adb1e
    aggregate_context_t *ctx = aggregate_bucket->data;
Packit 3adb1e
    bucket_list_t *new_list;
Packit 3adb1e
Packit 3adb1e
    new_list = serf_bucket_mem_alloc(aggregate_bucket->allocator,
Packit 3adb1e
                                     sizeof(*new_list));
Packit 3adb1e
    new_list->bucket = append_bucket;
Packit 3adb1e
    new_list->next = NULL;
Packit 3adb1e
Packit 3adb1e
    /* If we use APR_RING, this is trivial.  So, wait.
Packit 3adb1e
    new_list->next = ctx->list;
Packit 3adb1e
    ctx->list = new_list;
Packit 3adb1e
    */
Packit 3adb1e
    if (ctx->list == NULL) {
Packit 3adb1e
        ctx->list = new_list;
Packit 3adb1e
        ctx->last = new_list;
Packit 3adb1e
    }
Packit 3adb1e
    else {
Packit 3adb1e
        ctx->last->next = new_list;
Packit 3adb1e
        ctx->last = ctx->last->next;
Packit 3adb1e
    }
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
void serf_bucket_aggregate_hold_open(serf_bucket_t *aggregate_bucket, 
Packit 3adb1e
                                     serf_bucket_aggregate_eof_t fn,
Packit 3adb1e
                                     void *baton)
Packit 3adb1e
{
Packit 3adb1e
    aggregate_context_t *ctx = aggregate_bucket->data;
Packit 3adb1e
    ctx->hold_open = fn;
Packit 3adb1e
    ctx->hold_open_baton = baton;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
void serf_bucket_aggregate_prepend_iovec(
Packit 3adb1e
    serf_bucket_t *aggregate_bucket,
Packit 3adb1e
    struct iovec *vecs,
Packit 3adb1e
    int vecs_count)
Packit 3adb1e
{
Packit 3adb1e
    int i;
Packit 3adb1e
Packit 3adb1e
    /* Add in reverse order. */
Packit 3adb1e
    for (i = vecs_count - 1; i >= 0; i--) {
Packit 3adb1e
        serf_bucket_t *new_bucket;
Packit 3adb1e
Packit 3adb1e
        new_bucket = serf_bucket_simple_create(vecs[i].iov_base,
Packit 3adb1e
                                               vecs[i].iov_len,
Packit 3adb1e
                                               NULL, NULL,
Packit 3adb1e
                                               aggregate_bucket->allocator);
Packit 3adb1e
Packit 3adb1e
        serf_bucket_aggregate_prepend(aggregate_bucket, new_bucket);
Packit 3adb1e
Packit 3adb1e
    }
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
void serf_bucket_aggregate_append_iovec(
Packit 3adb1e
    serf_bucket_t *aggregate_bucket,
Packit 3adb1e
    struct iovec *vecs,
Packit 3adb1e
    int vecs_count)
Packit 3adb1e
{
Packit 3adb1e
    serf_bucket_t *new_bucket;
Packit 3adb1e
Packit 3adb1e
    new_bucket = serf_bucket_iovec_create(vecs, vecs_count,
Packit 3adb1e
                                          aggregate_bucket->allocator);
Packit 3adb1e
Packit 3adb1e
    serf_bucket_aggregate_append(aggregate_bucket, new_bucket);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t read_aggregate(serf_bucket_t *bucket,
Packit 3adb1e
                                   apr_size_t requested,
Packit 3adb1e
                                   int vecs_size, struct iovec *vecs,
Packit 3adb1e
                                   int *vecs_used)
Packit 3adb1e
{
Packit 3adb1e
    aggregate_context_t *ctx = bucket->data;
Packit 3adb1e
    int cur_vecs_used;
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
Packit 3adb1e
    *vecs_used = 0;
Packit 3adb1e
Packit 3adb1e
    if (!ctx->list) {
Packit 3adb1e
        if (ctx->hold_open) {
Packit 3adb1e
            return ctx->hold_open(ctx->hold_open_baton, bucket);
Packit 3adb1e
        }
Packit 3adb1e
        else {
Packit 3adb1e
            return APR_EOF;
Packit 3adb1e
        }
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    status = APR_SUCCESS;
Packit 3adb1e
    while (requested) {
Packit 3adb1e
        serf_bucket_t *head = ctx->list->bucket;
Packit 3adb1e
Packit 3adb1e
        status = serf_bucket_read_iovec(head, requested, vecs_size, vecs,
Packit 3adb1e
                                        &cur_vecs_used);
Packit 3adb1e
Packit 3adb1e
        if (SERF_BUCKET_READ_ERROR(status))
Packit 3adb1e
            return status;
Packit 3adb1e
Packit 3adb1e
        /* Add the number of vecs we read to our running total. */
Packit 3adb1e
        *vecs_used += cur_vecs_used;
Packit 3adb1e
Packit 3adb1e
        if (cur_vecs_used > 0 || status) {
Packit 3adb1e
            bucket_list_t *next_list;
Packit 3adb1e
Packit 3adb1e
            /* If we got SUCCESS (w/bytes) or EAGAIN, we want to return now
Packit 3adb1e
             * as it isn't safe to read more without returning to our caller.
Packit 3adb1e
             */
Packit 3adb1e
            if (!status || APR_STATUS_IS_EAGAIN(status) || status == SERF_ERROR_WAIT_CONN) {
Packit 3adb1e
                return status;
Packit 3adb1e
            }
Packit 3adb1e
Packit 3adb1e
            /* However, if we read EOF, we can stash this bucket in a
Packit 3adb1e
             * to-be-freed list and move on to the next bucket.  This ensures
Packit 3adb1e
             * that the bucket stays alive (so as not to violate our read
Packit 3adb1e
             * semantics).  We'll destroy this list of buckets the next time
Packit 3adb1e
             * we are asked to perform a read operation - thus ensuring the
Packit 3adb1e
             * proper read lifetime.
Packit 3adb1e
             */
Packit 3adb1e
            next_list = ctx->list->next;
Packit 3adb1e
            ctx->list->next = ctx->done;
Packit 3adb1e
            ctx->done = ctx->list;
Packit 3adb1e
            ctx->list = next_list;
Packit 3adb1e
Packit 3adb1e
            /* If we have no more in our list, return EOF. */
Packit 3adb1e
            if (!ctx->list) {
Packit 3adb1e
                if (ctx->hold_open) {
Packit 3adb1e
                    return ctx->hold_open(ctx->hold_open_baton, bucket);
Packit 3adb1e
                }
Packit 3adb1e
                else {
Packit 3adb1e
                    return APR_EOF;
Packit 3adb1e
                }
Packit 3adb1e
            }
Packit 3adb1e
Packit 3adb1e
            /* At this point, it safe to read the next bucket - if we can. */
Packit 3adb1e
Packit 3adb1e
            /* If the caller doesn't want ALL_AVAIL, decrement the size
Packit 3adb1e
             * of the items we just read from the list.
Packit 3adb1e
             */
Packit 3adb1e
            if (requested != SERF_READ_ALL_AVAIL) {
Packit 3adb1e
                int i;
Packit 3adb1e
Packit 3adb1e
                for (i = 0; i < cur_vecs_used; i++)
Packit 3adb1e
                    requested -= vecs[i].iov_len;
Packit 3adb1e
            }
Packit 3adb1e
Packit 3adb1e
            /* Adjust our vecs to account for what we just read. */
Packit 3adb1e
            vecs_size -= cur_vecs_used;
Packit 3adb1e
            vecs += cur_vecs_used;
Packit 3adb1e
Packit 3adb1e
            /* We reached our max.  Oh well. */
Packit 3adb1e
            if (!requested || !vecs_size) {
Packit 3adb1e
                return APR_SUCCESS;
Packit 3adb1e
            }
Packit 3adb1e
        }
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    return status;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t serf_aggregate_read(serf_bucket_t *bucket,
Packit 3adb1e
                                        apr_size_t requested,
Packit 3adb1e
                                        const char **data, apr_size_t *len)
Packit 3adb1e
{
Packit 3adb1e
    aggregate_context_t *ctx = bucket->data;
Packit 3adb1e
    struct iovec vec;
Packit 3adb1e
    int vecs_used;
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
Packit 3adb1e
    cleanup_aggregate(ctx, bucket->allocator);
Packit 3adb1e
Packit 3adb1e
    status = read_aggregate(bucket, requested, 1, &vec, &vecs_used);
Packit 3adb1e
Packit 3adb1e
    if (!vecs_used) {
Packit 3adb1e
        *len = 0;
Packit 3adb1e
    }
Packit 3adb1e
    else {
Packit 3adb1e
        *data = vec.iov_base;
Packit 3adb1e
        *len = vec.iov_len;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    return status;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t serf_aggregate_read_iovec(serf_bucket_t *bucket,
Packit 3adb1e
                                              apr_size_t requested,
Packit 3adb1e
                                              int vecs_size,
Packit 3adb1e
                                              struct iovec *vecs,
Packit 3adb1e
                                              int *vecs_used)
Packit 3adb1e
{
Packit 3adb1e
    aggregate_context_t *ctx = bucket->data;
Packit 3adb1e
Packit 3adb1e
    cleanup_aggregate(ctx, bucket->allocator);
Packit 3adb1e
Packit 3adb1e
    return read_aggregate(bucket, requested, vecs_size, vecs, vecs_used);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t serf_aggregate_readline(serf_bucket_t *bucket,
Packit 3adb1e
                                            int acceptable, int *found,
Packit 3adb1e
                                            const char **data, apr_size_t *len)
Packit 3adb1e
{
Packit 3adb1e
    aggregate_context_t *ctx = bucket->data;
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
Packit 3adb1e
    cleanup_aggregate(ctx, bucket->allocator);
Packit 3adb1e
Packit 3adb1e
    do {
Packit 3adb1e
        serf_bucket_t *head;
Packit 3adb1e
Packit 3adb1e
        *len = 0;
Packit 3adb1e
Packit 3adb1e
        if (!ctx->list) {
Packit 3adb1e
            if (ctx->hold_open) {
Packit 3adb1e
                return ctx->hold_open(ctx->hold_open_baton, bucket);
Packit 3adb1e
            }
Packit 3adb1e
            else {
Packit 3adb1e
                return APR_EOF;
Packit 3adb1e
            }
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        head = ctx->list->bucket;
Packit 3adb1e
Packit 3adb1e
        status = serf_bucket_readline(head, acceptable, found,
Packit 3adb1e
                                      data, len);
Packit 3adb1e
        if (SERF_BUCKET_READ_ERROR(status))
Packit 3adb1e
            return status;
Packit 3adb1e
Packit 3adb1e
        if (status == APR_EOF) {
Packit 3adb1e
            bucket_list_t *next_list;
Packit 3adb1e
Packit 3adb1e
            /* head bucket is empty, move to to-be-cleaned-up list. */
Packit 3adb1e
            next_list = ctx->list->next;
Packit 3adb1e
            ctx->list->next = ctx->done;
Packit 3adb1e
            ctx->done = ctx->list;
Packit 3adb1e
            ctx->list = next_list;
Packit 3adb1e
Packit 3adb1e
            /* If we have no more in our list, return EOF. */
Packit 3adb1e
            if (!ctx->list) {
Packit 3adb1e
                if (ctx->hold_open) {
Packit 3adb1e
                    return ctx->hold_open(ctx->hold_open_baton, bucket);
Packit 3adb1e
                }
Packit 3adb1e
                else {
Packit 3adb1e
                    return APR_EOF;
Packit 3adb1e
                }
Packit 3adb1e
            }
Packit 3adb1e
Packit 3adb1e
            /* we read something, so bail out and let the appl. read again. */
Packit 3adb1e
            if (*len)
Packit 3adb1e
                status = APR_SUCCESS;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        /* continue with APR_SUCCESS or APR_EOF and no data read yet. */
Packit 3adb1e
    } while (!*len && status != APR_EAGAIN);
Packit 3adb1e
Packit 3adb1e
    return status;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t serf_aggregate_peek(serf_bucket_t *bucket,
Packit 3adb1e
                                        const char **data,
Packit 3adb1e
                                        apr_size_t *len)
Packit 3adb1e
{
Packit 3adb1e
    aggregate_context_t *ctx = bucket->data;
Packit 3adb1e
    serf_bucket_t *head;
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
Packit 3adb1e
    cleanup_aggregate(ctx, bucket->allocator);
Packit 3adb1e
Packit 3adb1e
    /* Peek the first bucket in the list, if any. */
Packit 3adb1e
    if (!ctx->list) {
Packit 3adb1e
        *len = 0;
Packit 3adb1e
        if (ctx->hold_open) {
Packit 3adb1e
            status = ctx->hold_open(ctx->hold_open_baton, bucket);
Packit 3adb1e
            if (status == APR_EAGAIN)
Packit 3adb1e
                status = APR_SUCCESS;
Packit 3adb1e
            return status;
Packit 3adb1e
        }
Packit 3adb1e
        else {
Packit 3adb1e
            return APR_EOF;
Packit 3adb1e
        }
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    head = ctx->list->bucket;
Packit 3adb1e
Packit 3adb1e
    status = serf_bucket_peek(head, data, len);
Packit 3adb1e
Packit 3adb1e
    if (status == APR_EOF) {
Packit 3adb1e
        if (ctx->list->next) {
Packit 3adb1e
            status = APR_SUCCESS;
Packit 3adb1e
        } else {
Packit 3adb1e
            if (ctx->hold_open) {
Packit 3adb1e
                status = ctx->hold_open(ctx->hold_open_baton, bucket);
Packit 3adb1e
                if (status == APR_EAGAIN)
Packit 3adb1e
                    status = APR_SUCCESS;
Packit 3adb1e
                return status;
Packit 3adb1e
            }
Packit 3adb1e
        }
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    return status;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static serf_bucket_t * serf_aggregate_read_bucket(
Packit 3adb1e
    serf_bucket_t *bucket,
Packit 3adb1e
    const serf_bucket_type_t *type)
Packit 3adb1e
{
Packit 3adb1e
    aggregate_context_t *ctx = bucket->data;
Packit 3adb1e
    serf_bucket_t *found_bucket;
Packit 3adb1e
Packit 3adb1e
    if (!ctx->list) {
Packit 3adb1e
        return NULL;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    if (ctx->list->bucket->type == type) {
Packit 3adb1e
        /* Got the bucket. Consume it from our list. */
Packit 3adb1e
        found_bucket = ctx->list->bucket;
Packit 3adb1e
        ctx->list = ctx->list->next;
Packit 3adb1e
        return found_bucket;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* Call read_bucket on first one in our list. */
Packit 3adb1e
    return serf_bucket_read_bucket(ctx->list->bucket, type);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
const serf_bucket_type_t serf_bucket_type_aggregate = {
Packit 3adb1e
    "AGGREGATE",
Packit 3adb1e
    serf_aggregate_read,
Packit 3adb1e
    serf_aggregate_readline,
Packit 3adb1e
    serf_aggregate_read_iovec,
Packit 3adb1e
    serf_default_read_for_sendfile,
Packit 3adb1e
    serf_aggregate_read_bucket,
Packit 3adb1e
    serf_aggregate_peek,
Packit 3adb1e
    serf_aggregate_destroy_and_data,
Packit 3adb1e
};