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 31typedef 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 45typedef 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 64serf_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 77void 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 124void 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 134void 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 144void 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 154const 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 201void 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 224void 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 240static 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 260static 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. */ 311static 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 335static 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 350static 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 379static 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 405static 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 452const 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