headers_buckets.c revision 262339
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 26typedef 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 40typedef 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 59serf_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 72void 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 119void 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 129void 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 139void 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 149const 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 196void 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 219void 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 235static 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 255static 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. */ 306static 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 330static 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 345static 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 374static 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 400static 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 447const 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