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