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