2 * httpread - Manage reading file(s) from HTTP/TCP socket
4 * Copyright 2008 Atheros Communications
6 * This software may be distributed under the terms of the BSD license.
7 * See README for more details.
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.).
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.
19 * It is assumed that the application does not expect any of the following:
20 * -- transfer encoding other than chunked
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.
28 * -- HTTP header may not exceed a hard-coded size.
31 * This code would be massively simpler without some of the new features of
32 * HTTP/1.1, especially chunked data.
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 */
48 /* control instance -- actual definition (opaque to application)
51 /* information from creation */
52 int sd; /* descriptor of TCP socket to read from */
53 void (*cb)(struct httpread *handle, void *cookie,
54 enum httpread_event e); /* call on event */
55 void *cookie; /* pass to callback */
56 int max_bytes; /* maximum file size else abort it */
57 int timeout_seconds; /* 0 or total duration timeout period */
59 /* dynamically used information follows */
61 int got_hdr; /* nonzero when header is finalized */
62 char hdr[HTTPREAD_HEADER_MAX_SIZE+1]; /* headers stored here */
65 enum httpread_hdr_type hdr_type;
66 int version; /* 1 if we've seen 1.1 */
67 int reply_code; /* for type REPLY, e.g. 200 for HTTP/1.1 200 OK */
68 int got_content_length; /* true if we know content length for sure */
69 int content_length; /* body length, iff got_content_length */
70 int chunked; /* nonzero for chunked data */
73 int got_body; /* nonzero when body is finalized */
76 int body_alloc_nbytes; /* amount allocated */
78 int got_file; /* here when we are done */
80 /* The following apply if data is chunked: */
81 int in_chunk_data; /* 0=in/at header, 1=in the data or tail*/
82 int chunk_start; /* offset in body of chunk hdr or data */
83 int chunk_size; /* data of chunk (not hdr or ending CRLF)*/
84 int in_trailer; /* in header fields after data (chunked only)*/
86 trailer_line_begin = 0,
87 trailer_empty_cr, /* empty line + CR */
94 /* Check words for equality, where words consist of graphical characters
95 * delimited by whitespace
96 * Returns nonzero if "equal" doing case insensitive comparison.
98 static int word_eq(char *s1, char *s2)
107 if (isalpha(c1) && isupper(c1))
109 if (isalpha(c2) && isupper(c2))
113 if (end1 || end2 || c1 != c2)
116 return end1 && end2; /* reached end of both words? */
120 static void httpread_timeout_handler(void *eloop_data, void *user_ctx);
122 /* httpread_destroy -- if h is non-NULL, clean up
123 * This must eventually be called by the application following
124 * call of the application's callback and may be called
125 * earlier if desired.
127 void httpread_destroy(struct httpread *h)
129 wpa_printf(MSG_DEBUG, "httpread_destroy(%p)", h);
133 eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
134 eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
137 os_memset(h, 0, sizeof(*h)); /* aid debugging */
138 h->sd = -1; /* aid debugging */
143 /* httpread_timeout_handler -- called on excessive total duration
145 static void httpread_timeout_handler(void *eloop_data, void *user_ctx)
147 struct httpread *h = user_ctx;
148 wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h);
149 (*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT);
153 /* Analyze options only so far as is needed to correctly obtain the file.
154 * The application can look at the raw header to find other options.
156 static int httpread_hdr_option_analyze(
158 char *hbp /* pointer to current line in header buffer */
161 if (word_eq(hbp, "CONTENT-LENGTH:")) {
162 while (isgraph(*hbp))
164 while (*hbp == ' ' || *hbp == '\t')
168 h->content_length = atol(hbp);
169 if (h->content_length < 0 || h->content_length > h->max_bytes) {
170 wpa_printf(MSG_DEBUG,
171 "httpread: Unacceptable Content-Length %d",
175 h->got_content_length = 1;
178 if (word_eq(hbp, "TRANSFER_ENCODING:") ||
179 word_eq(hbp, "TRANSFER-ENCODING:")) {
180 while (isgraph(*hbp))
182 while (*hbp == ' ' || *hbp == '\t')
184 /* There should (?) be no encodings of interest
185 * other than chunked...
187 if (word_eq(hbp, "CHUNKED")) {
189 h->in_chunk_data = 0;
190 /* ignore possible ;<parameters> */
194 /* skip anything we don't know, which is a lot */
199 static int httpread_hdr_analyze(struct httpread *h)
201 char *hbp = h->hdr; /* pointer into h->hdr */
202 int standard_first_line = 1;
204 /* First line is special */
205 h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN;
208 if (os_strncmp(hbp, "HTTP/", 5) == 0) {
209 h->hdr_type = HTTPREAD_HDR_TYPE_REPLY;
210 standard_first_line = 0;
212 if (hbp[0] == '1' && hbp[1] == '.' &&
213 isdigit(hbp[2]) && hbp[2] != '0')
215 while (isgraph(*hbp))
217 while (*hbp == ' ' || *hbp == '\t')
221 h->reply_code = atol(hbp);
222 } else if (word_eq(hbp, "GET"))
223 h->hdr_type = HTTPREAD_HDR_TYPE_GET;
224 else if (word_eq(hbp, "HEAD"))
225 h->hdr_type = HTTPREAD_HDR_TYPE_HEAD;
226 else if (word_eq(hbp, "POST"))
227 h->hdr_type = HTTPREAD_HDR_TYPE_POST;
228 else if (word_eq(hbp, "PUT"))
229 h->hdr_type = HTTPREAD_HDR_TYPE_PUT;
230 else if (word_eq(hbp, "DELETE"))
231 h->hdr_type = HTTPREAD_HDR_TYPE_DELETE;
232 else if (word_eq(hbp, "TRACE"))
233 h->hdr_type = HTTPREAD_HDR_TYPE_TRACE;
234 else if (word_eq(hbp, "CONNECT"))
235 h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT;
236 else if (word_eq(hbp, "NOTIFY"))
237 h->hdr_type = HTTPREAD_HDR_TYPE_NOTIFY;
238 else if (word_eq(hbp, "M-SEARCH"))
239 h->hdr_type = HTTPREAD_HDR_TYPE_M_SEARCH;
240 else if (word_eq(hbp, "M-POST"))
241 h->hdr_type = HTTPREAD_HDR_TYPE_M_POST;
242 else if (word_eq(hbp, "SUBSCRIBE"))
243 h->hdr_type = HTTPREAD_HDR_TYPE_SUBSCRIBE;
244 else if (word_eq(hbp, "UNSUBSCRIBE"))
245 h->hdr_type = HTTPREAD_HDR_TYPE_UNSUBSCRIBE;
249 if (standard_first_line) {
253 while (isgraph(*hbp))
255 while (*hbp == ' ' || *hbp == '\t')
258 * Find length, allocate memory for translated
259 * copy, then translate by changing %<hex><hex>
260 * into represented value.
263 while (isgraph(*hbp))
265 h->uri = os_malloc((hbp - rawuri) + 1);
269 while (rawuri < hbp) {
272 isxdigit(rawuri[1]) && isxdigit(rawuri[2])) {
273 *uri++ = hex2byte(rawuri + 1);
280 *uri = 0; /* null terminate */
281 while (*hbp == ' ' || *hbp == '\t')
284 if (0 == strncmp(hbp, "HTTP/", 5)) {
286 if (hbp[0] == '1' && hbp[1] == '.' &&
287 isdigit(hbp[2]) && hbp[2] != '0')
291 /* skip rest of line */
296 /* Remainder of lines are options, in any order;
297 * or empty line to terminate
300 /* Empty line to terminate */
301 if (hbp[0] == '\n' ||
302 (hbp[0] == '\r' && hbp[1] == '\n'))
306 if (httpread_hdr_option_analyze(h, hbp))
314 /* chunked overrides content-length always */
316 h->got_content_length = 0;
318 /* For some types, we should not try to read a body
319 * This is in addition to the application determining
320 * that we should not read a body.
322 switch (h->hdr_type) {
323 case HTTPREAD_HDR_TYPE_REPLY:
324 /* Some codes can have a body and some not.
325 * For now, just assume that any other than 200
328 if (h->reply_code != 200)
331 case HTTPREAD_HDR_TYPE_GET:
332 case HTTPREAD_HDR_TYPE_HEAD:
333 /* in practice it appears that it is assumed
334 * that GETs have a body length of 0... ?
336 if (h->chunked == 0 && h->got_content_length == 0)
339 case HTTPREAD_HDR_TYPE_POST:
340 case HTTPREAD_HDR_TYPE_PUT:
341 case HTTPREAD_HDR_TYPE_DELETE:
342 case HTTPREAD_HDR_TYPE_TRACE:
343 case HTTPREAD_HDR_TYPE_CONNECT:
344 case HTTPREAD_HDR_TYPE_NOTIFY:
345 case HTTPREAD_HDR_TYPE_M_SEARCH:
346 case HTTPREAD_HDR_TYPE_M_POST:
347 case HTTPREAD_HDR_TYPE_SUBSCRIBE:
348 case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
361 /* httpread_read_handler -- called when socket ready to read
363 * Note: any extra data we read past end of transmitted file is ignored;
364 * if we were to support keeping connections open for multiple files then
365 * this would have to be addressed.
367 static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx)
369 struct httpread *h = sock_ctx;
371 char *rbp; /* pointer into read buffer */
372 char *hbp; /* pointer into header buffer */
373 char *bbp; /* pointer into body buffer */
374 char readbuf[HTTPREAD_READBUF_SIZE]; /* temp use to read into */
376 /* read some at a time, then search for the interal
377 * boundaries between header and data and etc.
379 wpa_printf(MSG_DEBUG, "httpread: Trying to read more data(%p)", h);
380 nread = read(h->sd, readbuf, sizeof(readbuf));
382 wpa_printf(MSG_DEBUG, "httpread failed: %s", strerror(errno));
385 wpa_hexdump_ascii(MSG_MSGDUMP, "httpread - read", readbuf, nread);
387 /* end of transmission... this may be normal
388 * or may be an error... in some cases we can't
389 * tell which so we must assume it is normal then.
392 /* Must at least have completed header */
393 wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h);
396 if (h->chunked || h->got_content_length) {
397 /* Premature EOF; e.g. dropped connection */
398 wpa_printf(MSG_DEBUG,
399 "httpread premature eof(%p) %d/%d",
404 /* No explicit length, hopefully we have all the data
405 * although dropped connections can cause false
408 wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
414 /* Header consists of text lines (terminated by both CR and LF)
415 * and an empty line (CR LF only).
418 hbp = h->hdr + h->hdr_nbytes;
419 /* add to headers until:
420 * -- we run out of data in read buffer
421 * -- or, we run out of header buffer room
422 * -- or, we get double CRLF in headers
427 if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) {
428 wpa_printf(MSG_DEBUG,
429 "httpread: Too long header");
435 if (h->hdr_nbytes >= 4 &&
441 *hbp = 0; /* null terminate */
445 /* here we've just finished reading the header */
446 if (httpread_hdr_analyze(h)) {
447 wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h);
450 if (h->max_bytes == 0) {
451 wpa_printf(MSG_DEBUG, "httpread no body hdr end(%p)",
455 if (h->got_content_length && h->content_length == 0) {
456 wpa_printf(MSG_DEBUG,
457 "httpread zero content length(%p)", h);
462 /* Certain types of requests never have data and so
463 * must be specially recognized.
465 if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) ||
466 !os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) ||
467 !os_strncasecmp(h->hdr, "HEAD", 4) ||
468 !os_strncasecmp(h->hdr, "GET", 3)) {
470 wpa_printf(MSG_DEBUG, "httpread NO BODY for sp. type");
476 /* Data can be just plain binary data, or if "chunked"
477 * consists of chunks each with a header, ending with
483 /* Here to get (more of) body */
484 /* ensure we have enough room for worst case for body
485 * plus a null termination character
487 if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) {
489 int new_alloc_nbytes;
491 if (h->body_nbytes >= h->max_bytes) {
492 wpa_printf(MSG_DEBUG,
493 "httpread: body_nbytes=%d >= max_bytes=%d",
494 h->body_nbytes, h->max_bytes);
497 new_alloc_nbytes = h->body_alloc_nbytes +
498 HTTPREAD_BODYBUF_DELTA;
499 /* For content-length case, the first time
500 * through we allocate the whole amount
503 if (h->got_content_length &&
504 new_alloc_nbytes < (h->content_length + 1))
505 new_alloc_nbytes = h->content_length + 1;
506 if (new_alloc_nbytes < h->body_alloc_nbytes ||
507 new_alloc_nbytes > h->max_bytes +
508 HTTPREAD_BODYBUF_DELTA) {
509 wpa_printf(MSG_DEBUG,
510 "httpread: Unacceptable body length %d (body_alloc_nbytes=%u max_bytes=%u)",
512 h->body_alloc_nbytes,
516 if ((new_body = os_realloc(h->body, new_alloc_nbytes))
518 wpa_printf(MSG_DEBUG,
519 "httpread: Failed to reallocate buffer (len=%d)",
525 h->body_alloc_nbytes = new_alloc_nbytes;
528 bbp = h->body + h->body_nbytes;
531 /* See if we need to stop */
532 if (h->chunked && h->in_chunk_data == 0) {
533 /* in chunk header */
534 char *cbp = h->body + h->chunk_start;
535 if (bbp-cbp >= 2 && bbp[-2] == '\r' &&
537 /* end of chunk hdr line */
538 /* hdr line consists solely
539 * of a hex numeral and CFLF
541 if (!isxdigit(*cbp)) {
542 wpa_printf(MSG_DEBUG,
543 "httpread: Unexpected chunk header value (not a hex digit)");
546 h->chunk_size = strtoul(cbp, NULL, 16);
547 if (h->chunk_size < 0 ||
548 h->chunk_size > h->max_bytes) {
549 wpa_printf(MSG_DEBUG,
550 "httpread: Invalid chunk size %d",
554 /* throw away chunk header
555 * so we have only real data
557 h->body_nbytes = h->chunk_start;
559 if (h->chunk_size == 0) {
560 /* end of chunking */
561 /* trailer follows */
563 wpa_printf(MSG_DEBUG,
564 "httpread end chunks(%p)",
568 h->in_chunk_data = 1;
569 /* leave chunk_start alone */
571 } else if (h->chunked) {
573 if ((h->body_nbytes - h->chunk_start) ==
574 (h->chunk_size + 2)) {
575 /* end of chunk reached,
578 /* check chunk ended w/ CRLF
579 * which we'll throw away
581 if (bbp[-1] == '\n' &&
584 wpa_printf(MSG_DEBUG,
585 "httpread: Invalid chunk end");
590 h->chunk_start = h->body_nbytes;
591 h->in_chunk_data = 0;
592 h->chunk_size = 0; /* just in case */
594 } else if (h->got_content_length &&
595 h->body_nbytes >= h->content_length) {
597 wpa_printf(MSG_DEBUG,
598 "httpread got content(%p)", h);
603 /* Now transfer. Optimize using memcpy where we can. */
604 if (h->chunked && h->in_chunk_data) {
605 /* copy up to remainder of chunk data
606 * plus the required CR+LF at end
608 ncopy = (h->chunk_start + h->chunk_size + 2) -
610 } else if (h->chunked) {
611 /*in chunk header -- don't optimize */
616 } else if (h->got_content_length) {
617 ncopy = h->content_length - h->body_nbytes;
621 /* Note: should never be 0 */
623 wpa_printf(MSG_DEBUG,
624 "httpread: Invalid ncopy=%d", ncopy);
629 os_memcpy(bbp, rbp, ncopy);
631 h->body_nbytes += ncopy;
634 } /* body copy loop */
636 if (h->chunked && h->in_trailer) {
637 /* If "chunked" then there is always a trailer,
638 * consisting of zero or more non-empty lines
639 * ending with CR LF and then an empty line w/ CR LF.
640 * We do NOT support trailers except to skip them --
641 * this is supported (generally) by the http spec.
649 switch (h->trailer_state) {
650 case trailer_line_begin:
652 h->trailer_state = trailer_empty_cr;
654 h->trailer_state = trailer_nonempty;
656 case trailer_empty_cr:
659 h->trailer_state = trailer_line_begin;
661 wpa_printf(MSG_DEBUG,
662 "httpread got content(%p)",
667 h->trailer_state = trailer_nonempty;
669 case trailer_nonempty:
671 h->trailer_state = trailer_nonempty_cr;
673 case trailer_nonempty_cr:
675 h->trailer_state = trailer_line_begin;
677 h->trailer_state = trailer_nonempty;
686 wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h);
687 (*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR);
691 wpa_printf(MSG_DEBUG, "httpread: get more (%p)", h);
695 wpa_printf(MSG_DEBUG, "httpread got file %d bytes type %d",
696 h->body_nbytes, h->hdr_type);
697 wpa_hexdump_ascii(MSG_MSGDUMP, "httpread: body",
698 h->body, h->body_nbytes);
699 /* Null terminate for convenience of some applications */
701 h->body[h->body_nbytes] = 0; /* null terminate */
703 /* Assume that we do NOT support keeping connection alive,
704 * and just in case somehow we don't get destroyed right away,
707 eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
708 /* The application can destroy us whenever they feel like...
711 eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
712 (*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY);
716 /* httpread_create -- start a new reading session making use of eloop.
717 * The new instance will use the socket descriptor for reading (until
718 * it gets a file and not after) but will not close the socket, even
719 * when the instance is destroyed (the application must do that).
720 * Return NULL on error.
722 * Provided that httpread_create successfully returns a handle,
723 * the callback fnc is called to handle httpread_event events.
724 * The caller should do destroy on any errors or unknown events.
726 * Pass max_bytes == 0 to not read body at all (required for e.g.
727 * reply to HEAD request).
729 struct httpread * httpread_create(
730 int sd, /* descriptor of TCP socket to read from */
731 void (*cb)(struct httpread *handle, void *cookie,
732 enum httpread_event e), /* call on event */
733 void *cookie, /* pass to callback */
734 int max_bytes, /* maximum body size else abort it */
735 int timeout_seconds /* 0; or total duration timeout period */
738 struct httpread *h = NULL;
740 h = os_zalloc(sizeof(*h));
746 h->max_bytes = max_bytes;
747 h->timeout_seconds = timeout_seconds;
749 if (timeout_seconds > 0 &&
750 eloop_register_timeout(timeout_seconds, 0,
751 httpread_timeout_handler, NULL, h)) {
752 /* No way to recover (from malloc failure) */
755 if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler,
757 /* No way to recover (from malloc failure) */
770 /* httpread_hdr_type_get -- When file is ready, returns header type. */
771 enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h)
777 /* httpread_uri_get -- When file is ready, uri_get returns (translated) URI
778 * or possibly NULL (which would be an error).
780 char * httpread_uri_get(struct httpread *h)
786 /* httpread_reply_code_get -- When reply is ready, returns reply code */
787 int httpread_reply_code_get(struct httpread *h)
789 return h->reply_code;
793 /* httpread_length_get -- When file is ready, returns file length. */
794 int httpread_length_get(struct httpread *h)
796 return h->body_nbytes;
800 /* httpread_data_get -- When file is ready, returns file content
801 * with null byte appened.
802 * Might return NULL in some error condition.
804 void * httpread_data_get(struct httpread *h)
806 return h->body ? h->body : "";
810 /* httpread_hdr_get -- When file is ready, returns header content
811 * with null byte appended.
812 * Might return NULL in some error condition.
814 char * httpread_hdr_get(struct httpread *h)
820 /* httpread_hdr_line_get -- When file is ready, returns pointer
821 * to line within header content matching the given tag
822 * (after the tag itself and any spaces/tabs).
824 * The tag should end with a colon for reliable matching.
826 * If not found, returns NULL;
828 char * httpread_hdr_line_get(struct httpread *h, const char *tag)
830 int tag_len = os_strlen(tag);
832 hdr = os_strchr(hdr, '\n');
837 if (!os_strncasecmp(hdr, tag, tag_len)) {
839 while (*hdr == ' ' || *hdr == '\t')
843 hdr = os_strchr(hdr, '\n');