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