httpread.c revision 189251
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 */
58int 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 */
66struct 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 */
116static 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 */
141static 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
151static 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 */
158void 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 */
181static 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 */
193static 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		while (isgraph(*hbp))
211			hbp++;
212		while (*hbp == ' ' || *hbp == '\t')
213			hbp++;
214		/* There should (?) be no encodings of interest
215		 * other than chunked...
216		 */
217		if (os_strncmp(hbp, "CHUNKED", 7)) {
218			h->chunked = 1;
219			h->in_chunk_data = 0;
220			/* ignore possible ;<parameters> */
221		}
222		return 0;
223	}
224	/* skip anything we don't know, which is a lot */
225	return 0;
226}
227
228
229static int httpread_hdr_analyze(struct httpread *h)
230{
231	char *hbp = h->hdr;      /* pointer into h->hdr */
232	int standard_first_line = 1;
233
234	/* First line is special */
235	h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN;
236	if (!isgraph(*hbp))
237		goto bad;
238	if (os_strncmp(hbp, "HTTP/", 5) == 0) {
239		h->hdr_type = HTTPREAD_HDR_TYPE_REPLY;
240		standard_first_line = 0;
241		hbp += 5;
242		if (hbp[0] == '1' && hbp[1] == '.' &&
243		    isdigit(hbp[2]) && hbp[2] != '0')
244			h->version = 1;
245		while (isgraph(*hbp))
246			hbp++;
247		while (*hbp == ' ' || *hbp == '\t')
248			hbp++;
249		if (!isdigit(*hbp))
250			goto bad;
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;
276	else {
277	}
278
279	if (standard_first_line) {
280		char *rawuri;
281		char *uri;
282		/* skip type */
283		while (isgraph(*hbp))
284			hbp++;
285		while (*hbp == ' ' || *hbp == '\t')
286			hbp++;
287		/* parse uri.
288		 * Find length, allocate memory for translated
289		 * copy, then translate by changing %<hex><hex>
290		 * into represented value.
291		 */
292		rawuri = hbp;
293		while (isgraph(*hbp))
294			hbp++;
295		h->uri = os_malloc((hbp - rawuri) + 1);
296		if (h->uri == NULL)
297			goto bad;
298		uri = h->uri;
299		while (rawuri < hbp) {
300			int c = *rawuri;
301			if (c == '%' &&
302			    isxdigit(rawuri[1]) && isxdigit(rawuri[2])) {
303				*uri++ = (hex_value(rawuri[1]) << 4) |
304					hex_value(rawuri[2]);
305				rawuri += 3;
306			} else {
307				*uri++ = c;
308				rawuri++;
309			}
310		}
311		*uri = 0;       /* null terminate */
312		while (isgraph(*hbp))
313			hbp++;
314		while (*hbp == ' ' || *hbp == '\t')
315			hbp++;
316		/* get version */
317		if (0 == strncmp(hbp, "HTTP/", 5)) {
318			hbp += 5;
319			if (hbp[0] == '1' && hbp[1] == '.' &&
320			    isdigit(hbp[2]) && hbp[2] != '0')
321				h->version = 1;
322		}
323	}
324	/* skip rest of line */
325	while (*hbp)
326		if (*hbp++ == '\n')
327			break;
328
329	/* Remainder of lines are options, in any order;
330	 * or empty line to terminate
331	 */
332	for (;;) {
333		/* Empty line to terminate */
334		if (hbp[0] == '\n' ||
335		    (hbp[0] == '\r' && hbp[1] == '\n'))
336			break;
337		if (!isgraph(*hbp))
338			goto bad;
339		if (httpread_hdr_option_analyze(h, hbp))
340			goto bad;
341		/* skip line */
342		while (*hbp)
343			if (*hbp++ == '\n')
344				break;
345	}
346
347	/* chunked overrides content-length always */
348	if (h->chunked)
349		h->got_content_length = 0;
350
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.
354	 */
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
359		 * do not...
360		 */
361		if (h->reply_code != 200)
362			h->max_bytes = 0;
363		break;
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... ?
368		 */
369		if (h->chunked == 0 && h->got_content_length == 0)
370			h->max_bytes = 0;
371		break;
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:
382	default:
383		break;
384	}
385
386	return 0;
387
388bad:
389	/* Error */
390	return -1;
391}
392
393
394/* httpread_read_handler -- called when socket ready to read
395 *
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.
399 */
400static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx)
401{
402	struct httpread *h = sock_ctx;
403	int nread;
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 */
408
409	if (httpread_debug >= 20)
410		wpa_printf(MSG_DEBUG, "ENTER httpread_read_handler(%p)", h);
411
412	/* read some at a time, then search for the interal
413	 * boundaries between header and data and etc.
414	 */
415	nread = read(h->sd, readbuf, sizeof(readbuf));
416	if (nread < 0)
417		goto bad;
418	if (nread == 0) {
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.
422		 */
423		if (!h->got_hdr) {
424			/* Must at least have completed header */
425			wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h);
426			goto bad;
427		}
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",
432				   h, h->body_nbytes,
433				   h->content_length);
434			goto bad;
435		}
436		/* No explicit length, hopefully we have all the data
437		 * although dropped connections can cause false
438		 * end
439		 */
440		if (httpread_debug >= 10)
441			wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
442			h->got_body = 1;
443			goto got_file;
444	}
445	rbp = readbuf;
446
447	/* Header consists of text lines (terminated by both CR and LF)
448	 * and an empty line (CR LF only).
449	 */
450	if (!h->got_hdr) {
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
456		 */
457		for (;;) {
458			if (nread == 0)
459				goto get_more;
460			if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) {
461				goto bad;
462			}
463			*hbp++ = *rbp++;
464			nread--;
465			h->hdr_nbytes++;
466			if (h->hdr_nbytes >= 4 &&
467			    hbp[-1] == '\n' &&
468			    hbp[-2] == '\r' &&
469			    hbp[-3] == '\n' &&
470			    hbp[-4] == '\r' ) {
471				h->got_hdr = 1;
472				*hbp = 0;       /* null terminate */
473				break;
474			}
475		}
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);
479			goto bad;
480		}
481		if (h->max_bytes == 0) {
482			if (httpread_debug >= 10)
483				wpa_printf(MSG_DEBUG,
484					   "httpread no body hdr end(%p)", h);
485			goto got_file;
486		}
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)",
491					   h);
492			goto got_file;
493		}
494	}
495
496	/* Certain types of requests never have data and so
497	 * must be specially recognized.
498	 */
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)) {
503		if (!h->got_body) {
504			if (httpread_debug >= 10)
505				wpa_printf(MSG_DEBUG,
506					   "httpread NO BODY for sp. type");
507		}
508		h->got_body = 1;
509		goto got_file;
510	}
511
512	/* Data can be just plain binary data, or if "chunked"
513	 * consists of chunks each with a header, ending with
514	 * an ending header.
515	 */
516	if (!h->got_body) {
517		/* Here to get (more of) body */
518		/* ensure we have enough room for worst case for body
519		 * plus a null termination character
520		 */
521		if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) {
522			char *new_body;
523			int new_alloc_nbytes;
524
525			if (h->body_nbytes >= h->max_bytes)
526				goto bad;
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
531			 * we need.
532			 */
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))
537			    == NULL)
538				goto bad;
539
540			h->body = new_body;
541			h->body_alloc_nbytes = new_alloc_nbytes;
542		}
543		/* add bytes */
544		bbp = h->body + h->body_nbytes;
545		for (;;) {
546			int ncopy;
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' &&
552				    bbp[-1] == '\n') {
553					/* end of chunk hdr line */
554					/* hdr line consists solely
555					 * of a hex numeral and CFLF
556					 */
557					if (!isxdigit(*cbp))
558						goto bad;
559					h->chunk_size = strtoul(cbp, NULL, 16);
560					/* throw away chunk header
561					 * so we have only real data
562					 */
563					h->body_nbytes = h->chunk_start;
564					bbp = cbp;
565					if (h->chunk_size == 0) {
566						/* end of chunking */
567						/* trailer follows */
568						h->in_trailer = 1;
569						if (httpread_debug >= 20)
570							wpa_printf(
571								MSG_DEBUG,
572								"httpread end chunks(%p)", h);
573						break;
574					}
575					h->in_chunk_data = 1;
576					/* leave chunk_start alone */
577				}
578			} else if (h->chunked) {
579				/* in chunk data */
580				if ((h->body_nbytes - h->chunk_start) ==
581				    (h->chunk_size + 2)) {
582					/* end of chunk reached,
583					 * new chunk starts
584					 */
585					/* check chunk ended w/ CRLF
586					 * which we'll throw away
587					 */
588					if (bbp[-1] == '\n' &&
589					    bbp[-2] == '\r') {
590					} else
591						goto bad;
592					h->body_nbytes -= 2;
593					bbp -= 2;
594					h->chunk_start = h->body_nbytes;
595					h->in_chunk_data = 0;
596					h->chunk_size = 0; /* just in case */
597				}
598			} else if (h->got_content_length &&
599				   h->body_nbytes >= h->content_length) {
600				h->got_body = 1;
601				if (httpread_debug >= 10)
602					wpa_printf(
603						MSG_DEBUG,
604						"httpread got content(%p)", h);
605				goto got_file;
606			}
607			if (nread <= 0)
608				break;
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
613				 */
614				ncopy = (h->chunk_start + h->chunk_size + 2) -
615					h->body_nbytes;
616			} else if (h->chunked) {
617				/*in chunk header -- don't optimize */
618				*bbp++ = *rbp++;
619				nread--;
620				h->body_nbytes++;
621				continue;
622			} else if (h->got_content_length) {
623				ncopy = h->content_length - h->body_nbytes;
624			} else {
625				ncopy = nread;
626			}
627			/* Note: should never be 0 */
628			if (ncopy > nread)
629				ncopy = nread;
630			os_memcpy(bbp, rbp, ncopy);
631			bbp += ncopy;
632			h->body_nbytes += ncopy;
633			rbp += ncopy;
634			nread -= ncopy;
635		}       /* body copy loop */
636	}       /* !got_body */
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.
643		 */
644		bbp = h->body + h->body_nbytes;
645		for (;;) {
646			int c;
647			if (nread <= 0)
648				break;
649			c = *rbp++;
650			nread--;
651			switch (h->trailer_state) {
652			case trailer_line_begin:
653				if (c == '\r')
654					h->trailer_state = trailer_empty_cr;
655				else
656					h->trailer_state = trailer_nonempty;
657				break;
658			case trailer_empty_cr:
659				/* end empty line */
660				if (c == '\n') {
661					h->trailer_state = trailer_line_begin;
662					h->in_trailer = 0;
663					if (httpread_debug >= 10)
664						wpa_printf(
665							MSG_DEBUG,
666							"httpread got content(%p)", h);
667					h->got_body = 1;
668					goto got_file;
669				}
670				h->trailer_state = trailer_nonempty;
671				break;
672			case trailer_nonempty:
673				if (c == '\r')
674					h->trailer_state = trailer_nonempty_cr;
675				break;
676			case trailer_nonempty_cr:
677				if (c == '\n')
678					h->trailer_state = trailer_line_begin;
679				else
680					h->trailer_state = trailer_nonempty;
681				break;
682			}
683		}
684	}
685	goto get_more;
686
687bad:
688	/* Error */
689	wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h);
690	(*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR);
691	return;
692
693get_more:
694	return;
695
696got_file:
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 */
702	if (h->body)
703		h->body[h->body_nbytes] = 0; /* null terminate */
704	h->got_file = 1;
705	/* Assume that we do NOT support keeping connection alive,
706	 * and just in case somehow we don't get destroyed right away,
707	 * unregister now.
708	 */
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...
713	 * cancel timeout.
714	 */
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);
719}
720
721
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.
727 *
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.
731 *
732 * Pass max_bytes == 0 to not read body at all (required for e.g.
733 * reply to HEAD request).
734 */
735struct 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 */
742	)
743{
744	struct httpread *h = NULL;
745
746	h = os_zalloc(sizeof(*h));
747	if (h == NULL)
748		goto fail;
749	h->sd = sd;
750	h->cb = cb;
751	h->cookie = cookie;
752	h->max_bytes = max_bytes;
753	h->timeout_seconds = timeout_seconds;
754
755	if (timeout_seconds > 0) {
756		if (eloop_register_timeout(timeout_seconds, 0,
757					   httpread_timeout_handler,
758					   NULL, h)) {
759			/* No way to recover (from malloc failure) */
760			goto fail;
761		}
762		h->to_registered = 1;
763	}
764	if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler,
765				NULL, h)) {
766		/* No way to recover (from malloc failure) */
767		goto fail;
768	}
769	h->sd_registered = 1;
770	return h;
771
772fail:
773
774	/* Error */
775	httpread_destroy(h);
776	return NULL;
777}
778
779
780/* httpread_hdr_type_get -- When file is ready, returns header type. */
781enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h)
782{
783	return h->hdr_type;
784}
785
786
787/* httpread_uri_get -- When file is ready, uri_get returns (translated) URI
788 * or possibly NULL (which would be an error).
789 */
790char * httpread_uri_get(struct httpread *h)
791{
792	return h->uri;
793}
794
795
796/* httpread_reply_code_get -- When reply is ready, returns reply code */
797int httpread_reply_code_get(struct httpread *h)
798{
799	return h->reply_code;
800}
801
802
803/* httpread_length_get -- When file is ready, returns file length. */
804int httpread_length_get(struct httpread *h)
805{
806	return h->body_nbytes;
807}
808
809
810/* httpread_data_get -- When file is ready, returns file content
811 * with null byte appened.
812 * Might return NULL in some error condition.
813 */
814void * httpread_data_get(struct httpread *h)
815{
816	return h->body ? h->body : "";
817}
818
819
820/* httpread_hdr_get -- When file is ready, returns header content
821 * with null byte appended.
822 * Might return NULL in some error condition.
823 */
824char * httpread_hdr_get(struct httpread *h)
825{
826	return h->hdr;
827}
828
829
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).
833 *
834 * The tag should end with a colon for reliable matching.
835 *
836 * If not found, returns NULL;
837 */
838char * httpread_hdr_line_get(struct httpread *h, const char *tag)
839{
840	int tag_len = os_strlen(tag);
841	char *hdr = h->hdr;
842	hdr = os_strchr(hdr, '\n');
843	if (hdr == NULL)
844		return NULL;
845	hdr++;
846	for (;;) {
847		if (!os_strncasecmp(hdr, tag, tag_len)) {
848			hdr += tag_len;
849			while (*hdr == ' ' || *hdr == '\t')
850				hdr++;
851			return hdr;
852		}
853		hdr = os_strchr(hdr, '\n');
854		if (hdr == NULL)
855			return NULL;
856		hdr++;
857	}
858}
859