/[jscoverage]/trunk/http-message.c
ViewVC logotype

Contents of /trunk/http-message.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 177 - (show annotations)
Sat Sep 20 23:30:26 2008 UTC (11 years ago) by siliconforks
File MIME type: text/plain
File size: 21579 byte(s)
Respect the charset parameter in the HTTP Content-Type header.
1 /*
2 http-message.c - HTTP message object
3 Copyright (C) 2008 siliconforks.com
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <config.h>
21
22 #include "http-server.h"
23
24 #include <assert.h>
25 #include <errno.h>
26 #include <string.h>
27
28 #include "stream.h"
29 #include "util.h"
30
31 enum ChunkedBodyState {
32 CHUNKED_BODY_CHUNK_SIZE, CHUNKED_BODY_CHUNK_DATA, CHUNKED_BODY_TRAILER, CHUNKED_BODY_DONE
33 };
34
35 struct HTTPMessage {
36 char * start_line;
37 HTTPHeader * headers;
38
39 /* used for sending and receiving */
40 struct HTTPConnection * connection;
41
42 /* used only for receiving */
43 bool has_content_length;
44 bool is_chunked;
45 size_t bytes_remaining;
46 enum ChunkedBodyState chunked_body_state;
47 Stream * chunk_buffer;
48
49 /* used only for sending */
50 bool is_started;
51 };
52
53 static bool is_lws(uint8_t c) {
54 return c == '\r' || c == '\n' || c == ' ' || c == '\t';
55 }
56
57 static bool is_separator(uint8_t c) {
58 /* RFC 2616 2.2 */
59 return strchr("()<>@,;:\\\"/[]?={} \t", c) != NULL;
60 }
61
62 static bool is_token_char(uint8_t c) {
63 /* RFC 2616 2.2 */
64 return 32 <= c && c <= 126 && ! is_separator(c);
65 }
66
67 static bool is_text(uint8_t c) {
68 return ! (c <= 31 || c == 127);
69 }
70
71 static void skip_lws(const uint8_t ** p) {
72 while (**p != '\0' && is_lws(**p)) {
73 (*p)++;
74 }
75 }
76
77 static uint8_t * parse_token(const uint8_t ** p) {
78 const uint8_t * start = *p;
79 while (**p != '\0' && is_token_char(**p)) {
80 (*p)++;
81 }
82
83 if (*p == start) {
84 return NULL;
85 }
86
87 return (uint8_t *) xstrndup((char *) start, *p - start);
88 }
89
90 static uint8_t * parse_quoted_string(const uint8_t ** p) {
91 const uint8_t * start = *p;
92
93 if (**p != '"') {
94 return NULL;
95 }
96 (*p)++;
97
98 while (**p != '\0' && **p != '"') {
99 if (**p == '\\') {
100 (*p)++;
101 if (**p < 1 || **p > 127) {
102 return NULL;
103 }
104 (*p)++;
105 }
106 else if (is_text(**p)) {
107 (*p)++;
108 }
109 else {
110 return NULL;
111 }
112 }
113
114 if (**p != '"') {
115 return NULL;
116 }
117 (*p)++;
118
119 return (uint8_t *) xstrndup((char *) start, *p - start);
120 }
121
122 HTTPMessage * HTTPMessage_new(HTTPConnection * connection) {
123 HTTPMessage * message = xmalloc(sizeof(HTTPMessage));
124 message->start_line = NULL;
125 message->headers = NULL;
126 message->connection = connection;
127
128 message->has_content_length = false;
129 message->is_chunked = false;
130 message->bytes_remaining = 0;
131 message->chunked_body_state = CHUNKED_BODY_CHUNK_SIZE;
132 message->chunk_buffer = NULL;
133
134 message->is_started = false;
135 return message;
136 }
137
138 void HTTPMessage_delete(HTTPMessage * message) {
139 free(message->start_line);
140
141 HTTPHeader * h = message->headers;
142 while (h != NULL) {
143 HTTPHeader * doomed = h;
144 h = h->next;
145 free(doomed->name);
146 free(doomed->value);
147 free(doomed);
148 }
149
150 if (message->chunk_buffer != NULL) {
151 Stream_delete(message->chunk_buffer);
152 }
153
154 free(message);
155 }
156
157 HTTPConnection * HTTPMessage_get_connection(const HTTPMessage * message) {
158 return message->connection;
159 }
160
161 void HTTPMessage_add_header(HTTPMessage * message, const char * name, const char * value) {
162 HTTPHeader * last = NULL;
163 for (HTTPHeader * h = message->headers; h != NULL; h = h->next) {
164 if (strcmp(h->name, name) == 0) {
165 char * new_value;
166 xasprintf(&new_value, "%s, %s", h->value, value);
167 free(h->value);
168 h->value = new_value;
169 return;
170 }
171 last = h;
172 }
173
174 HTTPHeader * header = xmalloc(sizeof(HTTPHeader));
175 header->name = xstrdup(name);
176 header->value = xstrdup(value);
177 header->next = NULL;
178 if (last == NULL) {
179 message->headers = header;
180 }
181 else {
182 last->next = header;
183 }
184 }
185
186 void HTTPMessage_set_header(HTTPMessage * message, const char * name, const char * value) {
187 for (HTTPHeader * h = message->headers; h != NULL; h = h->next) {
188 if (strcmp(name, h->name) == 0) {
189 free(h->value);
190 h->value = xstrdup(value);
191 return;
192 }
193 }
194 HTTPMessage_add_header(message, name, value);
195 }
196
197 char * HTTPMessage_get_charset(const HTTPMessage * message) {
198 const char * content_type = HTTPMessage_find_header(message, HTTP_CONTENT_TYPE);
199 if (content_type == NULL) {
200 return NULL;
201 }
202
203 const uint8_t * p = (const uint8_t *) content_type;
204
205 /* e.g., text/html */
206 uint8_t * token;
207 skip_lws(&p);
208 if (! is_token_char(*p)) {
209 return NULL;
210 }
211 token = parse_token(&p);
212 free(token);
213 skip_lws(&p);
214 if (*p != '/') {
215 return NULL;
216 }
217 p++;
218 skip_lws(&p);
219 if (! is_token_char(*p)) {
220 return NULL;
221 }
222 token = parse_token(&p);
223 free(token);
224
225 skip_lws(&p);
226
227 while (*p != '\0') {
228 bool is_charset = false;
229 if (*p != ';') {
230 return NULL;
231 }
232 p++;
233
234 skip_lws(&p);
235
236 if (! is_token_char(*p)) {
237 return NULL;
238 }
239 uint8_t * attribute = parse_token(&p);
240 if (strcasecmp((char *) attribute, "charset") == 0) {
241 is_charset = true;
242 }
243 free(attribute);
244 skip_lws(&p);
245 if (*p != '=') {
246 return NULL;
247 }
248 p++;
249
250 skip_lws(&p);
251
252 if (*p == '"') {
253 uint8_t * value = parse_quoted_string(&p);
254 if (value == NULL) {
255 return NULL;
256 }
257 if (is_charset) {
258 return (char *) value;
259 }
260 free(value);
261 }
262 else if (is_token_char(*p)) {
263 uint8_t * value = parse_token(&p);
264 if (is_charset) {
265 return (char *) value;
266 }
267 free(value);
268 }
269 else {
270 return NULL;
271 }
272
273 skip_lws(&p);
274 }
275
276 return NULL;
277 }
278
279 void HTTPMessage_set_content_length(HTTPMessage * message, size_t value) {
280 char * s;
281 xasprintf(&s, "%u", value);
282 HTTPMessage_set_header(message, HTTP_CONTENT_LENGTH, s);
283 free(s);
284 }
285
286 const char * HTTPMessage_find_header(const HTTPMessage * message, const char * name) {
287 for (HTTPHeader * h = message->headers; h != NULL; h = h->next) {
288 if (strcasecmp(h->name, name) == 0) {
289 return h->value;
290 }
291 }
292 return NULL;
293 }
294
295 const HTTPHeader * HTTPMessage_get_headers(const HTTPMessage * message) {
296 return message->headers;
297 }
298
299 const char * HTTPMessage_get_start_line(const HTTPMessage * message) {
300 return message->start_line;
301 }
302
303 void HTTPMessage_set_start_line(HTTPMessage * message, const char * start_line) {
304 free(message->start_line);
305 message->start_line = xstrdup(start_line);
306 }
307
308 static int read_line(Stream * stream, HTTPConnection * connection) __attribute__((warn_unused_result));
309
310 static int read_line(Stream * stream, HTTPConnection * connection) {
311 for (;;) {
312 int octet;
313 int result = HTTPConnection_read_octet(connection, &octet);
314 if (result != 0) {
315 return result;
316 }
317
318 /* check for end of input */
319 if (octet == -1) {
320 return 0;
321 }
322
323 char c = (char) octet;
324 Stream_write_char(stream, c);
325 if (c == '\n') {
326 return 0;
327 }
328 }
329 }
330
331 static int read_header(Stream * stream, HTTPConnection * connection) __attribute__((warn_unused_result));
332
333 static int read_header(Stream * stream, HTTPConnection * connection) {
334 int c;
335
336 do {
337 int result = read_line(stream, connection);
338 if (result != 0) {
339 return result;
340 }
341
342 /* check for blank line ending the headers */
343 if (stream->length == 0 ||
344 (stream->length == 1 && stream->data[0] == '\n') ||
345 (stream->length == 2 && stream->data[0] == '\r' && stream->data[1] == '\n')) {
346 break;
347 }
348
349 result = HTTPConnection_peek_octet(connection, &c);
350 if (result != 0) {
351 return result;
352 }
353 }
354 while (c == ' ' || c == '\t');
355
356 return 0;
357 }
358
359 static bool stream_contains_nul(const Stream * stream) {
360 for (size_t i = 0; i < stream->length; i++) {
361 if (stream->data[i] == '\0') {
362 return true;
363 }
364 }
365 return false;
366 }
367
368 int HTTPMessage_read_start_line_and_headers(HTTPMessage * message) {
369 Stream * stream = Stream_new(0);
370
371 /* read the start line */
372 int result = read_line(stream, message->connection);
373 if (result != 0) {
374 Stream_delete(stream);
375 return -1;
376 }
377
378 /* check for NUL byte */
379 if (stream_contains_nul(stream)) {
380 Stream_delete(stream);
381 return -1;
382 }
383
384 message->start_line = xstrndup((char *) stream->data, stream->length);
385
386 /* read the headers - RFC 2616 4.2 */
387 message->headers = NULL;
388 for (;;) {
389 Stream_reset(stream);
390 result = read_header(stream, message->connection);
391 if (result != 0) {
392 Stream_delete(stream);
393 return -1;
394 }
395
396 /* check for CRLF (or similar) to terminate headers */
397 if (stream->length == 0 ||
398 (stream->length == 1 && stream->data[0] == '\n') ||
399 (stream->length == 2 && stream->data[0] == '\r' && stream->data[1] == '\n')) {
400 break;
401 }
402
403 /* check for NUL byte */
404 if (stream_contains_nul(stream)) {
405 Stream_delete(stream);
406 return -1;
407 }
408
409 /* NUL-terminate the header */
410 Stream_write_char(stream, '\0');
411
412 const uint8_t * p = stream->data;
413
414 char * name = (char *) parse_token(&p);
415 if (name == NULL) {
416 Stream_delete(stream);
417 return -1;
418 }
419
420 skip_lws(&p);
421
422 /* expect colon */
423 if (*p != ':') {
424 free(name);
425 Stream_delete(stream);
426 return -1;
427 }
428
429 /* skip over colon */
430 p++;
431
432 skip_lws(&p);
433
434 if (*p == '\0') {
435 free(name);
436 Stream_delete(stream);
437 return -1;
438 }
439
440 /* skip backward over LWS, starting from the last char in the buffer */
441 uint8_t * end = stream->data + stream->length - 2;
442 while (end > p && is_lws(*end)) {
443 end--;
444 }
445
446 char * value = xstrndup((char *) p, end - p + 1);
447
448 HTTPMessage_add_header(message, name, value);
449 free(name);
450 free(value);
451 }
452
453 Stream_delete(stream);
454
455 /*
456 RFC 2616 4.3:
457 - a request has a body iff the request headers include Content-Length or Transfer-Encoding
458 - a response has a body iff the request is not HEAD and the response is not 1xx, 204, 304
459 */
460
461 const char * content_length = HTTPMessage_find_header(message, HTTP_CONTENT_LENGTH);
462 if (content_length != NULL) {
463 size_t value = 0;
464 for (const char * p = content_length; *p != '\0'; p++) {
465 /* check for overflow */
466 if (SIZE_MAX / 10 < value) {
467 return -1;
468 }
469 value *= 10;
470
471 uint8_t digit = *p;
472
473 /* check that it contains only decimal digits */
474 if (digit < '0' || digit > '9') {
475 return -1;
476 }
477
478 size_t digit_value = digit - '0';
479
480 /* check for overflow */
481 if (SIZE_MAX - digit_value < value) {
482 return -1;
483 }
484 value += digit_value;
485 }
486
487 message->bytes_remaining = value;
488 message->has_content_length = true;
489 }
490
491 const char * transfer_encoding = HTTPMessage_find_header(message, HTTP_TRANSFER_ENCODING);
492 if (transfer_encoding != NULL) {
493 uint8_t * token = NULL;
494
495 const uint8_t * p = (const uint8_t *) transfer_encoding;
496 result = 0;
497 for (;;) {
498 skip_lws(&p);
499
500 if (! is_token_char(*p)) {
501 result = -1;
502 break;
503 }
504
505 free(token);
506 token = parse_token(&p);
507
508 skip_lws(&p);
509
510 while (*p == ';') {
511 p++;
512
513 skip_lws(&p);
514
515 if (! is_token_char(*p)) {
516 result = -1;
517 break;
518 }
519 uint8_t * attribute = parse_token(&p);
520 free(attribute);
521
522 skip_lws(&p);
523
524 if (*p != '=') {
525 result = -1;
526 break;
527 }
528 p++;
529
530 skip_lws(&p);
531
532 if (*p == '"') {
533 uint8_t * value = parse_quoted_string(&p);
534 if (value == NULL) {
535 result = -1;
536 break;
537 }
538 free(value);
539 }
540 else if (is_token_char(*p)) {
541 uint8_t * value = parse_token(&p);
542 free(value);
543 }
544 else {
545 result = -1;
546 break;
547 }
548
549 skip_lws(&p);
550 }
551
552 if (result == -1) {
553 break;
554 }
555
556 if (*p != ',') {
557 break;
558 }
559
560 p++;
561 }
562
563 if (result == 0 && *p == '\0' && token != NULL && strcasecmp((char *) token, "chunked") == 0) {
564 message->is_chunked = true;
565 message->chunk_buffer = Stream_new(0);
566 }
567
568 free(token);
569 }
570
571 return result;
572 }
573
574 int HTTPMessage_write_start_line_and_headers(HTTPMessage * message) {
575 int result = 0;
576
577 if (message->is_started) {
578 return result;
579 }
580
581 message->is_started = true;
582
583 /* send the start line */
584 assert(message->start_line != NULL);
585 size_t length = strlen(message->start_line);
586 assert(length >= 2 && message->start_line[length - 2] == '\r' && message->start_line[length - 1] == '\n');
587 result = HTTPConnection_write(message->connection, message->start_line, length);
588 if (result != 0) {
589 return result;
590 }
591
592 /* send the headers */
593 HTTPMessage_set_header(message, HTTP_CONNECTION, "close");
594 for (HTTPHeader * h = message->headers; h != NULL; h = h->next) {
595 result = HTTPConnection_write(message->connection, h->name, strlen(h->name));
596 if (result != 0) {
597 return result;
598 }
599 result = HTTPConnection_write(message->connection, ": ", 2);
600 if (result != 0) {
601 return result;
602 }
603 result = HTTPConnection_write(message->connection, h->value, strlen(h->value));
604 if (result != 0) {
605 return result;
606 }
607 result = HTTPConnection_write(message->connection, "\r\n", 2);
608 if (result != 0) {
609 return result;
610 }
611 }
612
613 result = HTTPConnection_write(message->connection, "\r\n", 2);
614 return result;
615 }
616
617 bool HTTPMessage_has_sent_headers(const HTTPMessage * message) {
618 return message->is_started;
619 }
620
621 int HTTPMessage_write(HTTPMessage * message, const void * p, size_t size) {
622 int result = 0;
623 result = HTTPMessage_write_start_line_and_headers(message);
624 if (result != 0) {
625 return result;
626 }
627 result = HTTPConnection_write(message->connection, p, size);
628 return result;
629 }
630
631 int HTTPMessage_flush(HTTPMessage * message) {
632 int result = 0;
633 result = HTTPMessage_write_start_line_and_headers(message);
634 if (result != 0) {
635 return result;
636 }
637 result = HTTPConnection_flush(message->connection);
638 return result;
639 }
640
641 static int read_chunk_size_line(HTTPMessage * message) __attribute__((warn_unused_result));
642
643 static int read_chunk_size_line(HTTPMessage * message) {
644 Stream_reset(message->chunk_buffer);
645 int result = read_line(message->chunk_buffer, message->connection);
646 if (result != 0) {
647 return result;
648 }
649 if (message->chunk_buffer->length < 2) {
650 return -1;
651 }
652 return 0;
653 }
654
655 static int read_chunk_size(Stream * stream, size_t * chunk_size) __attribute__((warn_unused_result));
656
657 static int read_chunk_size(Stream * stream, size_t * chunk_size) {
658 size_t value = 0;
659 for (size_t i = 0; i < stream->length; i++) {
660 uint8_t digit = stream->data[i];
661
662 /* check that it contains only hexadecimal digits */
663 size_t digit_value;
664 if ('0' <= digit && digit <= '9') {
665 digit_value = digit - '0';
666 }
667 else if ('a' <= digit && digit <= 'f') {
668 digit_value = digit - 'a' + 10;
669 }
670 else if ('A' <= digit && digit <= 'F') {
671 digit_value = digit - 'A' + 10;
672 }
673 else if (is_lws(digit) || digit == ';') {
674 break;
675 }
676 else {
677 return -1;
678 }
679
680 /* check for overflow */
681 if (SIZE_MAX / 16 < value) {
682 return -1;
683 }
684 value *= 16;
685
686 /* check for overflow */
687 if (SIZE_MAX - digit_value < value) {
688 return -1;
689 }
690 value += digit_value;
691 }
692
693 *chunk_size = value;
694 return 0;
695 }
696
697 static int read_chunked_message_body(HTTPMessage * message, void * p, size_t capacity, size_t * bytes_read) {
698 int result = 0;
699 *bytes_read = 0;
700
701 if (message->chunked_body_state == CHUNKED_BODY_DONE) {
702 return 0;
703 }
704
705 uint8_t * s = p;
706 int c;
707 for (*bytes_read = 0; *bytes_read < capacity; (*bytes_read)++) {
708 switch (message->chunked_body_state) {
709 case CHUNKED_BODY_CHUNK_SIZE:
710 if (message->chunk_buffer->length == 0) {
711 /* read a `chunk-size' line */
712 result = read_chunk_size_line(message);
713 if (result != 0) {
714 return result;
715 }
716
717 message->bytes_remaining = message->chunk_buffer->length;
718 }
719
720 if (message->bytes_remaining == 0) {
721 return -1;
722 }
723
724 /* serve from the chunk buffer */
725 s[*bytes_read] = message->chunk_buffer->data[message->chunk_buffer->length - message->bytes_remaining];
726 message->bytes_remaining--;
727
728 if (message->bytes_remaining == 0) {
729 size_t chunk_size;
730 result = read_chunk_size(message->chunk_buffer, &chunk_size);
731 if (result != 0) {
732 return result;
733 }
734 Stream_reset(message->chunk_buffer);
735 if (chunk_size == 0) {
736 message->chunked_body_state = CHUNKED_BODY_TRAILER;
737 }
738 else if (SIZE_MAX - 2 < chunk_size) {
739 /* overflow */
740 return -1;
741 }
742 else {
743 message->chunked_body_state = CHUNKED_BODY_CHUNK_DATA;
744 message->bytes_remaining = chunk_size + 2;
745 }
746 }
747
748 break;
749 case CHUNKED_BODY_CHUNK_DATA:
750 /* serve from the chunk */
751 result = HTTPConnection_read_octet(message->connection, &c);
752 if (result != 0) {
753 return result;
754 }
755 if (c == -1) {
756 result = -1;
757 message->chunked_body_state = CHUNKED_BODY_DONE;
758 return result;
759 }
760 s[*bytes_read] = (uint8_t) c;
761 message->bytes_remaining--;
762
763 if (message->bytes_remaining == 0) {
764 message->chunked_body_state = CHUNKED_BODY_CHUNK_SIZE;
765 }
766
767 break;
768 case CHUNKED_BODY_TRAILER:
769 if (message->chunk_buffer->length == 0) {
770 /* read a header */
771 result = read_header(message->chunk_buffer, message->connection);
772 if (result != 0) {
773 return result;
774 }
775 message->bytes_remaining = message->chunk_buffer->length;
776 }
777
778 if (message->bytes_remaining == 0) {
779 message->chunked_body_state = CHUNKED_BODY_DONE;
780 return result;
781 }
782
783 /* serve from the chunk buffer */
784 s[*bytes_read] = message->chunk_buffer->data[message->chunk_buffer->length - message->bytes_remaining];
785 message->bytes_remaining--;
786
787 if (message->bytes_remaining == 0) {
788 size_t length = message->chunk_buffer->length;
789 uint8_t * chunk_buffer = message->chunk_buffer->data;
790 if (length == 0 ||
791 (length == 1 && chunk_buffer[0] == '\n') ||
792 (length == 2 && chunk_buffer[0] == '\r' && chunk_buffer[1] == '\n')) {
793 message->chunked_body_state = CHUNKED_BODY_DONE;
794 return result;
795 }
796 Stream_reset(message->chunk_buffer);
797 }
798
799 break;
800 default:
801 break;
802 }
803 }
804
805 return result;
806 }
807
808 static int read_chunked_entity_body(HTTPMessage * message, void * p, size_t capacity, size_t * bytes_read) {
809 int result = 0;
810 *bytes_read = 0;
811
812 if (message->chunked_body_state == CHUNKED_BODY_DONE) {
813 return result;
814 }
815
816 uint8_t * s = p;
817 for (*bytes_read = 0; *bytes_read < capacity; (*bytes_read)++) {
818 if (message->bytes_remaining == 0) {
819 result = read_chunk_size_line(message);
820 if (result != 0) {
821 break;
822 }
823 size_t chunk_size;
824 result = read_chunk_size(message->chunk_buffer, &chunk_size);
825 if (result != 0) {
826 break;
827 }
828 message->bytes_remaining = chunk_size;
829 if (chunk_size == 0) {
830 message->chunked_body_state = CHUNKED_BODY_DONE;
831 break;
832 }
833 }
834
835 int c;
836 result = HTTPConnection_read_octet(message->connection, &c);
837 if (result != 0) {
838 break;
839 }
840 if (c == -1) {
841 result = -1;
842 message->chunked_body_state = CHUNKED_BODY_DONE;
843 break;
844 }
845 s[*bytes_read] = (uint8_t) c;
846 message->bytes_remaining--;
847 }
848
849 return result;
850 }
851
852 int HTTPMessage_read_message_body(HTTPMessage * message, void * p, size_t capacity, size_t * bytes_read) {
853 if (message->is_chunked) {
854 return read_chunked_message_body(message, p, capacity, bytes_read);
855 }
856
857 int result = 0;
858 uint8_t * s = p;
859 for (*bytes_read = 0; *bytes_read < capacity; (*bytes_read)++) {
860 if (message->has_content_length && message->bytes_remaining == 0) {
861 break;
862 }
863
864 int c;
865 result = HTTPConnection_read_octet(message->connection, &c);
866 if (result != 0) {
867 break;
868 }
869 if (c == -1) {
870 break;
871 }
872 s[*bytes_read] = (uint8_t) c;
873 message->bytes_remaining--;
874 }
875 return result;
876 }
877
878 int HTTPMessage_read_entity_body(HTTPMessage * message, void * p, size_t capacity, size_t * bytes_read) {
879 if (message->is_chunked) {
880 return read_chunked_entity_body(message, p, capacity, bytes_read);
881 }
882
883 return HTTPMessage_read_message_body(message, p, capacity, bytes_read);
884 }
885
886 int HTTPMessage_read_entire_entity_body(HTTPMessage * message, Stream * input_stream) {
887 int result = 0;
888 uint8_t * buffer[8192];
889 for (;;) {
890 size_t bytes_read;
891 result = HTTPMessage_read_entity_body(message, buffer, 8192, &bytes_read);
892 if (result != 0) {
893 break;
894 }
895 if (bytes_read == 0) {
896 break;
897 }
898 Stream_write(input_stream, buffer, bytes_read);
899 }
900 return result;
901 }

  ViewVC Help
Powered by ViewVC 1.1.24