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