PageRenderTime 55ms CodeModel.GetById 13ms app.highlight 35ms RepoModel.GetById 1ms app.codeStats 1ms

/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/http-message.c

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