1/* 2 * httpread - Manage reading file(s) from HTTP/TCP socket 3 * Author: Ted Merrill 4 * Copyright 2008 Atheros Communications 5 * 6 * This software may be distributed under the terms of the BSD license. 7 * See README for more details. 8 * 9 * The files are buffered via internal callbacks from eloop, then presented to 10 * an application callback routine when completely read into memory. May also 11 * be used if no file is expected but just to get the header, including HTTP 12 * replies (e.g. HTTP/1.1 200 OK etc.). 13 * 14 * This does not attempt to be an optimally efficient implementation, but does 15 * attempt to be of reasonably small size and memory consumption; assuming that 16 * only small files are to be read. A maximum file size is provided by 17 * application and enforced. 18 * 19 * It is assumed that the application does not expect any of the following: 20 * -- transfer encoding other than chunked 21 * -- trailer fields 22 * It is assumed that, even if the other side requested that the connection be 23 * kept open, that we will close it (thus HTTP messages sent by application 24 * should have the connection closed field); this is allowed by HTTP/1.1 and 25 * simplifies things for us. 26 * 27 * Other limitations: 28 * -- HTTP header may not exceed a hard-coded size. 29 * 30 * Notes: 31 * This code would be massively simpler without some of the new features of 32 * HTTP/1.1, especially chunked data. 33 */ 34 35#include "includes.h" 36 37#include "common.h" 38#include "eloop.h" 39#include "httpread.h" 40 41 42/* Tunable parameters */ 43#define HTTPREAD_READBUF_SIZE 1024 /* read in chunks of this size */ 44#define HTTPREAD_HEADER_MAX_SIZE 4096 /* max allowed for headers */ 45#define HTTPREAD_BODYBUF_DELTA 4096 /* increase allocation by this */ 46 47#if 0 48/* httpread_debug -- set this global variable > 0 e.g. from debugger 49 * to enable debugs (larger numbers for more debugs) 50 * Make this a #define of 0 to eliminate the debugging code. 51 */ 52int httpread_debug = 99; 53#else 54#define httpread_debug 0 /* eliminates even the debugging code */ 55#endif 56 57 58/* control instance -- actual definition (opaque to application) 59 */ 60struct httpread { 61 /* information from creation */ 62 int sd; /* descriptor of TCP socket to read from */ 63 void (*cb)(struct httpread *handle, void *cookie, 64 enum httpread_event e); /* call on event */ 65 void *cookie; /* pass to callback */ 66 int max_bytes; /* maximum file size else abort it */ 67 int timeout_seconds; /* 0 or total duration timeout period */ 68 69 /* dynamically used information follows */ 70 71 int got_hdr; /* nonzero when header is finalized */ 72 char hdr[HTTPREAD_HEADER_MAX_SIZE+1]; /* headers stored here */ 73 int hdr_nbytes; 74 75 enum httpread_hdr_type hdr_type; 76 int version; /* 1 if we've seen 1.1 */ 77 int reply_code; /* for type REPLY, e.g. 200 for HTTP/1.1 200 OK */ 78 int got_content_length; /* true if we know content length for sure */ 79 int content_length; /* body length, iff got_content_length */ 80 int chunked; /* nonzero for chunked data */ 81 char *uri; 82 83 int got_body; /* nonzero when body is finalized */ 84 char *body; 85 int body_nbytes; 86 int body_alloc_nbytes; /* amount allocated */ 87 88 int got_file; /* here when we are done */ 89 90 /* The following apply if data is chunked: */ 91 int in_chunk_data; /* 0=in/at header, 1=in the data or tail*/ 92 int chunk_start; /* offset in body of chunk hdr or data */ 93 int chunk_size; /* data of chunk (not hdr or ending CRLF)*/ 94 int in_trailer; /* in header fields after data (chunked only)*/ 95 enum trailer_state { 96 trailer_line_begin = 0, 97 trailer_empty_cr, /* empty line + CR */ 98 trailer_nonempty, 99 trailer_nonempty_cr, 100 } trailer_state; 101}; 102 103 104/* Check words for equality, where words consist of graphical characters 105 * delimited by whitespace 106 * Returns nonzero if "equal" doing case insensitive comparison. 107 */ 108static int word_eq(char *s1, char *s2) 109{ 110 int c1; 111 int c2; 112 int end1 = 0; 113 int end2 = 0; 114 for (;;) { 115 c1 = *s1++; 116 c2 = *s2++; 117 if (isalpha(c1) && isupper(c1)) 118 c1 = tolower(c1); 119 if (isalpha(c2) && isupper(c2)) 120 c2 = tolower(c2); 121 end1 = !isgraph(c1); 122 end2 = !isgraph(c2); 123 if (end1 || end2 || c1 != c2) 124 break; 125 } 126 return end1 && end2; /* reached end of both words? */ 127} 128 129 130static void httpread_timeout_handler(void *eloop_data, void *user_ctx); 131 132/* httpread_destroy -- if h is non-NULL, clean up 133 * This must eventually be called by the application following 134 * call of the application's callback and may be called 135 * earlier if desired. 136 */ 137void httpread_destroy(struct httpread *h) 138{ 139 if (httpread_debug >= 10) 140 wpa_printf(MSG_DEBUG, "ENTER httpread_destroy(%p)", h); 141 if (!h) 142 return; 143 144 eloop_cancel_timeout(httpread_timeout_handler, NULL, h); 145 eloop_unregister_sock(h->sd, EVENT_TYPE_READ); 146 os_free(h->body); 147 os_free(h->uri); 148 os_memset(h, 0, sizeof(*h)); /* aid debugging */ 149 h->sd = -1; /* aid debugging */ 150 os_free(h); 151} 152 153 154/* httpread_timeout_handler -- called on excessive total duration 155 */ 156static void httpread_timeout_handler(void *eloop_data, void *user_ctx) 157{ 158 struct httpread *h = user_ctx; 159 wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h); 160 (*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT); 161} 162 163 164/* Analyze options only so far as is needed to correctly obtain the file. 165 * The application can look at the raw header to find other options. 166 */ 167static int httpread_hdr_option_analyze( 168 struct httpread *h, 169 char *hbp /* pointer to current line in header buffer */ 170 ) 171{ 172 if (word_eq(hbp, "CONTENT-LENGTH:")) { 173 while (isgraph(*hbp)) 174 hbp++; 175 while (*hbp == ' ' || *hbp == '\t') 176 hbp++; 177 if (!isdigit(*hbp)) 178 return -1; 179 h->content_length = atol(hbp); 180 h->got_content_length = 1; 181 return 0; 182 } 183 if (word_eq(hbp, "TRANSFER_ENCODING:") || 184 word_eq(hbp, "TRANSFER-ENCODING:")) { 185 while (isgraph(*hbp)) 186 hbp++; 187 while (*hbp == ' ' || *hbp == '\t') 188 hbp++; 189 /* There should (?) be no encodings of interest 190 * other than chunked... 191 */ 192 if (word_eq(hbp, "CHUNKED")) { 193 h->chunked = 1; 194 h->in_chunk_data = 0; 195 /* ignore possible ;<parameters> */ 196 } 197 return 0; 198 } 199 /* skip anything we don't know, which is a lot */ 200 return 0; 201} 202 203 204static int httpread_hdr_analyze(struct httpread *h) 205{ 206 char *hbp = h->hdr; /* pointer into h->hdr */ 207 int standard_first_line = 1; 208 209 /* First line is special */ 210 h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN; 211 if (!isgraph(*hbp)) 212 goto bad; 213 if (os_strncmp(hbp, "HTTP/", 5) == 0) { 214 h->hdr_type = HTTPREAD_HDR_TYPE_REPLY; 215 standard_first_line = 0; 216 hbp += 5; 217 if (hbp[0] == '1' && hbp[1] == '.' && 218 isdigit(hbp[2]) && hbp[2] != '0') 219 h->version = 1; 220 while (isgraph(*hbp)) 221 hbp++; 222 while (*hbp == ' ' || *hbp == '\t') 223 hbp++; 224 if (!isdigit(*hbp)) 225 goto bad; 226 h->reply_code = atol(hbp); 227 } else if (word_eq(hbp, "GET")) 228 h->hdr_type = HTTPREAD_HDR_TYPE_GET; 229 else if (word_eq(hbp, "HEAD")) 230 h->hdr_type = HTTPREAD_HDR_TYPE_HEAD; 231 else if (word_eq(hbp, "POST")) 232 h->hdr_type = HTTPREAD_HDR_TYPE_POST; 233 else if (word_eq(hbp, "PUT")) 234 h->hdr_type = HTTPREAD_HDR_TYPE_PUT; 235 else if (word_eq(hbp, "DELETE")) 236 h->hdr_type = HTTPREAD_HDR_TYPE_DELETE; 237 else if (word_eq(hbp, "TRACE")) 238 h->hdr_type = HTTPREAD_HDR_TYPE_TRACE; 239 else if (word_eq(hbp, "CONNECT")) 240 h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT; 241 else if (word_eq(hbp, "NOTIFY")) 242 h->hdr_type = HTTPREAD_HDR_TYPE_NOTIFY; 243 else if (word_eq(hbp, "M-SEARCH")) 244 h->hdr_type = HTTPREAD_HDR_TYPE_M_SEARCH; 245 else if (word_eq(hbp, "M-POST")) 246 h->hdr_type = HTTPREAD_HDR_TYPE_M_POST; 247 else if (word_eq(hbp, "SUBSCRIBE")) 248 h->hdr_type = HTTPREAD_HDR_TYPE_SUBSCRIBE; 249 else if (word_eq(hbp, "UNSUBSCRIBE")) 250 h->hdr_type = HTTPREAD_HDR_TYPE_UNSUBSCRIBE; 251 else { 252 } 253 254 if (standard_first_line) { 255 char *rawuri; 256 char *uri; 257 /* skip type */ 258 while (isgraph(*hbp)) 259 hbp++; 260 while (*hbp == ' ' || *hbp == '\t') 261 hbp++; 262 /* parse uri. 263 * Find length, allocate memory for translated 264 * copy, then translate by changing %<hex><hex> 265 * into represented value. 266 */ 267 rawuri = hbp; 268 while (isgraph(*hbp)) 269 hbp++; 270 h->uri = os_malloc((hbp - rawuri) + 1); 271 if (h->uri == NULL) 272 goto bad; 273 uri = h->uri; 274 while (rawuri < hbp) { 275 int c = *rawuri; 276 if (c == '%' && 277 isxdigit(rawuri[1]) && isxdigit(rawuri[2])) { 278 *uri++ = hex2byte(rawuri + 1); 279 rawuri += 3; 280 } else { 281 *uri++ = c; 282 rawuri++; 283 } 284 } 285 *uri = 0; /* null terminate */ 286 while (isgraph(*hbp)) 287 hbp++; 288 while (*hbp == ' ' || *hbp == '\t') 289 hbp++; 290 /* get version */ 291 if (0 == strncmp(hbp, "HTTP/", 5)) { 292 hbp += 5; 293 if (hbp[0] == '1' && hbp[1] == '.' && 294 isdigit(hbp[2]) && hbp[2] != '0') 295 h->version = 1; 296 } 297 } 298 /* skip rest of line */ 299 while (*hbp) 300 if (*hbp++ == '\n') 301 break; 302 303 /* Remainder of lines are options, in any order; 304 * or empty line to terminate 305 */ 306 for (;;) { 307 /* Empty line to terminate */ 308 if (hbp[0] == '\n' || 309 (hbp[0] == '\r' && hbp[1] == '\n')) 310 break; 311 if (!isgraph(*hbp)) 312 goto bad; 313 if (httpread_hdr_option_analyze(h, hbp)) 314 goto bad; 315 /* skip line */ 316 while (*hbp) 317 if (*hbp++ == '\n') 318 break; 319 } 320 321 /* chunked overrides content-length always */ 322 if (h->chunked) 323 h->got_content_length = 0; 324 325 /* For some types, we should not try to read a body 326 * This is in addition to the application determining 327 * that we should not read a body. 328 */ 329 switch (h->hdr_type) { 330 case HTTPREAD_HDR_TYPE_REPLY: 331 /* Some codes can have a body and some not. 332 * For now, just assume that any other than 200 333 * do not... 334 */ 335 if (h->reply_code != 200) 336 h->max_bytes = 0; 337 break; 338 case HTTPREAD_HDR_TYPE_GET: 339 case HTTPREAD_HDR_TYPE_HEAD: 340 /* in practice it appears that it is assumed 341 * that GETs have a body length of 0... ? 342 */ 343 if (h->chunked == 0 && h->got_content_length == 0) 344 h->max_bytes = 0; 345 break; 346 case HTTPREAD_HDR_TYPE_POST: 347 case HTTPREAD_HDR_TYPE_PUT: 348 case HTTPREAD_HDR_TYPE_DELETE: 349 case HTTPREAD_HDR_TYPE_TRACE: 350 case HTTPREAD_HDR_TYPE_CONNECT: 351 case HTTPREAD_HDR_TYPE_NOTIFY: 352 case HTTPREAD_HDR_TYPE_M_SEARCH: 353 case HTTPREAD_HDR_TYPE_M_POST: 354 case HTTPREAD_HDR_TYPE_SUBSCRIBE: 355 case HTTPREAD_HDR_TYPE_UNSUBSCRIBE: 356 default: 357 break; 358 } 359 360 return 0; 361 362bad: 363 /* Error */ 364 return -1; 365} 366 367 368/* httpread_read_handler -- called when socket ready to read 369 * 370 * Note: any extra data we read past end of transmitted file is ignored; 371 * if we were to support keeping connections open for multiple files then 372 * this would have to be addressed. 373 */ 374static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) 375{ 376 struct httpread *h = sock_ctx; 377 int nread; 378 char *rbp; /* pointer into read buffer */ 379 char *hbp; /* pointer into header buffer */ 380 char *bbp; /* pointer into body buffer */ 381 char readbuf[HTTPREAD_READBUF_SIZE]; /* temp use to read into */ 382 383 if (httpread_debug >= 20) 384 wpa_printf(MSG_DEBUG, "ENTER httpread_read_handler(%p)", h); 385 386 /* read some at a time, then search for the interal 387 * boundaries between header and data and etc. 388 */ 389 nread = read(h->sd, readbuf, sizeof(readbuf)); 390 if (nread < 0) 391 goto bad; 392 if (nread == 0) { 393 /* end of transmission... this may be normal 394 * or may be an error... in some cases we can't 395 * tell which so we must assume it is normal then. 396 */ 397 if (!h->got_hdr) { 398 /* Must at least have completed header */ 399 wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h); 400 goto bad; 401 } 402 if (h->chunked || h->got_content_length) { 403 /* Premature EOF; e.g. dropped connection */ 404 wpa_printf(MSG_DEBUG, 405 "httpread premature eof(%p) %d/%d", 406 h, h->body_nbytes, 407 h->content_length); 408 goto bad; 409 } 410 /* No explicit length, hopefully we have all the data 411 * although dropped connections can cause false 412 * end 413 */ 414 if (httpread_debug >= 10) 415 wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h); 416 h->got_body = 1; 417 goto got_file; 418 } 419 rbp = readbuf; 420 421 /* Header consists of text lines (terminated by both CR and LF) 422 * and an empty line (CR LF only). 423 */ 424 if (!h->got_hdr) { 425 hbp = h->hdr + h->hdr_nbytes; 426 /* add to headers until: 427 * -- we run out of data in read buffer 428 * -- or, we run out of header buffer room 429 * -- or, we get double CRLF in headers 430 */ 431 for (;;) { 432 if (nread == 0) 433 goto get_more; 434 if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) { 435 goto bad; 436 } 437 *hbp++ = *rbp++; 438 nread--; 439 h->hdr_nbytes++; 440 if (h->hdr_nbytes >= 4 && 441 hbp[-1] == '\n' && 442 hbp[-2] == '\r' && 443 hbp[-3] == '\n' && 444 hbp[-4] == '\r' ) { 445 h->got_hdr = 1; 446 *hbp = 0; /* null terminate */ 447 break; 448 } 449 } 450 /* here we've just finished reading the header */ 451 if (httpread_hdr_analyze(h)) { 452 wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h); 453 goto bad; 454 } 455 if (h->max_bytes == 0) { 456 if (httpread_debug >= 10) 457 wpa_printf(MSG_DEBUG, 458 "httpread no body hdr end(%p)", h); 459 goto got_file; 460 } 461 if (h->got_content_length && h->content_length == 0) { 462 if (httpread_debug >= 10) 463 wpa_printf(MSG_DEBUG, 464 "httpread zero content length(%p)", 465 h); 466 goto got_file; 467 } 468 } 469 470 /* Certain types of requests never have data and so 471 * must be specially recognized. 472 */ 473 if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) || 474 !os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) || 475 !os_strncasecmp(h->hdr, "HEAD", 4) || 476 !os_strncasecmp(h->hdr, "GET", 3)) { 477 if (!h->got_body) { 478 if (httpread_debug >= 10) 479 wpa_printf(MSG_DEBUG, 480 "httpread NO BODY for sp. type"); 481 } 482 h->got_body = 1; 483 goto got_file; 484 } 485 486 /* Data can be just plain binary data, or if "chunked" 487 * consists of chunks each with a header, ending with 488 * an ending header. 489 */ 490 if (nread == 0) 491 goto get_more; 492 if (!h->got_body) { 493 /* Here to get (more of) body */ 494 /* ensure we have enough room for worst case for body 495 * plus a null termination character 496 */ 497 if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) { 498 char *new_body; 499 int new_alloc_nbytes; 500 501 if (h->body_nbytes >= h->max_bytes) 502 goto bad; 503 new_alloc_nbytes = h->body_alloc_nbytes + 504 HTTPREAD_BODYBUF_DELTA; 505 /* For content-length case, the first time 506 * through we allocate the whole amount 507 * we need. 508 */ 509 if (h->got_content_length && 510 new_alloc_nbytes < (h->content_length + 1)) 511 new_alloc_nbytes = h->content_length + 1; 512 if ((new_body = os_realloc(h->body, new_alloc_nbytes)) 513 == NULL) 514 goto bad; 515 516 h->body = new_body; 517 h->body_alloc_nbytes = new_alloc_nbytes; 518 } 519 /* add bytes */ 520 bbp = h->body + h->body_nbytes; 521 for (;;) { 522 int ncopy; 523 /* See if we need to stop */ 524 if (h->chunked && h->in_chunk_data == 0) { 525 /* in chunk header */ 526 char *cbp = h->body + h->chunk_start; 527 if (bbp-cbp >= 2 && bbp[-2] == '\r' && 528 bbp[-1] == '\n') { 529 /* end of chunk hdr line */ 530 /* hdr line consists solely 531 * of a hex numeral and CFLF 532 */ 533 if (!isxdigit(*cbp)) 534 goto bad; 535 h->chunk_size = strtoul(cbp, NULL, 16); 536 /* throw away chunk header 537 * so we have only real data 538 */ 539 h->body_nbytes = h->chunk_start; 540 bbp = cbp; 541 if (h->chunk_size == 0) { 542 /* end of chunking */ 543 /* trailer follows */ 544 h->in_trailer = 1; 545 if (httpread_debug >= 20) 546 wpa_printf( 547 MSG_DEBUG, 548 "httpread end chunks(%p)", h); 549 break; 550 } 551 h->in_chunk_data = 1; 552 /* leave chunk_start alone */ 553 } 554 } else if (h->chunked) { 555 /* in chunk data */ 556 if ((h->body_nbytes - h->chunk_start) == 557 (h->chunk_size + 2)) { 558 /* end of chunk reached, 559 * new chunk starts 560 */ 561 /* check chunk ended w/ CRLF 562 * which we'll throw away 563 */ 564 if (bbp[-1] == '\n' && 565 bbp[-2] == '\r') { 566 } else 567 goto bad; 568 h->body_nbytes -= 2; 569 bbp -= 2; 570 h->chunk_start = h->body_nbytes; 571 h->in_chunk_data = 0; 572 h->chunk_size = 0; /* just in case */ 573 } 574 } else if (h->got_content_length && 575 h->body_nbytes >= h->content_length) { 576 h->got_body = 1; 577 if (httpread_debug >= 10) 578 wpa_printf( 579 MSG_DEBUG, 580 "httpread got content(%p)", h); 581 goto got_file; 582 } 583 if (nread <= 0) 584 break; 585 /* Now transfer. Optimize using memcpy where we can. */ 586 if (h->chunked && h->in_chunk_data) { 587 /* copy up to remainder of chunk data 588 * plus the required CR+LF at end 589 */ 590 ncopy = (h->chunk_start + h->chunk_size + 2) - 591 h->body_nbytes; 592 } else if (h->chunked) { 593 /*in chunk header -- don't optimize */ 594 *bbp++ = *rbp++; 595 nread--; 596 h->body_nbytes++; 597 continue; 598 } else if (h->got_content_length) { 599 ncopy = h->content_length - h->body_nbytes; 600 } else { 601 ncopy = nread; 602 } 603 /* Note: should never be 0 */ 604 if (ncopy > nread) 605 ncopy = nread; 606 os_memcpy(bbp, rbp, ncopy); 607 bbp += ncopy; 608 h->body_nbytes += ncopy; 609 rbp += ncopy; 610 nread -= ncopy; 611 } /* body copy loop */ 612 } /* !got_body */ 613 if (h->chunked && h->in_trailer) { 614 /* If "chunked" then there is always a trailer, 615 * consisting of zero or more non-empty lines 616 * ending with CR LF and then an empty line w/ CR LF. 617 * We do NOT support trailers except to skip them -- 618 * this is supported (generally) by the http spec. 619 */ 620 for (;;) { 621 int c; 622 if (nread <= 0) 623 break; 624 c = *rbp++; 625 nread--; 626 switch (h->trailer_state) { 627 case trailer_line_begin: 628 if (c == '\r') 629 h->trailer_state = trailer_empty_cr; 630 else 631 h->trailer_state = trailer_nonempty; 632 break; 633 case trailer_empty_cr: 634 /* end empty line */ 635 if (c == '\n') { 636 h->trailer_state = trailer_line_begin; 637 h->in_trailer = 0; 638 if (httpread_debug >= 10) 639 wpa_printf( 640 MSG_DEBUG, 641 "httpread got content(%p)", h); 642 h->got_body = 1; 643 goto got_file; 644 } 645 h->trailer_state = trailer_nonempty; 646 break; 647 case trailer_nonempty: 648 if (c == '\r') 649 h->trailer_state = trailer_nonempty_cr; 650 break; 651 case trailer_nonempty_cr: 652 if (c == '\n') 653 h->trailer_state = trailer_line_begin; 654 else 655 h->trailer_state = trailer_nonempty; 656 break; 657 } 658 } 659 } 660 goto get_more; 661 662bad: 663 /* Error */ 664 wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h); 665 (*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR); 666 return; 667 668get_more: 669 return; 670 671got_file: 672 if (httpread_debug >= 10) 673 wpa_printf(MSG_DEBUG, 674 "httpread got file %d bytes type %d", 675 h->body_nbytes, h->hdr_type); 676 /* Null terminate for convenience of some applications */ 677 if (h->body) 678 h->body[h->body_nbytes] = 0; /* null terminate */ 679 h->got_file = 1; 680 /* Assume that we do NOT support keeping connection alive, 681 * and just in case somehow we don't get destroyed right away, 682 * unregister now. 683 */ 684 eloop_unregister_sock(h->sd, EVENT_TYPE_READ); 685 /* The application can destroy us whenever they feel like... 686 * cancel timeout. 687 */ 688 eloop_cancel_timeout(httpread_timeout_handler, NULL, h); 689 (*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY); 690} 691 692 693/* httpread_create -- start a new reading session making use of eloop. 694 * The new instance will use the socket descriptor for reading (until 695 * it gets a file and not after) but will not close the socket, even 696 * when the instance is destroyed (the application must do that). 697 * Return NULL on error. 698 * 699 * Provided that httpread_create successfully returns a handle, 700 * the callback fnc is called to handle httpread_event events. 701 * The caller should do destroy on any errors or unknown events. 702 * 703 * Pass max_bytes == 0 to not read body at all (required for e.g. 704 * reply to HEAD request). 705 */ 706struct httpread * httpread_create( 707 int sd, /* descriptor of TCP socket to read from */ 708 void (*cb)(struct httpread *handle, void *cookie, 709 enum httpread_event e), /* call on event */ 710 void *cookie, /* pass to callback */ 711 int max_bytes, /* maximum body size else abort it */ 712 int timeout_seconds /* 0; or total duration timeout period */ 713 ) 714{ 715 struct httpread *h = NULL; 716 717 h = os_zalloc(sizeof(*h)); 718 if (h == NULL) 719 goto fail; 720 h->sd = sd; 721 h->cb = cb; 722 h->cookie = cookie; 723 h->max_bytes = max_bytes; 724 h->timeout_seconds = timeout_seconds; 725 726 if (timeout_seconds > 0 && 727 eloop_register_timeout(timeout_seconds, 0, 728 httpread_timeout_handler, NULL, h)) { 729 /* No way to recover (from malloc failure) */ 730 goto fail; 731 } 732 if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler, 733 NULL, h)) { 734 /* No way to recover (from malloc failure) */ 735 goto fail; 736 } 737 return h; 738 739fail: 740 741 /* Error */ 742 httpread_destroy(h); 743 return NULL; 744} 745 746 747/* httpread_hdr_type_get -- When file is ready, returns header type. */ 748enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h) 749{ 750 return h->hdr_type; 751} 752 753 754/* httpread_uri_get -- When file is ready, uri_get returns (translated) URI 755 * or possibly NULL (which would be an error). 756 */ 757char * httpread_uri_get(struct httpread *h) 758{ 759 return h->uri; 760} 761 762 763/* httpread_reply_code_get -- When reply is ready, returns reply code */ 764int httpread_reply_code_get(struct httpread *h) 765{ 766 return h->reply_code; 767} 768 769 770/* httpread_length_get -- When file is ready, returns file length. */ 771int httpread_length_get(struct httpread *h) 772{ 773 return h->body_nbytes; 774} 775 776 777/* httpread_data_get -- When file is ready, returns file content 778 * with null byte appened. 779 * Might return NULL in some error condition. 780 */ 781void * httpread_data_get(struct httpread *h) 782{ 783 return h->body ? h->body : ""; 784} 785 786 787/* httpread_hdr_get -- When file is ready, returns header content 788 * with null byte appended. 789 * Might return NULL in some error condition. 790 */ 791char * httpread_hdr_get(struct httpread *h) 792{ 793 return h->hdr; 794} 795 796 797/* httpread_hdr_line_get -- When file is ready, returns pointer 798 * to line within header content matching the given tag 799 * (after the tag itself and any spaces/tabs). 800 * 801 * The tag should end with a colon for reliable matching. 802 * 803 * If not found, returns NULL; 804 */ 805char * httpread_hdr_line_get(struct httpread *h, const char *tag) 806{ 807 int tag_len = os_strlen(tag); 808 char *hdr = h->hdr; 809 hdr = os_strchr(hdr, '\n'); 810 if (hdr == NULL) 811 return NULL; 812 hdr++; 813 for (;;) { 814 if (!os_strncasecmp(hdr, tag, tag_len)) { 815 hdr += tag_len; 816 while (*hdr == ' ' || *hdr == '\t') 817 hdr++; 818 return hdr; 819 } 820 hdr = os_strchr(hdr, '\n'); 821 if (hdr == NULL) 822 return NULL; 823 hdr++; 824 } 825} 826