Blame buckets/response_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 <apr_lib.h>
Packit 3adb1e
#include <apr_strings.h>
Packit 3adb1e
#include <apr_date.h>
Packit 3adb1e
Packit 3adb1e
#include "serf.h"
Packit 3adb1e
#include "serf_bucket_util.h"
Packit 3adb1e
#include "serf_private.h"
Packit 3adb1e
Packit 3adb1e
typedef struct {
Packit 3adb1e
    serf_bucket_t *stream;
Packit 3adb1e
    serf_bucket_t *body;        /* Pointer to the stream wrapping the body. */
Packit 3adb1e
    serf_bucket_t *headers;     /* holds parsed headers */
Packit 3adb1e
Packit 3adb1e
    enum {
Packit 3adb1e
        STATE_STATUS_LINE,      /* reading status line */
Packit 3adb1e
        STATE_HEADERS,          /* reading headers */
Packit 3adb1e
        STATE_BODY,             /* reading body */
Packit 3adb1e
        STATE_TRAILERS,         /* reading trailers */
Packit 3adb1e
        STATE_DONE              /* we've sent EOF */
Packit 3adb1e
    } state;
Packit 3adb1e
Packit 3adb1e
    /* Buffer for accumulating a line from the response. */
Packit 3adb1e
    serf_linebuf_t linebuf;
Packit 3adb1e
Packit 3adb1e
    serf_status_line sl;
Packit 3adb1e
Packit 3adb1e
    int chunked;                /* Do we need to read trailers? */
Packit 3adb1e
    int head_req;               /* Was this a HEAD request? */
Packit 3adb1e
} response_context_t;
Packit 3adb1e
Packit 3adb1e
/* Returns 1 if according to RFC2626 this response can have a body, 0 if it
Packit 3adb1e
   must not have a body. */
Packit 3adb1e
static int expect_body(response_context_t *ctx)
Packit 3adb1e
{
Packit 3adb1e
    if (ctx->head_req)
Packit 3adb1e
        return 0;
Packit 3adb1e
Packit 3adb1e
    /* 100 Continue and 101 Switching Protocols */
Packit 3adb1e
    if (ctx->sl.code >= 100 && ctx->sl.code < 200)
Packit 3adb1e
        return 0;
Packit 3adb1e
Packit 3adb1e
    /* 204 No Content */
Packit 3adb1e
    if (ctx->sl.code == 204)
Packit 3adb1e
        return 0;
Packit 3adb1e
Packit 3adb1e
    /* 205? */
Packit 3adb1e
Packit 3adb1e
    /* 304 Not Modified */
Packit 3adb1e
    if (ctx->sl.code == 304)
Packit 3adb1e
        return 0;
Packit 3adb1e
Packit 3adb1e
    return 1;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
serf_bucket_t *serf_bucket_response_create(
Packit 3adb1e
    serf_bucket_t *stream,
Packit 3adb1e
    serf_bucket_alloc_t *allocator)
Packit 3adb1e
{
Packit 3adb1e
    response_context_t *ctx;
Packit 3adb1e
Packit 3adb1e
    ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx));
Packit 3adb1e
    ctx->stream = stream;
Packit 3adb1e
    ctx->body = NULL;
Packit 3adb1e
    ctx->headers = serf_bucket_headers_create(allocator);
Packit 3adb1e
    ctx->state = STATE_STATUS_LINE;
Packit 3adb1e
    ctx->chunked = 0;
Packit 3adb1e
    ctx->head_req = 0;
Packit 3adb1e
Packit 3adb1e
    serf_linebuf_init(&ctx->linebuf);
Packit 3adb1e
Packit 3adb1e
    return serf_bucket_create(&serf_bucket_type_response, allocator, ctx);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
void serf_bucket_response_set_head(
Packit 3adb1e
    serf_bucket_t *bucket)
