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