headers_buckets.c revision 251886
1/* Copyright 2004 Justin Erenkrantz and Greg Stein
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include <stdlib.h>
17
18#include <apr_general.h>  /* for strcasecmp() */
19
20#include "serf.h"
21#include "serf_bucket_util.h"
22
23
24typedef struct header_list {
25    const char *header;
26    const char *value;
27
28    apr_size_t header_size;
29    apr_size_t value_size;
30
31    int alloc_flags;
32#define ALLOC_HEADER 0x0001  /* header lives in our allocator */
33#define ALLOC_VALUE  0x0002  /* value lives in our allocator */
34
35    struct header_list *next;
36} header_list_t;
37
38typedef struct {
39    header_list_t *list;
40
41    header_list_t *cur_read;
42    enum {
43        READ_START,     /* haven't started reading yet */
44        READ_HEADER,    /* reading cur_read->header */
45        READ_SEP,       /* reading ": " */
46        READ_VALUE,     /* reading cur_read->value */
47        READ_CRLF,      /* reading "\r\n" */
48        READ_TERM,      /* reading the final "\r\n" */
49        READ_DONE       /* no more data to read */
50    } state;
51    apr_size_t amt_read; /* how much of the current state we've read */
52
53} headers_context_t;
54
55
56serf_bucket_t *serf_bucket_headers_create(
57    serf_bucket_alloc_t *allocator)
58{
59    headers_context_t *ctx;
60
61    ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx));
62    ctx->list = NULL;
63    ctx->state = READ_START;
64
65    return serf_bucket_create(&serf_bucket_type_headers, allocator, ctx);
66}
67
68void serf_bucket_headers_setx(
69    serf_bucket_t *bkt,
70    const char *header, apr_size_t header_size, int header_copy,
71    const char *value, apr_size_t value_size, int value_copy)
72{
73    headers_context_t *ctx = bkt->data;
74    header_list_t *iter = ctx->list;
75    header_list_t *hdr;
76
77#if 0
78    /* ### include this? */
79    if (ctx->cur_read) {
80        /* we started reading. can't change now. */
81        abort();
82    }
83#endif
84
85    hdr = serf_bucket_mem_alloc(bkt->allocator, sizeof(*hdr));
86    hdr->header_size = header_size;
87    hdr->value_size = value_size;
88    hdr->alloc_flags = 0;
89    hdr->next = NULL;
90
91    if (header_copy) {
92        hdr->header = serf_bstrmemdup(bkt->allocator, header, header_size);
93        hdr->alloc_flags |= ALLOC_HEADER;
94    }
95    else {
96        hdr->header = header;
97    }
98
99    if (value_copy) {
100        hdr->value = serf_bstrmemdup(bkt->allocator, value, value_size);
101        hdr->alloc_flags |= ALLOC_VALUE;
102    }
103    else {
104        hdr->value = value;
105    }
106
107    /* Add the new header at the end of the list. */
108    while (iter && iter->next) {
109        iter = iter->next;
110    }
111    if (iter)
112        iter->next = hdr;
113    else
114        ctx->list = hdr;
115}
116
117void serf_bucket_headers_set(
118    serf_bucket_t *headers_bucket,
119    const char *header,
120    const char *value)
121{
122    serf_bucket_headers_setx(headers_bucket,
123                             header, strlen(header), 0,
124                             value, strlen(value), 1);
125}
126
127void serf_bucket_headers_setc(
128    serf_bucket_t *headers_bucket,
129    const char *header,
130    const char *value)
131{
132    serf_bucket_headers_setx(headers_bucket,
133                             header, strlen(header), 1,
134                             value, strlen(value), 1);
135}
136
137void serf_bucket_headers_setn(
138    serf_bucket_t *headers_bucket,
139    const char *header,
140    const char *value)
141{
142    serf_bucket_headers_setx(headers_bucket,
143                             header, strlen(header), 0,
144                             value, strlen(value), 0);
145}
146
147const char *serf_bucket_headers_get(
148    serf_bucket_t *headers_bucket,
149    const char *header)
150{
151    headers_context_t *ctx = headers_bucket->data;
152    header_list_t *found = ctx->list;
153    const char *val = NULL;
154    int value_size = 0;
155    int val_alloc = 0;
156
157    while (found) {
158        if (strcasecmp(found->header, header) == 0) {
159            if (val) {
160                /* The header is already present.  RFC 2616, section 4.2
161                   indicates that we should append the new value, separated by
162                   a comma.  Reasoning: for headers whose values are known to
163                   be comma-separated, that is clearly the correct behavior;
164                   for others, the correct behavior is undefined anyway. */
165
166                /* The "+1" is for the comma; the +1 in the alloc
167                   call is for the terminating '\0' */
168                apr_size_t new_size = found->value_size + value_size + 1;
169                char *new_val = serf_bucket_mem_alloc(headers_bucket->allocator,
170                                                      new_size + 1);
171                memcpy(new_val, val, value_size);
172                new_val[value_size] = ',';
173                memcpy(new_val + value_size + 1, found->value,
174                       found->value_size);
175                new_val[new_size] = '\0';
176                /* Copy the new value over the already existing value. */
177                if (val_alloc)
178                    serf_bucket_mem_free(headers_bucket->allocator, (void*)val);
179                val_alloc |= ALLOC_VALUE;
180                val = new_val;
181                value_size = new_size;
182            }
183            else {
184                val = found->value;
185                value_size = found->value_size;
186            }
187        }
188        found = found->next;
189    }
190
191    return val;
192}
193
194void serf_bucket_headers_do(
195    serf_bucket_t *headers_bucket,
196    serf_bucket_headers_do_callback_fn_t func,
197    void *baton)
198{
199    headers_context_t *ctx = headers_bucket->data;
200    header_list_t *scan = ctx->list;
201
202    while (scan) {
203        if (func(baton, scan->header, scan->value) != 0) {
204            break;
205        }
206        scan = scan->next;
207    }
208}
209
210static void serf_headers_destroy_and_data(serf_bucket_t *bucket)
211{
212    headers_context_t *ctx = bucket->data;
213    header_list_t *scan = ctx->list;
214
215    while (scan) {
216        header_list_t *next_hdr = scan->next;
217
218        if (scan->alloc_flags & ALLOC_HEADER)
219            serf_bucket_mem_free(bucket->allocator, (void *)scan->header);
220        if (scan->alloc_flags & ALLOC_VALUE)
221            serf_bucket_mem_free(bucket->allocator, (void *)scan->value);
222        serf_bucket_mem_free(bucket->allocator, scan);
223
224        scan = next_hdr;
225    }
226
227    serf_default_destroy_and_data(bucket);
228}
229
230static void select_value(
231    headers_context_t *ctx,
232    const char **value,
233    apr_size_t *len)
234{
235    const char *v;
236    apr_size_t l;
237
238    if (ctx->state == READ_START) {
239        if (ctx->list == NULL) {
240            /* No headers. Move straight to the TERM state. */
241            ctx->state = READ_TERM;
242        }
243        else {
244            ctx->state = READ_HEADER;
245            ctx->cur_read = ctx->list;
246        }
247        ctx->amt_read = 0;
248    }
249
250    switch (ctx->state) {
251    case READ_HEADER:
252        v = ctx->cur_read->header;
253        l = ctx->cur_read->header_size;
254        break;
255    case READ_SEP:
256        v = ": ";
257        l = 2;
258        break;
259    case READ_VALUE:
260        v = ctx->cur_read->value;
261        l = ctx->cur_read->value_size;
262        break;
263    case READ_CRLF:
264    case READ_TERM:
265        v = "\r\n";
266        l = 2;
267        break;
268    case READ_DONE:
269        *len = 0;
270        return;
271    default:
272        /* Not reachable */
273        return;
274    }
275
276    *value = v + ctx->amt_read;
277    *len = l - ctx->amt_read;
278}
279
280/* the current data chunk has been read/consumed. move our internal state. */
281static apr_status_t consume_chunk(headers_context_t *ctx)
282{
283    /* move to the next state, resetting the amount read. */
284    ++ctx->state;
285    ctx->amt_read = 0;
286
287    /* just sent the terminator and moved to DONE. signal completion. */
288    if (ctx->state == READ_DONE)
289        return APR_EOF;
290
291    /* end of this header. move to the next one. */
292    if (ctx->state == READ_TERM) {
293        ctx->cur_read = ctx->cur_read->next;
294        if (ctx->cur_read != NULL) {
295            /* We've got another head to send. Reset the read state. */
296            ctx->state = READ_HEADER;
297        }
298        /* else leave in READ_TERM */
299    }
300
301    /* there is more data which can be read immediately. */
302    return APR_SUCCESS;
303}
304
305static apr_status_t serf_headers_peek(serf_bucket_t *bucket,
306                                      const char **data,
307                                      apr_size_t *len)
308{
309    headers_context_t *ctx = bucket->data;
310
311    select_value(ctx, data, len);
312
313    /* already done or returning the CRLF terminator? return EOF */
314    if (ctx->state == READ_DONE || ctx->state == READ_TERM)
315        return APR_EOF;
316
317    return APR_SUCCESS;
318}
319
320static apr_status_t serf_headers_read(serf_bucket_t *bucket,
321                                      apr_size_t requested,
322                                      const char **data, apr_size_t *len)
323{
324    headers_context_t *ctx = bucket->data;
325    apr_size_t avail;
326
327    select_value(ctx, data, &avail);
328    if (ctx->state == READ_DONE)
329        return APR_EOF;
330
331    if (requested >= avail) {
332        /* return everything from this chunk */
333        *len = avail;
334
335        /* we consumed this chunk. advance the state. */
336        return consume_chunk(ctx);
337    }
338
339    /* return just the amount requested, and advance our pointer */
340    *len = requested;
341    ctx->amt_read += requested;
342
343    /* there is more that can be read immediately */
344    return APR_SUCCESS;
345}
346
347static apr_status_t serf_headers_readline(serf_bucket_t *bucket,
348                                          int acceptable, int *found,
349                                          const char **data, apr_size_t *len)
350{
351    headers_context_t *ctx = bucket->data;
352    apr_status_t status;
353
354    /* ### what behavior should we use here? APR_EGENERAL for now */
355    if ((acceptable & SERF_NEWLINE_CRLF) == 0)
356        return APR_EGENERAL;
357
358    /* get whatever is in this chunk */
359    select_value(ctx, data, len);
360    if (ctx->state == READ_DONE)
361        return APR_EOF;
362
363    /* we consumed this chunk. advance the state. */
364    status = consume_chunk(ctx);
365
366    /* the type of newline found is easy... */
367    *found = (ctx->state == READ_CRLF || ctx->state == READ_TERM)
368        ? SERF_NEWLINE_CRLF : SERF_NEWLINE_NONE;
369
370    return status;
371}
372
373static apr_status_t serf_headers_read_iovec(serf_bucket_t *bucket,
374                                            apr_size_t requested,
375                                            int vecs_size,
376                                            struct iovec *vecs,
377                                            int *vecs_used)
378{
379    apr_size_t avail = requested;
380    int i;
381
382    *vecs_used = 0;
383
384    for (i = 0; i < vecs_size; i++) {
385        const char *data;
386        apr_size_t len;
387        apr_status_t status;
388
389        /* Calling read() would not be a safe opt in the general case, but it
390         * is here for the header bucket as it only frees all of the header
391         * keys and values when the entire bucket goes away - not on a
392         * per-read() basis as is normally the case.
393         */
394        status = serf_headers_read(bucket, avail, &data, &len);
395
396        if (len) {
397            vecs[*vecs_used].iov_base = (char*)data;
398            vecs[*vecs_used].iov_len = len;
399
400            (*vecs_used)++;
401
402            if (avail != SERF_READ_ALL_AVAIL) {
403                avail -= len;
404
405                /* If we reach 0, then read()'s status will suffice.  */
406                if (avail == 0) {
407                    return status;
408                }
409            }
410        }
411
412        if (status) {
413            return status;
414        }
415    }
416
417    return APR_SUCCESS;
418}
419
420const serf_bucket_type_t serf_bucket_type_headers = {
421    "HEADERS",
422    serf_headers_read,
423    serf_headers_readline,
424    serf_headers_read_iovec,
425    serf_default_read_for_sendfile,
426    serf_default_read_bucket,
427    serf_headers_peek,
428    serf_headers_destroy_and_data,
429};
430