2 * httpread - Manage reading file(s) from HTTP/TCP socket
4 * Copyright 2008 Atheros Communications
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.
10 * Alternatively, this software may be distributed under the terms of BSD
13 * See README and COPYING for more details.
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.).
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.
25 * It is assumed that the application does not expect any of the following:
26 * -- transfer encoding other than chunked
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.
34 * -- HTTP header may not exceed a hard-coded size.
37 * This code would be massively simpler without some of the new features of
38 * HTTP/1.1, especially chunked data.
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 */
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.
58 int httpread_debug = 99;
60 #define httpread_debug 0 /* eliminates even the debugging code */
64 /* control instance -- actual definition (opaque to application)
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 */
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 */
79 int got_hdr; /* nonzero when header is finalized */
80 char hdr[HTTPREAD_HEADER_MAX_SIZE+1]; /* headers stored here */
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 */
91 int got_body; /* nonzero when body is finalized */
94 int body_alloc_nbytes; /* amount allocated */
96 int got_file; /* here when we are done */
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)*/
104 trailer_line_begin = 0,
105 trailer_empty_cr, /* empty line + CR */
112 /* Check words for equality, where words consist of graphical characters
113 * delimited by whitespace
114 * Returns nonzero if "equal" doing case insensitive comparison.
116 static int word_eq(char *s1, char *s2)
125 if (isalpha(c1) && isupper(c1))
127 if (isalpha(c2) && isupper(c2))
131 if (end1 || end2 || c1 != c2)
134 return end1 && end2; /* reached end of both words? */
138 /* convert hex to binary
139 * Requires that c have been previously tested true with isxdigit().
141 static int hex_value(int c)
151 static void httpread_timeout_handler(void *eloop_data, void *user_ctx);
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.
158 void httpread_destroy(struct httpread *h)
160 if (httpread_debug >= 10)
161 wpa_printf(MSG_DEBUG, "ENTER httpread_destroy(%p)", h);
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;
173 os_memset(h, 0, sizeof(*h)); /* aid debugging */
174 h->sd = -1; /* aid debugging */
179 /* httpread_timeout_handler -- called on excessive total duration
181 static void httpread_timeout_handler(void *eloop_data, void *user_ctx)
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);
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.
193 static int httpread_hdr_option_analyze(
195 char *hbp /* pointer to current line in header buffer */
198 if (word_eq(hbp, "CONTENT-LENGTH:")) {
199 while (isgraph(*hbp))
201 while (*hbp == ' ' || *hbp == '\t')
205 h->content_length = atol(hbp);
206 h->got_content_length = 1;
209 if (word_eq(hbp, "TRANSFER_ENCODING:")) {
210 while (isgraph(*hbp))
212 while (*hbp == ' ' || *hbp == '\t')
214 /* There should (?) be no encodings of interest
215 * other than chunked...
217 if (os_strncmp(hbp, "CHUNKED", 7)) {
219 h->in_chunk_data = 0;
220 /* ignore possible ;<parameters> */
224 /* skip anything we don't know, which is a lot */
229 static int httpread_hdr_analyze(struct httpread *h)
231 char *hbp = h->hdr; /* pointer into h->hdr */
232 int standard_first_line = 1;
234 /* First line is special */
235 h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN;
238 if (os_strncmp(hbp, "HTTP/", 5) == 0) {
239 h->hdr_type = HTTPREAD_HDR_TYPE_REPLY;
240 standard_first_line = 0;
242 if (hbp[0] == '1' && hbp[1] == '.' &&
243 isdigit(hbp[2]) && hbp[2] != '0')
245 while (isgraph(*hbp))
247 while (*hbp == ' ' || *hbp == '\t')
251 h->reply_code = atol(hbp);
252 } else if (word_eq(hbp, "GET"))
253 h->hdr_type = HTTPREAD_HDR_TYPE_GET;
254 else if (word_eq(hbp, "HEAD"))
255 h->hdr_type = HTTPREAD_HDR_TYPE_HEAD;
256 else if (word_eq(hbp, "POST"))
257 h->hdr_type = HTTPREAD_HDR_TYPE_POST;
258 else if (word_eq(hbp, "PUT"))
259 h->hdr_type = HTTPREAD_HDR_TYPE_PUT;
260 else if (word_eq(hbp, "DELETE"))
261 h->hdr_type = HTTPREAD_HDR_TYPE_DELETE;
262 else if (word_eq(hbp, "TRACE"))
263 h->hdr_type = HTTPREAD_HDR_TYPE_TRACE;
264 else if (word_eq(hbp, "CONNECT"))
265 h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT;
266 else if (word_eq(hbp, "NOTIFY"))
267 h->hdr_type = HTTPREAD_HDR_TYPE_NOTIFY;
268 else if (word_eq(hbp, "M-SEARCH"))
269 h->hdr_type = HTTPREAD_HDR_TYPE_M_SEARCH;
270 else if (word_eq(hbp, "M-POST"))
271 h->hdr_type = HTTPREAD_HDR_TYPE_M_POST;
272 else if (word_eq(hbp, "SUBSCRIBE"))
273 h->hdr_type = HTTPREAD_HDR_TYPE_SUBSCRIBE;
274 else if (word_eq(hbp, "UNSUBSCRIBE"))
275 h->hdr_type = HTTPREAD_HDR_TYPE_UNSUBSCRIBE;
279 if (standard_first_line) {
283 while (isgraph(*hbp))
285 while (*hbp == ' ' || *hbp == '\t')
288 * Find length, allocate memory for translated
289 * copy, then translate by changing %<hex><hex>
290 * into represented value.
293 while (isgraph(*hbp))
295 h->uri = os_malloc((hbp - rawuri) + 1);
299 while (rawuri < hbp) {
302 isxdigit(rawuri[1]) && isxdigit(rawuri[2])) {
303 *uri++ = (hex_value(rawuri[1]) << 4) |
304 hex_value(rawuri[2]);
311 *uri = 0; /* null terminate */
312 while (isgraph(*hbp))
314 while (*hbp == ' ' || *hbp == '\t')
317 if (0 == strncmp(hbp, "HTTP/", 5)) {
319 if (hbp[0] == '1' && hbp[1] == '.' &&
320 isdigit(hbp[2]) && hbp[2] != '0')
324 /* skip rest of line */
329 /* Remainder of lines are options, in any order;
330 * or empty line to terminate
333 /* Empty line to terminate */
334 if (hbp[0] == '\n' ||
335 (hbp[0] == '\r' && hbp[1] == '\n'))
339 if (httpread_hdr_option_analyze(h, hbp))
347 /* chunked overrides content-length always */
349 h->got_content_length = 0;
351 /* For some types, we should not try to read a body
352 * This is in addition to the application determining
353 * that we should not read a body.
355 switch (h->hdr_type) {
356 case HTTPREAD_HDR_TYPE_REPLY:
357 /* Some codes can have a body and some not.
358 * For now, just assume that any other than 200
361 if (h->reply_code != 200)
364 case HTTPREAD_HDR_TYPE_GET:
365 case HTTPREAD_HDR_TYPE_HEAD:
366 /* in practice it appears that it is assumed
367 * that GETs have a body length of 0... ?
369 if (h->chunked == 0 && h->got_content_length == 0)
372 case HTTPREAD_HDR_TYPE_POST:
373 case HTTPREAD_HDR_TYPE_PUT:
374 case HTTPREAD_HDR_TYPE_DELETE:
375 case HTTPREAD_HDR_TYPE_TRACE:
376 case HTTPREAD_HDR_TYPE_CONNECT:
377 case HTTPREAD_HDR_TYPE_NOTIFY:
378 case HTTPREAD_HDR_TYPE_M_SEARCH:
379 case HTTPREAD_HDR_TYPE_M_POST:
380 case HTTPREAD_HDR_TYPE_SUBSCRIBE:
381 case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
394 /* httpread_read_handler -- called when socket ready to read
396 * Note: any extra data we read past end of transmitted file is ignored;
397 * if we were to support keeping connections open for multiple files then
398 * this would have to be addressed.
400 static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx)
402 struct httpread *h = sock_ctx;
404 char *rbp; /* pointer into read buffer */
405 char *hbp; /* pointer into header buffer */
406 char *bbp; /* pointer into body buffer */
407 char readbuf[HTTPREAD_READBUF_SIZE]; /* temp use to read into */
409 if (httpread_debug >= 20)
410 wpa_printf(MSG_DEBUG, "ENTER httpread_read_handler(%p)", h);
412 /* read some at a time, then search for the interal
413 * boundaries between header and data and etc.
415 nread = read(h->sd, readbuf, sizeof(readbuf));
419 /* end of transmission... this may be normal
420 * or may be an error... in some cases we can't
421 * tell which so we must assume it is normal then.
424 /* Must at least have completed header */
425 wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h);
428 if (h->chunked || h->got_content_length) {
429 /* Premature EOF; e.g. dropped connection */
430 wpa_printf(MSG_DEBUG,
431 "httpread premature eof(%p) %d/%d",
436 /* No explicit length, hopefully we have all the data
437 * although dropped connections can cause false
440 if (httpread_debug >= 10)
441 wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
447 /* Header consists of text lines (terminated by both CR and LF)
448 * and an empty line (CR LF only).
451 hbp = h->hdr + h->hdr_nbytes;
452 /* add to headers until:
453 * -- we run out of data in read buffer
454 * -- or, we run out of header buffer room
455 * -- or, we get double CRLF in headers
460 if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) {
466 if (h->hdr_nbytes >= 4 &&
472 *hbp = 0; /* null terminate */
476 /* here we've just finished reading the header */
477 if (httpread_hdr_analyze(h)) {
478 wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h);
481 if (h->max_bytes == 0) {
482 if (httpread_debug >= 10)
483 wpa_printf(MSG_DEBUG,
484 "httpread no body hdr end(%p)", h);
487 if (h->got_content_length && h->content_length == 0) {
488 if (httpread_debug >= 10)
489 wpa_printf(MSG_DEBUG,
490 "httpread zero content length(%p)",
496 /* Certain types of requests never have data and so
497 * must be specially recognized.
499 if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) ||
500 !os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) ||
501 !os_strncasecmp(h->hdr, "HEAD", 4) ||
502 !os_strncasecmp(h->hdr, "GET", 3)) {
504 if (httpread_debug >= 10)
505 wpa_printf(MSG_DEBUG,
506 "httpread NO BODY for sp. type");
512 /* Data can be just plain binary data, or if "chunked"
513 * consists of chunks each with a header, ending with
517 /* Here to get (more of) body */
518 /* ensure we have enough room for worst case for body
519 * plus a null termination character
521 if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) {
523 int new_alloc_nbytes;
525 if (h->body_nbytes >= h->max_bytes)
527 new_alloc_nbytes = h->body_alloc_nbytes +
528 HTTPREAD_BODYBUF_DELTA;
529 /* For content-length case, the first time
530 * through we allocate the whole amount
533 if (h->got_content_length &&
534 new_alloc_nbytes < (h->content_length + 1))
535 new_alloc_nbytes = h->content_length + 1;
536 if ((new_body = os_realloc(h->body, new_alloc_nbytes))
541 h->body_alloc_nbytes = new_alloc_nbytes;
544 bbp = h->body + h->body_nbytes;
547 /* See if we need to stop */
548 if (h->chunked && h->in_chunk_data == 0) {
549 /* in chunk header */
550 char *cbp = h->body + h->chunk_start;
551 if (bbp-cbp >= 2 && bbp[-2] == '\r' &&
553 /* end of chunk hdr line */
554 /* hdr line consists solely
555 * of a hex numeral and CFLF
559 h->chunk_size = strtoul(cbp, NULL, 16);
560 /* throw away chunk header
561 * so we have only real data
563 h->body_nbytes = h->chunk_start;
565 if (h->chunk_size == 0) {
566 /* end of chunking */
567 /* trailer follows */
569 if (httpread_debug >= 20)
572 "httpread end chunks(%p)", h);
575 h->in_chunk_data = 1;
576 /* leave chunk_start alone */
578 } else if (h->chunked) {
580 if ((h->body_nbytes - h->chunk_start) ==
581 (h->chunk_size + 2)) {
582 /* end of chunk reached,
585 /* check chunk ended w/ CRLF
586 * which we'll throw away
588 if (bbp[-1] == '\n' &&
594 h->chunk_start = h->body_nbytes;
595 h->in_chunk_data = 0;
596 h->chunk_size = 0; /* just in case */
598 } else if (h->got_content_length &&
599 h->body_nbytes >= h->content_length) {
601 if (httpread_debug >= 10)
604 "httpread got content(%p)", h);
609 /* Now transfer. Optimize using memcpy where we can. */
610 if (h->chunked && h->in_chunk_data) {
611 /* copy up to remainder of chunk data
612 * plus the required CR+LF at end
614 ncopy = (h->chunk_start + h->chunk_size + 2) -
616 } else if (h->chunked) {
617 /*in chunk header -- don't optimize */
622 } else if (h->got_content_length) {
623 ncopy = h->content_length - h->body_nbytes;
627 /* Note: should never be 0 */
630 os_memcpy(bbp, rbp, ncopy);
632 h->body_nbytes += ncopy;
635 } /* body copy loop */
637 if (h->chunked && h->in_trailer) {
638 /* If "chunked" then there is always a trailer,
639 * consisting of zero or more non-empty lines
640 * ending with CR LF and then an empty line w/ CR LF.
641 * We do NOT support trailers except to skip them --
642 * this is supported (generally) by the http spec.
644 bbp = h->body + h->body_nbytes;
651 switch (h->trailer_state) {
652 case trailer_line_begin:
654 h->trailer_state = trailer_empty_cr;
656 h->trailer_state = trailer_nonempty;
658 case trailer_empty_cr:
661 h->trailer_state = trailer_line_begin;
663 if (httpread_debug >= 10)
666 "httpread got content(%p)", h);
670 h->trailer_state = trailer_nonempty;
672 case trailer_nonempty:
674 h->trailer_state = trailer_nonempty_cr;
676 case trailer_nonempty_cr:
678 h->trailer_state = trailer_line_begin;
680 h->trailer_state = trailer_nonempty;
689 wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h);
690 (*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR);
697 if (httpread_debug >= 10)
698 wpa_printf(MSG_DEBUG,
699 "httpread got file %d bytes type %d",
700 h->body_nbytes, h->hdr_type);
701 /* Null terminate for convenience of some applications */
703 h->body[h->body_nbytes] = 0; /* null terminate */
705 /* Assume that we do NOT support keeping connection alive,
706 * and just in case somehow we don't get destroyed right away,
709 if (h->sd_registered)
710 eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
711 h->sd_registered = 0;
712 /* The application can destroy us whenever they feel like...
715 if (h->to_registered)
716 eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
717 h->to_registered = 0;
718 (*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY);
722 /* httpread_create -- start a new reading session making use of eloop.
723 * The new instance will use the socket descriptor for reading (until
724 * it gets a file and not after) but will not close the socket, even
725 * when the instance is destroyed (the application must do that).
726 * Return NULL on error.
728 * Provided that httpread_create successfully returns a handle,
729 * the callback fnc is called to handle httpread_event events.
730 * The caller should do destroy on any errors or unknown events.
732 * Pass max_bytes == 0 to not read body at all (required for e.g.
733 * reply to HEAD request).
735 struct httpread * httpread_create(
736 int sd, /* descriptor of TCP socket to read from */
737 void (*cb)(struct httpread *handle, void *cookie,
738 enum httpread_event e), /* call on event */
739 void *cookie, /* pass to callback */
740 int max_bytes, /* maximum body size else abort it */
741 int timeout_seconds /* 0; or total duration timeout period */
744 struct httpread *h = NULL;
746 h = os_zalloc(sizeof(*h));
752 h->max_bytes = max_bytes;
753 h->timeout_seconds = timeout_seconds;
755 if (timeout_seconds > 0) {
756 if (eloop_register_timeout(timeout_seconds, 0,
757 httpread_timeout_handler,
759 /* No way to recover (from malloc failure) */
762 h->to_registered = 1;
764 if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler,
766 /* No way to recover (from malloc failure) */
769 h->sd_registered = 1;
780 /* httpread_hdr_type_get -- When file is ready, returns header type. */
781 enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h)
787 /* httpread_uri_get -- When file is ready, uri_get returns (translated) URI
788 * or possibly NULL (which would be an error).
790 char * httpread_uri_get(struct httpread *h)
796 /* httpread_reply_code_get -- When reply is ready, returns reply code */
797 int httpread_reply_code_get(struct httpread *h)
799 return h->reply_code;
803 /* httpread_length_get -- When file is ready, returns file length. */
804 int httpread_length_get(struct httpread *h)
806 return h->body_nbytes;
810 /* httpread_data_get -- When file is ready, returns file content
811 * with null byte appened.
812 * Might return NULL in some error condition.
814 void * httpread_data_get(struct httpread *h)
816 return h->body ? h->body : "";
820 /* httpread_hdr_get -- When file is ready, returns header content
821 * with null byte appended.
822 * Might return NULL in some error condition.
824 char * httpread_hdr_get(struct httpread *h)
830 /* httpread_hdr_line_get -- When file is ready, returns pointer
831 * to line within header content matching the given tag
832 * (after the tag itself and any spaces/tabs).
834 * The tag should end with a colon for reliable matching.
836 * If not found, returns NULL;
838 char * httpread_hdr_line_get(struct httpread *h, const char *tag)
840 int tag_len = os_strlen(tag);
842 hdr = os_strchr(hdr, '\n');
847 if (!os_strncasecmp(hdr, tag, tag_len)) {
849 while (*hdr == ' ' || *hdr == '\t')
853 hdr = os_strchr(hdr, '\n');