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