Blame buckets/headers_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 <stdlib.h>
Packit 3adb1e
Packit 3adb1e
#include <apr_general.h>  /* for strcasecmp() */
Packit 3adb1e
Packit 3adb1e
#include "serf.h"
Packit 3adb1e
#include "serf_bucket_util.h"
Packit 3adb1e
Packit 3adb1e
#include "serf_private.h" /* for serf__bucket_headers_remove */
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
typedef struct header_list {
Packit 3adb1e
    const char *header;
Packit 3adb1e
    const char *value;
Packit 3adb1e
Packit 3adb1e
    apr_size_t header_size;
Packit 3adb1e
    apr_size_t value_size;
Packit 3adb1e
Packit 3adb1e
    int alloc_flags;
Packit 3adb1e
#define ALLOC_HEADER 0x0001  /* header lives in our allocator */
Packit 3adb1e
#define ALLOC_VALUE  0x0002  /* value lives in our allocator */
Packit 3adb1e
Packit 3adb1e
    struct header_list *next;
Packit 3adb1e
} header_list_t;
Packit 3adb1e
Packit 3adb1e
typedef struct {
Packit 3adb1e
    header_list_t *list;
Packit 3adb1e
    header_list_t *last;
Packit 3adb1e
Packit 3adb1e
    header_list_t *cur_read;
Packit 3adb1e
    enum {
Packit 3adb1e
        READ_START,     /* haven't started reading yet */
Packit 3adb1e
        READ_HEADER,    /* reading cur_read->header */
Packit 3adb1e
        READ_SEP,       /* reading ": " */
Packit 3adb1e
        READ_VALUE,     /* reading cur_read->value */
Packit 3adb1e
        READ_CRLF,      /* reading "\r\n" */
Packit 3adb1e
        READ_TERM,      /* reading the final "\r\n" */
Packit 3adb1e
        READ_DONE       /* no more data to read */
Packit 3adb1e
    } state;
Packit 3adb1e
    apr_size_t amt_read; /* how much of the current state we've read */
Packit 3adb1e
Packit 3adb1e
} headers_context_t;
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
serf_bucket_t *serf_bucket_headers_create(
Packit 3adb1e
    serf_bucket_alloc_t *allocator)
Packit 3adb1e
{
Packit 3adb1e
    headers_context_t *ctx;
Packit 3adb1e
Packit 3adb1e
    ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx));
Packit 3adb1e
    ctx->list = NULL;
Packit 3adb1e
    ctx->last = NULL;
Packit 3adb1e
    ctx->state = READ_START;
Packit 3adb1e
Packit 3adb1e
    return serf_bucket_create(&serf_bucket_type_headers, allocator, ctx);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
void serf_bucket_headers_setx(
Packit 3adb1e
    serf_bucket_t *bkt,
Packit 3adb1e
    const char *header, apr_size_t header_size, int header_copy,
Packit 3adb1e
    const char *value, apr_size_t value_size, int value_copy)
