hostapd: Update vendor branch to 0.6.10
[dragonfly.git] / contrib / hostapd / src / wps / httpread.c
blob0d7165e9c227557f3d75d82b76a5258ec28e7b74
1 /**
2 * httpread - Manage reading file(s) from HTTP/TCP socket
3 * Author: Ted Merrill
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
11 * license.
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
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.
33 * Other limitations:
34 * -- HTTP header may not exceed a hard-coded size.
36 * Notes:
37 * This code would be massively simpler without some of the new features of
38 * HTTP/1.1, especially chunked data.
41 #include "includes.h"
43 #include "common.h"
44 #include "eloop.h"
45 #include "httpread.h"
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 */
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.
58 int httpread_debug = 99;
59 #else
60 #define httpread_debug 0 /* eliminates even the debugging code */
61 #endif
64 /* control instance -- actual definition (opaque to application)
66 struct 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 */
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 */
81 int hdr_nbytes;
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;
91 int got_body; /* nonzero when body is finalized */
92 char *body;
93 int body_nbytes;
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)*/
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;
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)
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;
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)
143 if (isdigit(c))
144 return c - '0';
145 if (islower(c))
146 return 10 + c - 'a';
147 return 10 + c - 'A';
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);
162 if (!h)
163 return;
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);
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(
194 struct httpread *h,
195 char *hbp /* pointer to current line in header buffer */
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;
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...
218 if (word_eq(hbp, "CHUNKED")) {
219 h->chunked = 1;
220 h->in_chunk_data = 0;
221 /* ignore possible ;<parameters> */
223 return 0;
225 /* skip anything we don't know, which is a lot */
226 return 0;
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;
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 {
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.
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++;
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;
325 /* skip rest of line */
326 while (*hbp)
327 if (*hbp++ == '\n')
328 break;
330 /* Remainder of lines are options, in any order;
331 * or empty line to terminate
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;
348 /* chunked overrides content-length always */
349 if (h->chunked)
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
360 * do not...
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... ?
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;
387 return 0;
389 bad:
390 /* Error */
391 return -1;
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;
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 */
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));
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.
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;
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;
437 /* No explicit length, hopefully we have all the data
438 * although dropped connections can cause false
439 * end
441 if (httpread_debug >= 10)
442 wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
443 h->got_body = 1;
444 goto got_file;
446 rbp = readbuf;
448 /* Header consists of text lines (terminated by both CR and LF)
449 * and an empty line (CR LF only).
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
458 for (;;) {
459 if (nread == 0)
460 goto get_more;
461 if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) {
462 goto bad;
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;
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;
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;
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)",
493 goto got_file;
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)) {
504 if (!h->got_body) {
505 if (httpread_debug >= 10)
506 wpa_printf(MSG_DEBUG,
507 "httpread NO BODY for sp. type");
509 h->got_body = 1;
510 goto got_file;
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.
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
524 if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) {
525 char *new_body;
526 int new_alloc_nbytes;
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.
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;
543 h->body = new_body;
544 h->body_alloc_nbytes = new_alloc_nbytes;
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
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
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;
578 h->in_chunk_data = 1;
579 /* leave chunk_start alone */
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
588 /* check chunk ended w/ CRLF
589 * which we'll throw away
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 */
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;
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
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;
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.
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;
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;
688 goto get_more;
690 bad:
691 /* Error */
692 wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h);
693 (*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR);
694 return;
696 get_more:
697 return;
699 got_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.
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.
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));
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;
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;
765 h->to_registered = 1;
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;
772 h->sd_registered = 1;
773 return h;
775 fail:
777 /* Error */
778 httpread_destroy(h);
779 return NULL;
783 /* httpread_hdr_type_get -- When file is ready, returns header type. */
784 enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h)
786 return h->hdr_type;
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)
795 return h->uri;
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)
829 return h->hdr;
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);
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;
856 hdr = os_strchr(hdr, '\n');
857 if (hdr == NULL)
858 return NULL;
859 hdr++;