Packit 3adb1e
{
Packit 3adb1e
    response_context_t *ctx = bucket->data;
Packit 3adb1e
Packit 3adb1e
    ctx->head_req = 1;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
serf_bucket_t *serf_bucket_response_get_headers(
Packit 3adb1e
    serf_bucket_t *bucket)
Packit 3adb1e
{
Packit 3adb1e
    return ((response_context_t *)bucket->data)->headers;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
static void serf_response_destroy_and_data(serf_bucket_t *bucket)
Packit 3adb1e
{
Packit 3adb1e
    response_context_t *ctx = bucket->data;
Packit 3adb1e
Packit 3adb1e
    if (ctx->state != STATE_STATUS_LINE) {
Packit 3adb1e
        serf_bucket_mem_free(bucket->allocator, (void*)ctx->sl.reason);
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    serf_bucket_destroy(ctx->stream);
Packit 3adb1e
    if (ctx->body != NULL)
Packit 3adb1e
        serf_bucket_destroy(ctx->body);
Packit 3adb1e
    serf_bucket_destroy(ctx->headers);
Packit 3adb1e
Packit 3adb1e
    serf_default_destroy_and_data(bucket);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t fetch_line(response_context_t *ctx, int acceptable)
Packit 3adb1e
{
Packit 3adb1e
    return serf_linebuf_fetch(&ctx->linebuf, ctx->stream, acceptable);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t parse_status_line(response_context_t *ctx,
Packit 3adb1e
                                      serf_bucket_alloc_t *allocator)
Packit 3adb1e
{
Packit 3adb1e
    int res;
Packit 3adb1e
    char *reason; /* ### stupid APR interface makes this non-const */
Packit 3adb1e
Packit 3adb1e
    /* Ensure a valid length, to avoid overflow on the final '\0' */
Packit 3adb1e
    if (ctx->linebuf.used >= SERF_LINEBUF_LIMIT) {
Packit 3adb1e
       return SERF_ERROR_BAD_HTTP_RESPONSE;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* apr_date_checkmask assumes its arguments are valid C strings */
Packit 3adb1e
    ctx->linebuf.line[ctx->linebuf.used] = '\0';
Packit 3adb1e
Packit 3adb1e
    /* ctx->linebuf.line should be of form: 'HTTP/1.1 200 OK',
Packit 3adb1e
       but we also explicitly allow the forms 'HTTP/1.1 200' (no reason)
Packit 3adb1e
       and 'HTTP/1.1 401.1 Logon failed' (iis extended error codes) */
Packit 3adb1e
    res = apr_date_checkmask(ctx->linebuf.line, "HTTP/#.# ###*");
Packit 3adb1e
    if (!res) {
Packit 3adb1e
        /* Not an HTTP response?  Well, at least we won't understand it. */
Packit 3adb1e
        return SERF_ERROR_BAD_HTTP_RESPONSE;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    ctx->sl.version = SERF_HTTP_VERSION(ctx->linebuf.line[5] - '0',
Packit 3adb1e
                                        ctx->linebuf.line[7] - '0');
Packit 3adb1e
    ctx->sl.code = apr_strtoi64(ctx->linebuf.line + 8, &reason, 10);
Packit 3adb1e
Packit 3adb1e
    /* Skip leading spaces for the reason string. */
Packit 3adb1e
    if (apr_isspace(*reason)) {
Packit 3adb1e
        reason++;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* Copy the reason value out of the line buffer. */
Packit 3adb1e
    ctx->sl.reason = serf_bstrmemdup(allocator, reason,
Packit 3adb1e
                                     ctx->linebuf.used
Packit 3adb1e
                                     - (reason - ctx->linebuf.line));
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
/* This code should be replaced with header buckets. */
Packit 3adb1e
static apr_status_t fetch_headers(serf_bucket_t *bkt, response_context_t *ctx)
Packit 3adb1e
{
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
Packit 3adb1e
    /* RFC 2616 says that CRLF is the only line ending, but we can easily
Packit 3adb1e
     * accept any kind of line ending.
Packit 3adb1e
     */
Packit 3adb1e
    status = fetch_line(ctx, SERF_NEWLINE_ANY);
Packit 3adb1e
    if (SERF_BUCKET_READ_ERROR(status)) {
Packit 3adb1e
        return status;
Packit 3adb1e
    }
Packit 3adb1e
    /* Something was read. Process it. */
Packit 3adb1e
Packit 3adb1e
    if (ctx->linebuf.state == SERF_LINEBUF_READY && ctx->linebuf.used) {
Packit 3adb1e
        const char *end_key;
Packit 3adb1e
        const char *c;
Packit 3adb1e
Packit 3adb1e
        end_key = c = memchr(ctx->linebuf.line, ':', ctx->linebuf.used);
Packit 3adb1e
        if (!c) {
Packit 3adb1e
            /* Bad headers? */
Packit 3adb1e
            return SERF_ERROR_BAD_HTTP_RESPONSE;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        /* Skip over initial ':' */
Packit 3adb1e
        c++;
Packit 3adb1e
Packit 3adb1e
        /* And skip all whitespaces. */
Packit 3adb1e
        for(; c < ctx->linebuf.line + ctx->linebuf.used; c++)
Packit 3adb1e
        {
Packit 3adb1e
            if (!apr_isspace(*c))
Packit 3adb1e
            {
Packit 3adb1e
              break;
Packit 3adb1e
            }
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        /* Always copy the headers (from the linebuf into new mem). */
Packit 3adb1e
        /* ### we should be able to optimize some mem copies */
Packit 3adb1e
        serf_bucket_headers_setx(
Packit 3adb1e
            ctx->headers,
Packit 3adb1e
            ctx->linebuf.line, end_key - ctx->linebuf.line, 1,
Packit 3adb1e
            c, ctx->linebuf.line + ctx->linebuf.used - c, 1);
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    return status;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
/* Perform one iteration of the state machine.
Packit 3adb1e
 *
Packit 3adb1e
 * Will return when one the following conditions occurred:
Packit 3adb1e
 *  1) a state change
Packit 3adb1e
 *  2) an error
Packit 3adb1e
 *  3) the stream is not ready or at EOF
Packit 3adb1e
 *  4) APR_SUCCESS, meaning the machine can be run again immediately
Packit 3adb1e
 */
Packit 3adb1e
static apr_status_t run_machine(serf_bucket_t *bkt, response_context_t *ctx)
Packit 3adb1e
{
Packit 3adb1e
    apr_status_t status = APR_SUCCESS; /* initialize to avoid gcc warnings */
Packit 3adb1e
Packit 3adb1e
    switch (ctx->state) {
Packit 3adb1e
    case STATE_STATUS_LINE:
Packit 3adb1e
        /* RFC 2616 says that CRLF is the only line ending, but we can easily
Packit 3adb1e
         * accept any kind of line ending.
Packit 3adb1e
         */
Packit 3adb1e
        status = fetch_line(ctx, SERF_NEWLINE_ANY);
Packit 3adb1e
        if (SERF_BUCKET_READ_ERROR(status))
Packit 3adb1e
            return status;
Packit 3adb1e
Packit 3adb1e
        if (ctx->linebuf.state == SERF_LINEBUF_READY) {
Packit 3adb1e
            /* The Status-Line is in the line buffer. Process it. */
Packit 3adb1e
            status = parse_status_line(ctx, bkt->allocator);
Packit 3adb1e
            if (status)
Packit 3adb1e
                return status;
Packit 3adb1e
Packit 3adb1e
            /* Good times ahead: we're switching protocols! */
Packit 3adb1e
            if (ctx->sl.code == 101) {
Packit 3adb1e
                ctx->body =
Packit 3adb1e
                    serf_bucket_barrier_create(ctx->stream, bkt->allocator);
Packit 3adb1e
                ctx->state = STATE_DONE;
Packit 3adb1e
                break;
Packit 3adb1e
            }
Packit 3adb1e
Packit 3adb1e
            /* Okay... move on to reading the headers. */
Packit 3adb1e
            ctx->state = STATE_HEADERS;
Packit 3adb1e
        }
Packit 3adb1e
        else {
Packit 3adb1e
            /* The connection closed before we could get the next
Packit 3adb1e
             * response.  Treat the request as lost so that our upper
Packit 3adb1e
             * end knows the server never tried to give us a response.
Packit 3adb1e
             */
Packit 3adb1e
            if (APR_STATUS_IS_EOF(status)) {
Packit 3adb1e
                return SERF_ERROR_REQUEST_LOST;
Packit 3adb1e
            }
Packit 3adb1e
        }
Packit 3adb1e
        break;
Packit 3adb1e
    case STATE_HEADERS:
Packit 3adb1e
        status = fetch_headers(bkt, ctx);
Packit 3adb1e
        if (SERF_BUCKET_READ_ERROR(status))
Packit 3adb1e
            return status;
Packit 3adb1e
Packit 3adb1e
        /* If an empty line was read, then we hit the end of the headers.
Packit 3adb1e
         * Move on to the body.
Packit 3adb1e
         */
Packit 3adb1e
        if (ctx->linebuf.state == SERF_LINEBUF_READY && !ctx->linebuf.used) {
Packit 3adb1e
            const void *v;
Packit 3adb1e
Packit 3adb1e
            /* Advance the state. */
Packit 3adb1e
            ctx->state = STATE_BODY;
Packit 3adb1e
Packit 3adb1e
            /* If this is a response to a HEAD request, or code == 1xx,204 or304
Packit 3adb1e
               then we don't receive a real body. */
Packit 3adb1e
            if (!expect_body(ctx)) {
Packit 3adb1e
                ctx->body = serf_bucket_simple_create(NULL, 0, NULL, NULL,
Packit 3adb1e
                                                      bkt->allocator);
Packit 3adb1e
                ctx->state = STATE_BODY;
Packit 3adb1e
                break;
Packit 3adb1e
            }
Packit 3adb1e
Packit 3adb1e
            ctx->body =
Packit 3adb1e
                serf_bucket_barrier_create(ctx->stream, bkt->allocator);
Packit 3adb1e
Packit 3adb1e
            /* Are we C-L, chunked, or conn close? */
Packit 3adb1e
            v = serf_bucket_headers_get(ctx->headers, "Content-Length");
Packit 3adb1e
            if (v) {
Packit 3adb1e
                apr_uint64_t length;
Packit 3adb1e
                length = apr_strtoi64(v, NULL, 10);
Packit 3adb1e
                if (errno == ERANGE) {
Packit 3adb1e
                    return APR_FROM_OS_ERROR(ERANGE);
Packit 3adb1e
                }
Packit 3adb1e
                ctx->body = serf_bucket_response_body_create(
Packit 3adb1e
                              ctx->body, length, bkt->allocator);
Packit 3adb1e
            }
Packit 3adb1e
            else {
Packit 3adb1e
                v = serf_bucket_headers_get(ctx->headers, "Transfer-Encoding");
Packit 3adb1e
Packit 3adb1e
                /* Need to handle multiple transfer-encoding. */
Packit 3adb1e
                if (v && strcasecmp("chunked", v) == 0) {
Packit 3adb1e
                    ctx->chunked = 1;
Packit 3adb1e
                    ctx->body = serf_bucket_dechunk_create(ctx->body,
Packit 3adb1e
                                                           bkt->allocator);
Packit 3adb1e
                }
Packit 3adb1e
            }
Packit 3adb1e
            v = serf_bucket_headers_get(ctx->headers, "Content-Encoding");
Packit 3adb1e
            if (v) {
Packit 3adb1e
                /* Need to handle multiple content-encoding. */
Packit 3adb1e
                if (v && strcasecmp("gzip", v) == 0) {
Packit 3adb1e
                    ctx->body =
Packit 3adb1e
                        serf_bucket_deflate_create(ctx->body, bkt->allocator,
Packit 3adb1e
                                                   SERF_DEFLATE_GZIP);
Packit 3adb1e
                }
Packit 3adb1e
                else if (v && strcasecmp("deflate", v) == 0) {
Packit 3adb1e
                    ctx->body =
Packit 3adb1e
                        serf_bucket_deflate_create(ctx->body, bkt->allocator,
Packit 3adb1e
                                                   SERF_DEFLATE_DEFLATE);
Packit 3adb1e
                }
Packit 3adb1e
            }
Packit 3adb1e
        }
Packit 3adb1e
        break;
Packit 3adb1e
    case STATE_BODY:
Packit 3adb1e
        /* Don't do anything. */
Packit 3adb1e
        break;
Packit 3adb1e
    case STATE_TRAILERS:
Packit 3adb1e
        status = fetch_headers(bkt, ctx);
Packit 3adb1e
        if (SERF_BUCKET_READ_ERROR(status))
Packit 3adb1e
            return status;
Packit 3adb1e
Packit 3adb1e
        /* If an empty line was read, then we're done. */
Packit 3adb1e
        if (ctx->linebuf.state == SERF_LINEBUF_READY && !ctx->linebuf.used) {
Packit 3adb1e
            ctx->state = STATE_DONE;
Packit 3adb1e
            return APR_EOF;
Packit 3adb1e
        }
Packit 3adb1e
        break;
Packit 3adb1e
    case STATE_DONE:
Packit 3adb1e
        return APR_EOF;
Packit 3adb1e
    default:
Packit 3adb1e
        /* Not reachable */
Packit 3adb1e
        return APR_EGENERAL;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    return status;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t wait_for_body(serf_bucket_t *bkt, response_context_t *ctx)
Packit 3adb1e
{
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
Packit 3adb1e
    /* Keep reading and moving through states if we aren't at the BODY */
Packit 3adb1e
    while (ctx->state != STATE_BODY) {
Packit 3adb1e
        status = run_machine(bkt, ctx);
Packit 3adb1e
Packit 3adb1e
        /* Anything other than APR_SUCCESS means that we cannot immediately
Packit 3adb1e
         * read again (for now).
Packit 3adb1e
         */
Packit 3adb1e
        if (status)
Packit 3adb1e
            return status;
Packit 3adb1e
    }
Packit 3adb1e
    /* in STATE_BODY */
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
apr_status_t serf_bucket_response_wait_for_headers(
Packit 3adb1e
    serf_bucket_t *bucket)
Packit 3adb1e
{
Packit 3adb1e
    response_context_t *ctx = bucket->data;
Packit 3adb1e
Packit 3adb1e
    return wait_for_body(bucket, ctx);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
apr_status_t serf_bucket_response_status(
Packit 3adb1e
    serf_bucket_t *bkt,
Packit 3adb1e
    serf_status_line *sline)
Packit 3adb1e
{
Packit 3adb1e
    response_context_t *ctx = bkt->data;
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
Packit 3adb1e
    if (ctx->state != STATE_STATUS_LINE) {
Packit 3adb1e
        /* We already read it and moved on. Just return it. */
Packit 3adb1e
        *sline = ctx->sl;
Packit 3adb1e
        return APR_SUCCESS;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* Running the state machine once will advance the machine, or state
Packit 3adb1e
     * that the stream isn't ready with enough data. There isn't ever a
Packit 3adb1e
     * need to run the machine more than once to try and satisfy this. We
Packit 3adb1e
     * have to look at the state to tell whether it advanced, though, as
Packit 3adb1e
     * it is quite possible to advance *and* to return APR_EAGAIN.
Packit 3adb1e
     */
Packit 3adb1e
    status = run_machine(bkt, ctx);
Packit 3adb1e
    if (ctx->state == STATE_HEADERS) {
Packit 3adb1e
        *sline = ctx->sl;
Packit 3adb1e
    }
Packit 3adb1e
    else {
Packit 3adb1e
        /* Indicate that we don't have the information yet. */
Packit 3adb1e
        sline->version = 0;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    return status;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t serf_response_read(serf_bucket_t *bucket,
Packit 3adb1e
                                       apr_size_t requested,
Packit 3adb1e
                                       const char **data, apr_size_t *len)
Packit 3adb1e
{
Packit 3adb1e
    response_context_t *ctx = bucket->data;
Packit 3adb1e
    apr_status_t rv;
Packit 3adb1e
Packit 3adb1e
    rv = wait_for_body(bucket, ctx);
Packit 3adb1e
    if (rv) {
Packit 3adb1e
        /* It's not possible to have read anything yet! */
Packit 3adb1e
        if (APR_STATUS_IS_EOF(rv) || APR_STATUS_IS_EAGAIN(rv)) {
Packit 3adb1e
            *len = 0;
Packit 3adb1e
        }
Packit 3adb1e
        return rv;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    rv = serf_bucket_read(ctx->body, requested, data, len);
Packit 3adb1e
    if (SERF_BUCKET_READ_ERROR(rv))
Packit 3adb1e
        return rv;
Packit 3adb1e
Packit 3adb1e
    if (APR_STATUS_IS_EOF(rv)) {
Packit 3adb1e
        if (ctx->chunked) {
Packit 3adb1e
            ctx->state = STATE_TRAILERS;
Packit 3adb1e
            /* Mask the result. */
Packit 3adb1e
            rv = APR_SUCCESS;
Packit 3adb1e
        } else {
Packit 3adb1e
            ctx->state = STATE_DONE;
Packit 3adb1e
        }
Packit 3adb1e
    }
Packit 3adb1e
    return rv;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t serf_response_readline(serf_bucket_t *bucket,
Packit 3adb1e
                                           int acceptable, int *found,
Packit 3adb1e
                                           const char **data, apr_size_t *len)
Packit 3adb1e
{
Packit 3adb1e
    response_context_t *ctx = bucket->data;
Packit 3adb1e
    apr_status_t rv;
Packit 3adb1e
Packit 3adb1e
    rv = wait_for_body(bucket, ctx);
Packit 3adb1e
    if (rv) {
Packit 3adb1e
        return rv;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* Delegate to the stream bucket to do the readline. */
Packit 3adb1e
    return serf_bucket_readline(ctx->body, acceptable, found, data, len);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
apr_status_t serf_response_full_become_aggregate(serf_bucket_t *bucket)
Packit 3adb1e
{
Packit 3adb1e
    response_context_t *ctx = bucket->data;
Packit 3adb1e
    serf_bucket_t *bkt;
Packit 3adb1e
    char buf[256];
Packit 3adb1e
    int size;
Packit 3adb1e
Packit 3adb1e
    serf_bucket_aggregate_become(bucket);
Packit 3adb1e
Packit 3adb1e
    /* Add reconstructed status line. */
Packit 3adb1e
    size = apr_snprintf(buf, 256, "HTTP/%d.%d %d ",
Packit 3adb1e
                        SERF_HTTP_VERSION_MAJOR(ctx->sl.version),
Packit 3adb1e
                        SERF_HTTP_VERSION_MINOR(ctx->sl.version),
Packit 3adb1e
                        ctx->sl.code);
Packit 3adb1e
    bkt = serf_bucket_simple_copy_create(buf, size,
Packit 3adb1e
                                         bucket->allocator);
Packit 3adb1e
    serf_bucket_aggregate_append(bucket, bkt);
Packit 3adb1e
    bkt = serf_bucket_simple_copy_create(ctx->sl.reason, strlen(ctx->sl.reason),
Packit 3adb1e
                                         bucket->allocator);
Packit 3adb1e
    serf_bucket_aggregate_append(bucket, bkt);
Packit 3adb1e
    bkt = SERF_BUCKET_SIMPLE_STRING_LEN("\r\n", 2,
Packit 3adb1e
                                        bucket->allocator);
Packit 3adb1e
    serf_bucket_aggregate_append(bucket, bkt);
Packit 3adb1e
Packit 3adb1e
    /* Add headers and stream buckets in order. */
Packit 3adb1e
    serf_bucket_aggregate_append(bucket, ctx->headers);
Packit 3adb1e
    serf_bucket_aggregate_append(bucket, ctx->stream);
Packit 3adb1e
Packit 3adb1e
    serf_bucket_mem_free(bucket->allocator, ctx);
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
/* ### need to implement */
Packit 3adb1e
#define serf_response_peek NULL
Packit 3adb1e
Packit 3adb1e
const serf_bucket_type_t serf_bucket_type_response = {
Packit 3adb1e
    "RESPONSE",
Packit 3adb1e
    serf_response_read,
Packit 3adb1e
    serf_response_readline,
Packit 3adb1e
    serf_default_read_iovec,
Packit 3adb1e
    serf_default_read_for_sendfile,
Packit 3adb1e
    serf_default_read_bucket,
Packit 3adb1e
    serf_response_peek,
Packit 3adb1e
    serf_response_destroy_and_data,
Packit 3adb1e
};