Packit 3adb1e
{
Packit 3adb1e
    headers_context_t *ctx = bkt->data;
Packit 3adb1e
    header_list_t *hdr;
Packit 3adb1e
Packit 3adb1e
#if 0
Packit 3adb1e
    /* ### include this? */
Packit 3adb1e
    if (ctx->cur_read) {
Packit 3adb1e
        /* we started reading. can't change now. */
Packit 3adb1e
        abort();
Packit 3adb1e
    }
Packit 3adb1e
#endif
Packit 3adb1e
Packit 3adb1e
    hdr = serf_bucket_mem_alloc(bkt->allocator, sizeof(*hdr));
Packit 3adb1e
    hdr->header_size = header_size;
Packit 3adb1e
    hdr->value_size = value_size;
Packit 3adb1e
    hdr->alloc_flags = 0;
Packit 3adb1e
    hdr->next = NULL;
Packit 3adb1e
Packit 3adb1e
    if (header_copy) {
Packit 3adb1e
        hdr->header = serf_bstrmemdup(bkt->allocator, header, header_size);
Packit 3adb1e
        hdr->alloc_flags |= ALLOC_HEADER;
Packit 3adb1e
    }
Packit 3adb1e
    else {
Packit 3adb1e
        hdr->header = header;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    if (value_copy) {
Packit 3adb1e
        hdr->value = serf_bstrmemdup(bkt->allocator, value, value_size);
Packit 3adb1e
        hdr->alloc_flags |= ALLOC_VALUE;
Packit 3adb1e
    }
Packit 3adb1e
    else {
Packit 3adb1e
        hdr->value = value;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* Add the new header at the end of the list. */
Packit 3adb1e
    if (ctx->last)
Packit 3adb1e
        ctx->last->next = hdr;
Packit 3adb1e
    else
Packit 3adb1e
        ctx->list = hdr;
Packit 3adb1e
Packit 3adb1e
    ctx->last = hdr;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
void serf_bucket_headers_set(
Packit 3adb1e
    serf_bucket_t *headers_bucket,
Packit 3adb1e
    const char *header,
Packit 3adb1e
    const char *value)
Packit 3adb1e
{
Packit 3adb1e
    serf_bucket_headers_setx(headers_bucket,
Packit 3adb1e
                             header, strlen(header), 0,
Packit 3adb1e
                             value, strlen(value), 1);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
void serf_bucket_headers_setc(
Packit 3adb1e
    serf_bucket_t *headers_bucket,
Packit 3adb1e
    const char *header,
Packit 3adb1e
    const char *value)
Packit 3adb1e
{
Packit 3adb1e
    serf_bucket_headers_setx(headers_bucket,
Packit 3adb1e
                             header, strlen(header), 1,
Packit 3adb1e
                             value, strlen(value), 1);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
void serf_bucket_headers_setn(
Packit 3adb1e
    serf_bucket_t *headers_bucket,
Packit 3adb1e
    const char *header,
Packit 3adb1e
    const char *value)
Packit 3adb1e
{
Packit 3adb1e
    serf_bucket_headers_setx(headers_bucket,
Packit 3adb1e
                             header, strlen(header), 0,
Packit 3adb1e
                             value, strlen(value), 0);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
const char *serf_bucket_headers_get(
Packit 3adb1e
    serf_bucket_t *headers_bucket,
Packit 3adb1e
    const char *header)
Packit 3adb1e
{
Packit 3adb1e
    headers_context_t *ctx = headers_bucket->data;
Packit 3adb1e
    header_list_t *found = ctx->list;
Packit 3adb1e
    const char *val = NULL;
Packit 3adb1e
    int value_size = 0;
Packit 3adb1e
    int val_alloc = 0;
Packit 3adb1e
Packit 3adb1e
    while (found) {
Packit 3adb1e
        if (strcasecmp(found->header, header) == 0) {
Packit 3adb1e
            if (val) {
Packit 3adb1e
                /* The header is already present.  RFC 2616, section 4.2
Packit 3adb1e
                   indicates that we should append the new value, separated by
Packit 3adb1e
                   a comma.  Reasoning: for headers whose values are known to
Packit 3adb1e
                   be comma-separated, that is clearly the correct behavior;
Packit 3adb1e
                   for others, the correct behavior is undefined anyway. */
Packit 3adb1e
Packit 3adb1e
                /* The "+1" is for the comma; the +1 in the alloc
Packit 3adb1e
                   call is for the terminating '\0' */
Packit 3adb1e
                apr_size_t new_size = found->value_size + value_size + 1;
Packit 3adb1e
                char *new_val = serf_bucket_mem_alloc(headers_bucket->allocator,
Packit 3adb1e
                                                      new_size + 1);
Packit 3adb1e
                memcpy(new_val, val, value_size);
Packit 3adb1e
                new_val[value_size] = ',';
Packit 3adb1e
                memcpy(new_val + value_size + 1, found->value,
Packit 3adb1e
                       found->value_size);
Packit 3adb1e
                new_val[new_size] = '\0';
Packit 3adb1e
                /* Copy the new value over the already existing value. */
Packit 3adb1e
                if (val_alloc)
Packit 3adb1e
                    serf_bucket_mem_free(headers_bucket->allocator, (void*)val);
Packit 3adb1e
                val_alloc |= ALLOC_VALUE;
Packit 3adb1e
                val = new_val;
Packit 3adb1e
                value_size = new_size;
Packit 3adb1e
            }
Packit 3adb1e
            else {
Packit 3adb1e
                val = found->value;
Packit 3adb1e
                value_size = found->value_size;
Packit 3adb1e
            }
Packit 3adb1e
        }
Packit 3adb1e
        found = found->next;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    return val;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
void serf__bucket_headers_remove(serf_bucket_t *bucket, const char *header)
Packit 3adb1e
{
Packit 3adb1e
    headers_context_t *ctx = bucket->data;
Packit 3adb1e
    header_list_t *scan = ctx->list, *prev = NULL;
Packit 3adb1e
Packit 3adb1e
    /* Find and delete all items with the same header (case insensitive) */
Packit 3adb1e
    while (scan) {
Packit 3adb1e
        if (strcasecmp(scan->header, header) == 0) {
Packit 3adb1e
            if (prev) {
Packit 3adb1e
                prev->next = scan->next;
Packit 3adb1e
            } else {
Packit 3adb1e
                ctx->list = scan->next;
Packit 3adb1e
            }
Packit 3adb1e
            if (ctx->last == scan) {
Packit 3adb1e
                ctx->last = NULL;
Packit 3adb1e
            }
Packit 3adb1e
        } else {
Packit 3adb1e
            prev = scan;
Packit 3adb1e
        }
Packit 3adb1e
        scan = scan->next;
Packit 3adb1e
    }
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
void serf_bucket_headers_do(
Packit 3adb1e
    serf_bucket_t *headers_bucket,
Packit 3adb1e
    serf_bucket_headers_do_callback_fn_t func,
Packit 3adb1e
    void *baton)
Packit 3adb1e
{
Packit 3adb1e
    headers_context_t *ctx = headers_bucket->data;
Packit 3adb1e
    header_list_t *scan = ctx->list;
Packit 3adb1e
Packit 3adb1e
    while (scan) {
Packit 3adb1e
        if (func(baton, scan->header, scan->value) != 0) {
Packit 3adb1e
            break;
Packit 3adb1e
        }
Packit 3adb1e
        scan = scan->next;
Packit 3adb1e
    }
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static void serf_headers_destroy_and_data(serf_bucket_t *bucket)
Packit 3adb1e
{
Packit 3adb1e
    headers_context_t *ctx = bucket->data;
Packit 3adb1e
    header_list_t *scan = ctx->list;
Packit 3adb1e
Packit 3adb1e
    while (scan) {
Packit 3adb1e
        header_list_t *next_hdr = scan->next;
Packit 3adb1e
Packit 3adb1e
        if (scan->alloc_flags & ALLOC_HEADER)
Packit 3adb1e
            serf_bucket_mem_free(bucket->allocator, (void *)scan->header);
Packit 3adb1e
        if (scan->alloc_flags & ALLOC_VALUE)
Packit 3adb1e
            serf_bucket_mem_free(bucket->allocator, (void *)scan->value);
Packit 3adb1e
        serf_bucket_mem_free(bucket->allocator, scan);
Packit 3adb1e
Packit 3adb1e
        scan = next_hdr;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    serf_default_destroy_and_data(bucket);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static void select_value(
Packit 3adb1e
    headers_context_t *ctx,
Packit 3adb1e
    const char **value,
Packit 3adb1e
    apr_size_t *len)
Packit 3adb1e
{
Packit 3adb1e
    const char *v;
Packit 3adb1e
    apr_size_t l;
Packit 3adb1e
Packit 3adb1e
    if (ctx->state == READ_START) {
Packit 3adb1e
        if (ctx->list == NULL) {
Packit 3adb1e
            /* No headers. Move straight to the TERM state. */
Packit 3adb1e
            ctx->state = READ_TERM;
Packit 3adb1e
        }
Packit 3adb1e
        else {
Packit 3adb1e
            ctx->state = READ_HEADER;
Packit 3adb1e
            ctx->cur_read = ctx->list;
Packit 3adb1e
        }
Packit 3adb1e
        ctx->amt_read = 0;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    switch (ctx->state) {
Packit 3adb1e
    case READ_HEADER:
Packit 3adb1e
        v = ctx->cur_read->header;
Packit 3adb1e
        l = ctx->cur_read->header_size;
Packit 3adb1e
        break;
Packit 3adb1e
    case READ_SEP:
Packit 3adb1e
        v = ": ";
Packit 3adb1e
        l = 2;
Packit 3adb1e
        break;
Packit 3adb1e
    case READ_VALUE:
Packit 3adb1e
        v = ctx->cur_read->value;
Packit 3adb1e
        l = ctx->cur_read->value_size;
Packit 3adb1e
        break;
Packit 3adb1e
    case READ_CRLF:
Packit 3adb1e
    case READ_TERM:
Packit 3adb1e
        v = "\r\n";
Packit 3adb1e
        l = 2;
Packit 3adb1e
        break;
Packit 3adb1e
    case READ_DONE:
Packit 3adb1e
        *len = 0;
Packit 3adb1e
        return;
Packit 3adb1e
    default:
Packit 3adb1e
        /* Not reachable */
Packit 3adb1e
        return;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    *value = v + ctx->amt_read;
Packit 3adb1e
    *len = l - ctx->amt_read;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
/* the current data chunk has been read/consumed. move our internal state. */
Packit 3adb1e
static apr_status_t consume_chunk(headers_context_t *ctx)
Packit 3adb1e
{
Packit 3adb1e
    /* move to the next state, resetting the amount read. */
Packit 3adb1e
    ++ctx->state;
Packit 3adb1e
    ctx->amt_read = 0;
Packit 3adb1e
Packit 3adb1e
    /* just sent the terminator and moved to DONE. signal completion. */
Packit 3adb1e
    if (ctx->state == READ_DONE)
Packit 3adb1e
        return APR_EOF;
Packit 3adb1e
Packit 3adb1e
    /* end of this header. move to the next one. */
Packit 3adb1e
    if (ctx->state == READ_TERM) {
Packit 3adb1e
        ctx->cur_read = ctx->cur_read->next;
Packit 3adb1e
        if (ctx->cur_read != NULL) {
Packit 3adb1e
            /* We've got another head to send. Reset the read state. */
Packit 3adb1e
            ctx->state = READ_HEADER;
Packit 3adb1e
        }
Packit 3adb1e
        /* else leave in READ_TERM */
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* there is more data which can be read immediately. */
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t serf_headers_peek(serf_bucket_t *bucket,
Packit 3adb1e
                                      const char **data,
Packit 3adb1e
                                      apr_size_t *len)
Packit 3adb1e
{
Packit 3adb1e
    headers_context_t *ctx = bucket->data;
Packit 3adb1e
Packit 3adb1e
    select_value(ctx, data, len);
Packit 3adb1e
Packit 3adb1e
    /* already done or returning the CRLF terminator? return EOF */
Packit 3adb1e
    if (ctx->state == READ_DONE || ctx->state == READ_TERM)
Packit 3adb1e
        return APR_EOF;
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t serf_headers_read(serf_bucket_t *bucket,
Packit 3adb1e
                                      apr_size_t requested,
Packit 3adb1e
                                      const char **data, apr_size_t *len)
Packit 3adb1e
{
Packit 3adb1e
    headers_context_t *ctx = bucket->data;
Packit 3adb1e
    apr_size_t avail;
Packit 3adb1e
Packit 3adb1e
    select_value(ctx, data, &avail);
Packit 3adb1e
    if (ctx->state == READ_DONE) {
Packit 3adb1e
        *len = avail;
Packit 3adb1e
        return APR_EOF;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    if (requested >= avail) {
Packit 3adb1e
        /* return everything from this chunk */
Packit 3adb1e
        *len = avail;
Packit 3adb1e
Packit 3adb1e
        /* we consumed this chunk. advance the state. */
Packit 3adb1e
        return consume_chunk(ctx);
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* return just the amount requested, and advance our pointer */
Packit 3adb1e
    *len = requested;
Packit 3adb1e
    ctx->amt_read += requested;
Packit 3adb1e
Packit 3adb1e
    /* there is more that can be read immediately */
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t serf_headers_readline(serf_bucket_t *bucket,
Packit 3adb1e
                                          int acceptable, int *found,
Packit 3adb1e
                                          const char **data, apr_size_t *len)
Packit 3adb1e
{
Packit 3adb1e
    headers_context_t *ctx = bucket->data;
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
Packit 3adb1e
    /* ### what behavior should we use here? APR_EGENERAL for now */
Packit 3adb1e
    if ((acceptable & SERF_NEWLINE_CRLF) == 0)
Packit 3adb1e
        return APR_EGENERAL;
Packit 3adb1e
Packit 3adb1e
    /* get whatever is in this chunk */
Packit 3adb1e
    select_value(ctx, data, len);
Packit 3adb1e
    if (ctx->state == READ_DONE)
Packit 3adb1e
        return APR_EOF;
Packit 3adb1e
Packit 3adb1e
    /* we consumed this chunk. advance the state. */
Packit 3adb1e
    status = consume_chunk(ctx);
Packit 3adb1e
Packit 3adb1e
    /* the type of newline found is easy... */
Packit 3adb1e
    *found = (ctx->state == READ_CRLF || ctx->state == READ_TERM)
Packit 3adb1e
        ? SERF_NEWLINE_CRLF : SERF_NEWLINE_NONE;
Packit 3adb1e
Packit 3adb1e
    return status;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t serf_headers_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
    apr_size_t avail = requested;
Packit 3adb1e
    int i;
Packit 3adb1e
Packit 3adb1e
    *vecs_used = 0;
Packit 3adb1e
Packit 3adb1e
    for (i = 0; i < vecs_size; i++) {
Packit 3adb1e
        const char *data;
Packit 3adb1e
        apr_size_t len;
Packit 3adb1e
        apr_status_t status;
Packit 3adb1e
Packit 3adb1e
        /* Calling read() would not be a safe opt in the general case, but it
Packit 3adb1e
         * is here for the header bucket as it only frees all of the header
Packit 3adb1e
         * keys and values when the entire bucket goes away - not on a
Packit 3adb1e
         * per-read() basis as is normally the case.
Packit 3adb1e
         */
Packit 3adb1e
        status = serf_headers_read(bucket, avail, &data, &len;;
Packit 3adb1e
Packit 3adb1e
        if (len) {
Packit 3adb1e
            vecs[*vecs_used].iov_base = (char*)data;
Packit 3adb1e
            vecs[*vecs_used].iov_len = len;
Packit 3adb1e
Packit 3adb1e
            (*vecs_used)++;
Packit 3adb1e
Packit 3adb1e
            if (avail != SERF_READ_ALL_AVAIL) {
Packit 3adb1e
                avail -= len;
Packit 3adb1e
Packit 3adb1e
                /* If we reach 0, then read()'s status will suffice.  */
Packit 3adb1e
                if (avail == 0) {
Packit 3adb1e
                    return status;
Packit 3adb1e
                }
Packit 3adb1e
            }
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        if (status) {
Packit 3adb1e
            return status;
Packit 3adb1e
        }
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
const serf_bucket_type_t serf_bucket_type_headers = {
Packit 3adb1e
    "HEADERS",
Packit 3adb1e
    serf_headers_read,
Packit 3adb1e
    serf_headers_readline,
Packit 3adb1e
    serf_headers_read_iovec,
Packit 3adb1e
    serf_default_read_for_sendfile,
Packit 3adb1e
    serf_default_read_bucket,
Packit 3adb1e
    serf_headers_peek,
Packit 3adb1e
    serf_headers_destroy_and_data,
Packit 3adb1e
};