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