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