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