]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/serf/buckets/headers_buckets.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / serf / buckets / headers_buckets.c
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
24 typedef 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
38 typedef 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
56 serf_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
68 void 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
117 void 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
127 void 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
137 void 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
147 const 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
194 void 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
210 static 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
230 static 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. */
281 static 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
305 static 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
320 static 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         *len = avail;
330         return APR_EOF;
331     }
332
333     if (requested >= avail) {
334         /* return everything from this chunk */
335         *len = avail;
336
337         /* we consumed this chunk. advance the state. */
338         return consume_chunk(ctx);
339     }
340
341     /* return just the amount requested, and advance our pointer */
342     *len = requested;
343     ctx->amt_read += requested;
344
345     /* there is more that can be read immediately */
346     return APR_SUCCESS;
347 }
348
349 static apr_status_t serf_headers_readline(serf_bucket_t *bucket,
350                                           int acceptable, int *found,
351                                           const char **data, apr_size_t *len)
352 {
353     headers_context_t *ctx = bucket->data;
354     apr_status_t status;
355
356     /* ### what behavior should we use here? APR_EGENERAL for now */
357     if ((acceptable & SERF_NEWLINE_CRLF) == 0)
358         return APR_EGENERAL;
359
360     /* get whatever is in this chunk */
361     select_value(ctx, data, len);
362     if (ctx->state == READ_DONE)
363         return APR_EOF;
364
365     /* we consumed this chunk. advance the state. */
366     status = consume_chunk(ctx);
367
368     /* the type of newline found is easy... */
369     *found = (ctx->state == READ_CRLF || ctx->state == READ_TERM)
370         ? SERF_NEWLINE_CRLF : SERF_NEWLINE_NONE;
371
372     return status;
373 }
374
375 static apr_status_t serf_headers_read_iovec(serf_bucket_t *bucket,
376                                             apr_size_t requested,
377                                             int vecs_size,
378                                             struct iovec *vecs,
379                                             int *vecs_used)
380 {
381     apr_size_t avail = requested;
382     int i;
383
384     *vecs_used = 0;
385
386     for (i = 0; i < vecs_size; i++) {
387         const char *data;
388         apr_size_t len;
389         apr_status_t status;
390
391         /* Calling read() would not be a safe opt in the general case, but it
392          * is here for the header bucket as it only frees all of the header
393          * keys and values when the entire bucket goes away - not on a
394          * per-read() basis as is normally the case.
395          */
396         status = serf_headers_read(bucket, avail, &data, &len);
397
398         if (len) {
399             vecs[*vecs_used].iov_base = (char*)data;
400             vecs[*vecs_used].iov_len = len;
401
402             (*vecs_used)++;
403
404             if (avail != SERF_READ_ALL_AVAIL) {
405                 avail -= len;
406
407                 /* If we reach 0, then read()'s status will suffice.  */
408                 if (avail == 0) {
409                     return status;
410                 }
411             }
412         }
413
414         if (status) {
415             return status;
416         }
417     }
418
419     return APR_SUCCESS;
420 }
421
422 const serf_bucket_type_t serf_bucket_type_headers = {
423     "HEADERS",
424     serf_headers_read,
425     serf_headers_readline,
426     serf_headers_read_iovec,
427     serf_default_read_for_sendfile,
428     serf_default_read_bucket,
429     serf_headers_peek,
430     serf_headers_destroy_and_data,
431 };