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 word_eq(hbp
, "TRANSFER-ENCODING:")) {
211 while (isgraph(*hbp
))
213 while (*hbp
== ' ' || *hbp
== '\t')
215 /* There should (?) be no encodings of interest
216 * other than chunked...
218 if (word_eq(hbp
, "CHUNKED")) {
220 h
->in_chunk_data
= 0;
221 /* ignore possible ;<parameters> */
225 /* skip anything we don't know, which is a lot */
230 static int httpread_hdr_analyze(struct httpread
*h
)
232 char *hbp
= h
->hdr
; /* pointer into h->hdr */
233 int standard_first_line
= 1;
235 /* First line is special */
236 h
->hdr_type
= HTTPREAD_HDR_TYPE_UNKNOWN
;
239 if (os_strncmp(hbp
, "HTTP/", 5) == 0) {
240 h
->hdr_type
= HTTPREAD_HDR_TYPE_REPLY
;
241 standard_first_line
= 0;
243 if (hbp
[0] == '1' && hbp
[1] == '.' &&
244 isdigit(hbp
[2]) && hbp
[2] != '0')
246 while (isgraph(*hbp
))
248 while (*hbp
== ' ' || *hbp
== '\t')
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
;
280 if (standard_first_line
) {
284 while (isgraph(*hbp
))
286 while (*hbp
== ' ' || *hbp
== '\t')
289 * Find length, allocate memory for translated
290 * copy, then translate by changing %<hex><hex>
291 * into represented value.
294 while (isgraph(*hbp
))
296 h
->uri
= os_malloc((hbp
- rawuri
) + 1);
300 while (rawuri
< hbp
) {
303 isxdigit(rawuri
[1]) && isxdigit(rawuri
[2])) {
304 *uri
++ = (hex_value(rawuri
[1]) << 4) |
305 hex_value(rawuri
[2]);
312 *uri
= 0; /* null terminate */
313 while (isgraph(*hbp
))
315 while (*hbp
== ' ' || *hbp
== '\t')
318 if (0 == strncmp(hbp
, "HTTP/", 5)) {
320 if (hbp
[0] == '1' && hbp
[1] == '.' &&
321 isdigit(hbp
[2]) && hbp
[2] != '0')
325 /* skip rest of line */
330 /* Remainder of lines are options, in any order;
331 * or empty line to terminate
334 /* Empty line to terminate */
335 if (hbp
[0] == '\n' ||
336 (hbp
[0] == '\r' && hbp
[1] == '\n'))
340 if (httpread_hdr_option_analyze(h
, hbp
))
348 /* chunked overrides content-length always */
350 h
->got_content_length
= 0;
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.
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
362 if (h
->reply_code
!= 200)
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... ?
370 if (h
->chunked
== 0 && h
->got_content_length
== 0)
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
:
395 /* httpread_read_handler -- called when socket ready to read
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.
401 static void httpread_read_handler(int sd
, void *eloop_ctx
, void *sock_ctx
)
403 struct httpread
*h
= sock_ctx
;
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 */
410 if (httpread_debug
>= 20)
411 wpa_printf(MSG_DEBUG
, "ENTER httpread_read_handler(%p)", h
);
413 /* read some at a time, then search for the interal
414 * boundaries between header and data and etc.
416 nread
= read(h
->sd
, readbuf
, sizeof(readbuf
));
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.
425 /* Must at least have completed header */
426 wpa_printf(MSG_DEBUG
, "httpread premature eof(%p)", h
);
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",
437 /* No explicit length, hopefully we have all the data
438 * although dropped connections can cause false
441 if (httpread_debug
>= 10)
442 wpa_printf(MSG_DEBUG
, "httpread ok eof(%p)", h
);
448 /* Header consists of text lines (terminated by both CR and LF)
449 * and an empty line (CR LF only).
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
461 if (h
->hdr_nbytes
== HTTPREAD_HEADER_MAX_SIZE
) {
467 if (h
->hdr_nbytes
>= 4 &&
473 *hbp
= 0; /* null terminate */
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
);
482 if (h
->max_bytes
== 0) {
483 if (httpread_debug
>= 10)
484 wpa_printf(MSG_DEBUG
,
485 "httpread no body hdr end(%p)", h
);
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)",
497 /* Certain types of requests never have data and so
498 * must be specially recognized.
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)) {
505 if (httpread_debug
>= 10)
506 wpa_printf(MSG_DEBUG
,
507 "httpread NO BODY for sp. type");
513 /* Data can be just plain binary data, or if "chunked"
514 * consists of chunks each with a header, ending with
520 /* Here to get (more of) body */
521 /* ensure we have enough room for worst case for body
522 * plus a null termination character
524 if (h
->body_alloc_nbytes
< (h
->body_nbytes
+ nread
+ 1)) {
526 int new_alloc_nbytes
;
528 if (h
->body_nbytes
>= h
->max_bytes
)
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
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
))
544 h
->body_alloc_nbytes
= new_alloc_nbytes
;
547 bbp
= h
->body
+ h
->body_nbytes
;
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' &&
556 /* end of chunk hdr line */
557 /* hdr line consists solely
558 * of a hex numeral and CFLF
562 h
->chunk_size
= strtoul(cbp
, NULL
, 16);
563 /* throw away chunk header
564 * so we have only real data
566 h
->body_nbytes
= h
->chunk_start
;
568 if (h
->chunk_size
== 0) {
569 /* end of chunking */
570 /* trailer follows */
572 if (httpread_debug
>= 20)
575 "httpread end chunks(%p)", h
);
578 h
->in_chunk_data
= 1;
579 /* leave chunk_start alone */
581 } else if (h
->chunked
) {
583 if ((h
->body_nbytes
- h
->chunk_start
) ==
584 (h
->chunk_size
+ 2)) {
585 /* end of chunk reached,
588 /* check chunk ended w/ CRLF
589 * which we'll throw away
591 if (bbp
[-1] == '\n' &&
597 h
->chunk_start
= h
->body_nbytes
;
598 h
->in_chunk_data
= 0;
599 h
->chunk_size
= 0; /* just in case */
601 } else if (h
->got_content_length
&&
602 h
->body_nbytes
>= h
->content_length
) {
604 if (httpread_debug
>= 10)
607 "httpread got content(%p)", h
);
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
617 ncopy
= (h
->chunk_start
+ h
->chunk_size
+ 2) -
619 } else if (h
->chunked
) {
620 /*in chunk header -- don't optimize */
625 } else if (h
->got_content_length
) {
626 ncopy
= h
->content_length
- h
->body_nbytes
;
630 /* Note: should never be 0 */
633 os_memcpy(bbp
, rbp
, ncopy
);
635 h
->body_nbytes
+= ncopy
;
638 } /* body copy loop */
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.
647 bbp
= h
->body
+ h
->body_nbytes
;
654 switch (h
->trailer_state
) {
655 case trailer_line_begin
:
657 h
->trailer_state
= trailer_empty_cr
;
659 h
->trailer_state
= trailer_nonempty
;
661 case trailer_empty_cr
:
664 h
->trailer_state
= trailer_line_begin
;
666 if (httpread_debug
>= 10)
669 "httpread got content(%p)", h
);
673 h
->trailer_state
= trailer_nonempty
;
675 case trailer_nonempty
:
677 h
->trailer_state
= trailer_nonempty_cr
;
679 case trailer_nonempty_cr
:
681 h
->trailer_state
= trailer_line_begin
;
683 h
->trailer_state
= trailer_nonempty
;
692 wpa_printf(MSG_DEBUG
, "httpread read/parse failure (%p)", h
);
693 (*h
->cb
)(h
, h
->cookie
, HTTPREAD_EVENT_ERROR
);
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 */
706 h
->body
[h
->body_nbytes
] = 0; /* null terminate */
708 /* Assume that we do NOT support keeping connection alive,
709 * and just in case somehow we don't get destroyed right away,
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...
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
);
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.
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.
735 * Pass max_bytes == 0 to not read body at all (required for e.g.
736 * reply to HEAD request).
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 */
747 struct httpread
*h
= NULL
;
749 h
= os_zalloc(sizeof(*h
));
755 h
->max_bytes
= max_bytes
;
756 h
->timeout_seconds
= timeout_seconds
;
758 if (timeout_seconds
> 0) {
759 if (eloop_register_timeout(timeout_seconds
, 0,
760 httpread_timeout_handler
,
762 /* No way to recover (from malloc failure) */
765 h
->to_registered
= 1;
767 if (eloop_register_sock(sd
, EVENT_TYPE_READ
, httpread_read_handler
,
769 /* No way to recover (from malloc failure) */
772 h
->sd_registered
= 1;
783 /* httpread_hdr_type_get -- When file is ready, returns header type. */
784 enum httpread_hdr_type
httpread_hdr_type_get(struct httpread
*h
)
790 /* httpread_uri_get -- When file is ready, uri_get returns (translated) URI
791 * or possibly NULL (which would be an error).
793 char * httpread_uri_get(struct httpread
*h
)
799 /* httpread_reply_code_get -- When reply is ready, returns reply code */
800 int httpread_reply_code_get(struct httpread
*h
)
802 return h
->reply_code
;
806 /* httpread_length_get -- When file is ready, returns file length. */
807 int httpread_length_get(struct httpread
*h
)
809 return h
->body_nbytes
;
813 /* httpread_data_get -- When file is ready, returns file content
814 * with null byte appened.
815 * Might return NULL in some error condition.
817 void * httpread_data_get(struct httpread
*h
)
819 return h
->body
? h
->body
: "";
823 /* httpread_hdr_get -- When file is ready, returns header content
824 * with null byte appended.
825 * Might return NULL in some error condition.
827 char * httpread_hdr_get(struct httpread
*h
)
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).
837 * The tag should end with a colon for reliable matching.
839 * If not found, returns NULL;
841 char * httpread_hdr_line_get(struct httpread
*h
, const char *tag
)
843 int tag_len
= os_strlen(tag
);
845 hdr
= os_strchr(hdr
, '\n');
850 if (!os_strncasecmp(hdr
, tag
, tag_len
)) {
852 while (*hdr
== ' ' || *hdr
== '\t')
856 hdr
= os_strchr(hdr
, '\n');