|
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 |
};
|