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