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