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
serf_bucket_headers_create(serf_bucket_alloc_t * allocator)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
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)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
serf_bucket_headers_set(serf_bucket_t * headers_bucket,const char * header,const char * value)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
serf_bucket_headers_setc(serf_bucket_t * headers_bucket,const char * header,const char * value)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
serf_bucket_headers_setn(serf_bucket_t * headers_bucket,const char * header,const char * value)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
serf_bucket_headers_get(serf_bucket_t * headers_bucket,const char * header)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
serf__bucket_headers_remove(serf_bucket_t * bucket,const char * header)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
serf_bucket_headers_do(serf_bucket_t * headers_bucket,serf_bucket_headers_do_callback_fn_t func,void * baton)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
serf_headers_destroy_and_data(serf_bucket_t * bucket)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
select_value(headers_context_t * ctx,const char ** value,apr_size_t * len)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. */
consume_chunk(headers_context_t * ctx)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
serf_headers_peek(serf_bucket_t * bucket,const char ** data,apr_size_t * len)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
serf_headers_read(serf_bucket_t * bucket,apr_size_t requested,const char ** data,apr_size_t * len)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
serf_headers_readline(serf_bucket_t * bucket,int acceptable,int * found,const char ** data,apr_size_t * len)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
serf_headers_read_iovec(serf_bucket_t * bucket,apr_size_t requested,int vecs_size,struct iovec * vecs,int * vecs_used)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 };
457