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

Annotation of /trunk/http-message.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 177 - (hide annotations)
Sat Sep 20 23:30:26 2008 UTC (10 years, 6 months ago) by siliconforks
File MIME type: text/plain
File size: 21579 byte(s)
Respect the charset parameter in the HTTP Content-Type header.
1 siliconforks 116 /*
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 siliconforks 177 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 siliconforks 116 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 siliconforks 177 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 siliconforks 116 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