http-parser.c

Small C library to parse HTTP requests
git clone git://git.finwo.net/lib/http-parser.c
Log | Files | Refs | README | LICENSE

http-parser.c (21635B)


      1 // vim:fdm=marker:fdl=0
      2 
      3 #ifdef __cplusplus
      4 extern "C" {
      5 #endif
      6 
      7 #include <stdlib.h>
      8 #include <stdio.h>
      9 #include <string.h>
     10 
     11 #include "finwo/asprintf.h"
     12 #include "finwo/strnstr.h"
     13 #include "tidwall/buf.h"
     14 
     15 #include "http-parser.h"
     16 #include "http-parser-statusses.h"
     17 
     18 const int _HTTP_PARSER_STATE_INIT         = 0;
     19 const int _HTTP_PARSER_STATE_HEADER       = 1;
     20 const int _HTTP_PARSER_STATE_BODY         = 2;
     21 const int _HTTP_PARSER_STATE_BODY_CHUNKED = 3;
     22 const int _HTTP_PARSER_STATE_DONE         = 4;
     23 const int _HTTP_PARSER_STATE_PANIC        = 666;
     24 
     25 #ifndef NULL
     26 #define NULL ((void*)0)
     27 #endif
     28 
     29 #if defined(_WIN32) || defined(_WIN64)
     30 #ifndef strcasecmp
     31 #define strcasecmp _stricmp
     32 #endif
     33 #endif
     34 
     35 // xtoi {{{
     36 /**
     37  * Convert hexidecimal string to int
     38  */
     39 int xtoi(char *str) {
     40   char *p = str;
     41   int i = 0;
     42   int sign = 1;
     43   while(*p) {
     44     if ( *p == '-' ) sign = -1;
     45 
     46     if ( *p >= '0' && *p <= '9' ) {
     47       i *= 16;
     48       i += (*p) - '0';
     49     }
     50 
     51     if ( *p >= 'a' && *p <= 'f' ) {
     52       i *= 16;
     53       i += 10 + (*p) - 'a';
     54     }
     55 
     56     if ( *p >= 'A' && *p <= 'F' ) {
     57       i *= 16;
     58       i += 10 + (*p) - 'A';
     59     }
     60 
     61     p = p+1;
     62   }
     63 
     64   return sign * i;
     65 }
     66 // }}}
     67 
     68 // non-exported structs {{{
     69 struct http_parser_meta {
     70   char *key;
     71   char *value;
     72 };
     73 // }}}
     74 
     75 // Meta management {{{
     76 
     77 static int fn_meta_cmp(const void *a, const void *b, void *udata) {
     78   const struct http_parser_meta *ta = (struct http_parser_meta *)a;
     79   const struct http_parser_meta *tb = (struct http_parser_meta *)b;
     80   return strcasecmp(ta->key, tb->key);
     81 }
     82 
     83 static void fn_meta_purge(void *item, void *udata) {
     84   struct http_parser_meta *subject = (struct http_parser_meta *)item;
     85   free(subject->key);
     86   free(subject->value);
     87   free(subject);
     88 }
     89 
     90 struct http_parser_meta * _http_parser_meta_get(struct http_parser_message *subject, const char *key) {
     91   struct http_parser_meta pattern = { .key = (char*)key };
     92   return mindex_get(subject->meta, &pattern);
     93 }
     94 const char * http_parser_meta_get(struct http_parser_message *subject, const char *key) {
     95   struct http_parser_meta *found = _http_parser_meta_get(subject, key);
     96   if (!found) return NULL;
     97   return found->value;
     98 }
     99 
    100 void _http_parser_meta_set(struct http_parser_message *subject, const char *key, const char *value) {
    101   struct http_parser_meta *meta = calloc(1, sizeof(struct http_parser_meta));
    102   meta->key   = strdup(key);
    103   meta->value = strdup(value);
    104   mindex_set(subject->meta, meta);
    105 }
    106 void http_parser_meta_set(struct http_parser_message *subject, const char *key, const char *value) {
    107   _http_parser_meta_set(subject, key, value);
    108 }
    109 
    110 void _http_parser_meta_del(struct http_parser_message *subject, const char *key) {
    111   struct http_parser_meta pattern = { .key = (char*)key };
    112   mindex_delete(subject->meta, &pattern);
    113 }
    114 void http_parser_meta_del(struct http_parser_message *subject, const char *key) {
    115   _http_parser_meta_del(subject, key);
    116 }
    117 // }}}
    118 
    119 // Header management {{{
    120 
    121 struct http_parser_meta * _http_parser_header_get(struct http_parser_message *subject, const char *key) {
    122   char *meta = calloc(strlen(key) + 8, sizeof(char));
    123   strcat(meta, "header:");
    124   strcat(meta, key);
    125   struct http_parser_meta *result = _http_parser_meta_get(subject, meta);
    126   free(meta);
    127   return result;
    128 }
    129 
    130 /**
    131  * Searches for the given key in the list of headers
    132  * Returns the header's value or NULL if not found
    133  */
    134 const char *http_parser_header_get(struct http_parser_message *subject, const char *key) {
    135   struct http_parser_meta *header = _http_parser_header_get(subject, key);
    136   if (!header) return NULL;
    137   return header->value;
    138 }
    139 
    140 void _http_parser_header_set(struct http_parser_message *subject, const char *key, const char *value) {
    141   char *meta = calloc(strlen(key) + 8, sizeof(char));
    142   strcat(meta, "header:");
    143   strcat(meta, key);
    144   _http_parser_meta_set(subject, meta, value);
    145   free(meta);
    146 }
    147 
    148 /**
    149  * Write a header into the subject's list of headers
    150  */
    151 void http_parser_header_set(struct http_parser_message *subject, const char *key, const char *value) {
    152   return _http_parser_header_set(subject, key, value);
    153 }
    154 
    155 void _http_parser_header_del(struct http_parser_message *subject, const char *key) {
    156   char *meta = calloc(strlen(key) + 8, sizeof(char));
    157   strcat(meta, "header:");
    158   strcat(meta, key);
    159   _http_parser_meta_del(subject, meta);
    160   free(meta);
    161 }
    162 
    163 void http_parser_header_del(struct http_parser_message *subject, const char *key) {
    164   _http_parser_header_del(subject, key);
    165 }
    166 
    167 // }}}
    168 
    169 /**
    170  * Frees everything in a http_message that was malloc'd by http-parser
    171  */
    172 void http_parser_message_free(struct http_parser_message *subject) {
    173   if (subject->method ) free(subject->method);
    174   if (subject->path   ) free(subject->path);
    175   if (subject->version) free(subject->version);
    176   if (subject->body   ) { buf_clear(subject->body); free(subject->body); }
    177   if (subject->meta   ) mindex_free(subject->meta);
    178   if (subject->buf    ) free(subject->buf);
    179   free(subject);
    180 }
    181 
    182 /**
    183  * Frees everything in a http pair that was malloc'd by http-parser
    184  */
    185 void http_parser_pair_free(struct http_parser_pair *pair) {
    186   if (pair->request) http_parser_message_free(pair->request);
    187   if (pair->response) http_parser_message_free(pair->response);
    188   free(pair);
    189 }
    190 
    191 /**
    192  * Initializes a http_message as request
    193  */
    194 struct http_parser_message * http_parser_request_init() {
    195   struct http_parser_message *message = calloc(1, sizeof(struct http_parser_message));
    196   message->chunksize = -1;
    197   message->meta      = mindex_init(
    198       fn_meta_cmp,
    199       fn_meta_purge,
    200       NULL
    201   );
    202   return message;
    203 }
    204 
    205 /**
    206  * Initializes a http_message as reponse
    207  */
    208 struct http_parser_message * http_parser_response_init() {
    209   struct http_parser_message *message = http_parser_request_init();
    210   message->status = 200;
    211   message->version = calloc(1,4);
    212   strcpy(message->version, "1.1");
    213   return message;
    214 }
    215 
    216 /**
    217  * Initialize a http_pair with userdata
    218  */
    219 struct http_parser_pair * http_parser_pair_init(void *udata) {
    220   struct http_parser_pair *pair = calloc(1, sizeof(struct http_parser_pair));
    221   pair->request  = http_parser_request_init();
    222   pair->response = http_parser_response_init();
    223   pair->udata    = udata;
    224   return pair;
    225 }
    226 
    227 
    228 /**
    229  * Removed N bytes from the beginning of the message body
    230  */
    231 static void http_parser_message_remove_body_bytes(struct http_parser_message *message, int bytes) {
    232   struct buf *newbuf = calloc(1, sizeof(struct buf));
    233   int size = message->body->len - bytes;
    234 
    235   if (size > 0) {
    236     buf_append(newbuf, message->body->data + bytes, size);
    237   }
    238 
    239   if (message->body) {
    240     buf_clear(message->body);
    241     free(message->body);
    242   }
    243   message->body = newbuf;
    244 }
    245 
    246 /**
    247  * Removes the first string from a http_message's body
    248  */
    249 static void http_parser_message_remove_body_string(struct http_parser_message *message) {
    250   int length = strlen(message->body->data);
    251   http_parser_message_remove_body_bytes(message, length + 2);
    252 }
    253 
    254 /**
    255  * Reads a header from a message's body and removes that lines from the body
    256  *
    257  * Caution: does not support multi-line headers yet
    258  */
    259 static int http_parser_message_read_header(struct http_parser_message *message) {
    260   char *index;
    261 
    262   // Require more data if no line break found
    263   index = strnstr(message->body->data, "\r\n", message->body->len);
    264   if (!index) return 1;
    265   *(index) = '\0';
    266 
    267   // Detect end of headers
    268   if (!strlen(message->body->data)) { // Using strlen, due to possible \r\n replacement
    269     http_parser_message_remove_body_string(message);
    270     return 0;
    271   }
    272 
    273   // Detect colon
    274   index = strstr(message->body->data, ": ");
    275   if (!index) {
    276     http_parser_message_remove_body_string(message);
    277     return 1;
    278   }
    279 
    280   // Split by the found colon & skip leading whitespace
    281   *(index) = '\0';
    282   index++;
    283   while(*(index) == ' ') index++;
    284 
    285   // Insert the header in our map
    286   _http_parser_header_set(message, message->body->data, index);
    287 
    288   // Remove the header remainder
    289   // Twice, because we split the string
    290   http_parser_message_remove_body_string(message);
    291   http_parser_message_remove_body_string(message);
    292   return 2;
    293 }
    294 
    295 /**
    296  * Reads chunked data
    297  */
    298 static int http_parser_message_read_chunked(struct http_parser_message *message) {
    299   char *aChunkSize;
    300   char *index;
    301   struct http_parser_event *ev;
    302 
    303   // Attempt reading the chunk size
    304   if (message->chunksize == -1) {
    305 
    306     // Check if we have a line
    307     index = strnstr(message->body->data, "\r\n", message->body->len);
    308     if (!index) {
    309       return 1;
    310     }
    311     *(index) = '\0';
    312 
    313     // Empty line = skip
    314     if (!strlen(message->body->data)) {
    315       http_parser_message_remove_body_string(message);
    316       return 2;
    317     }
    318 
    319     // Read hex chunksize
    320     aChunkSize = calloc(1, 17);
    321     sscanf(message->body->data, "%16s", aChunkSize);
    322     message->chunksize = xtoi(aChunkSize);
    323     free(aChunkSize);
    324 
    325     // Remove chunksize line
    326     http_parser_message_remove_body_string(message);
    327 
    328     // 0 = EOF
    329     if (message->chunksize == 0) {
    330       return 0;
    331     }
    332 
    333     // Signal we're still reading
    334     return 2;
    335   }
    336 
    337   // Create buffer if not present yet
    338   if (!message->buf) {
    339     message->buf = calloc(1,sizeof(struct buf));
    340   }
    341 
    342   // Ensure the body has enough data
    343   if (message->body->len < message->chunksize) {
    344     return 1;
    345   }
    346 
    347   // Either call onChunk method OR copy into message buffer
    348   if (message->onChunk) {
    349     // Call onChunk if the message has that set
    350     ev            = calloc(1,sizeof(struct http_parser_event));
    351     ev->udata     = message->udata;
    352     ev->chunk     = &((struct buf){
    353       .len  = message->chunksize,
    354       .cap  = message->chunksize,
    355       .data = message->body->data,
    356     });
    357     message->onChunk(ev);
    358     free(ev);
    359   } else {
    360     buf_append(message->buf, message->body->data, message->chunksize);
    361   }
    362 
    363   // Remove chunk from receiving data and reset chunking
    364   http_parser_message_remove_body_bytes(message, message->chunksize);
    365   message->chunksize = -1;
    366 
    367   // No error or end encountered
    368   return 2;
    369 }
    370 
    371 const char * http_parser_status_message(int status) {
    372   int i;
    373   for(i=0; http_parser_statusses[i].status; i++) {
    374     if (http_parser_statusses[i].status == status) {
    375       return http_parser_statusses[i].message;
    376     }
    377   }
    378   return NULL;
    379 }
    380 
    381 struct buf * http_parser_sprint_pair_response(struct http_parser_pair *pair) {
    382   return http_parser_sprint_response(pair->response);
    383 }
    384 
    385 struct buf * http_parser_sprint_pair_request(struct http_parser_pair *pair) {
    386   return http_parser_sprint_request(pair->request);
    387 }
    388 
    389 // Caution: headers should fit in 64k
    390 struct buf * http_parser_sprint_response(struct http_parser_message *response) {
    391   struct buf *result = calloc(1, sizeof(struct buf));
    392   result->cap  = 65536;
    393   result->data = calloc(1, result->cap);
    394 
    395   // Status
    396   sprintf(result->data,
    397       "HTTP/%s %d %s\r\n"
    398       , response->version
    399       , response->status
    400       , response->statusMessage ? response->statusMessage : http_parser_status_message(response->status)
    401   );
    402 
    403   // Headers
    404   int i;
    405   struct http_parser_meta *meta;
    406   if (response->meta) {
    407     for(i=0; i<mindex_length(response->meta); i++) {
    408       meta = mindex_nth(response->meta, i);
    409       if (strncmp("header:", meta->key, 7)) continue;
    410       strcat(result->data, (meta->key + 7));
    411       strcat(result->data, ": ");
    412       strcat(result->data, meta->value);
    413       strcat(result->data, "\r\n");
    414     }
    415   }
    416 
    417   strcat(result->data, "\r\n");
    418 
    419   // Treat result as buffer from here
    420   result->len = strlen(result->data);
    421 
    422   if (response->body) {
    423     buf_append(result, response->body->data, response->body->len);
    424   }
    425 
    426   return result;
    427 }
    428 
    429 // Caution: headers should fit in 64k
    430 struct buf * http_parser_sprint_request(struct http_parser_message *request) {
    431   struct buf *result = calloc(1, sizeof(struct buf));
    432   result->cap  = 65536;
    433   result->data = calloc(1, result->cap);
    434 
    435   char *tmppath;
    436   char *path = request->path;
    437   int isPathAllocced = 0;
    438   if (!path) path = "/";
    439   if (strstr(path, "/") != path) {
    440     tmppath        = path;
    441     path           = NULL;
    442     isPathAllocced = true;
    443     asprintf(&path, "/%s", tmppath);
    444   }
    445 
    446   // Status
    447   sprintf(result->data,
    448       "%s %s"
    449       , request->method
    450       , path
    451   );
    452 
    453   // Query
    454   if (request->query) {
    455     strcat(result->data, "?");
    456     strcat(result->data, request->query);
    457   }
    458 
    459   // HTTP version
    460   strcat(result->data, " HTTP/");
    461   strcat(result->data, request->version);
    462   strcat(result->data, "\r\n");
    463 
    464   // Headers
    465   int i;
    466   struct http_parser_meta *meta;
    467   if (request->meta) {
    468     for(i=0; i<mindex_length(request->meta); i++) {
    469       meta = mindex_nth(request->meta, i);
    470       if (strncmp("header:", meta->key, 7)) continue;
    471       strcat(result->data, (meta->key + 7));
    472       strcat(result->data, ": ");
    473       strcat(result->data, meta->value);
    474       strcat(result->data, "\r\n");
    475     }
    476   }
    477 
    478   strcat(result->data, "\r\n");
    479 
    480   // Handle chunked header
    481   const char *aTransferEncoding = http_parser_header_get(request, "transfer-encoding");
    482   int isChunked = 0;
    483   if (aTransferEncoding && strcasecmp(aTransferEncoding, "chunked")) {
    484     isChunked = 1;
    485     sprintf(result->data + strlen(result->data), "%lx\r\n", request->body->len);
    486   }
    487 
    488   // Treat result as buffer from here
    489   result->len = strlen(result->data);
    490 
    491   if (request->body) {
    492     buf_append(result, request->body->data, request->body->len);
    493   }
    494 
    495   if (isChunked) {
    496     buf_append(result, "0\r\n\r\n", 5);
    497   }
    498 
    499   if (isPathAllocced) {
    500     free(path);
    501   }
    502 
    503   return result;
    504 }
    505 
    506 /**
    507  * Pass data into the pair's request
    508  *
    509  * Triggers onRequest if set
    510  */
    511 void http_parser_pair_request_data(struct http_parser_pair *pair, const struct buf *data) {
    512   struct http_parser_event *ev;
    513   http_parser_request_data(pair->request, data);
    514   if (pair->request->ready && pair->onRequest) {
    515     ev           = calloc(1,sizeof(struct http_parser_event));
    516     ev->request  = pair->request;
    517     ev->response = pair->response;
    518     ev->pair     = pair;
    519     ev->udata    = pair->udata;
    520     pair->onRequest(ev);
    521     free(ev);
    522     pair->onRequest = NULL;
    523   }
    524 }
    525 
    526 /**
    527  * Pass data into the pair's request
    528  *
    529  * Triggers onResponse if set
    530  */
    531 void http_parser_pair_response_data(struct http_parser_pair *pair, const struct buf *data) {
    532   struct http_parser_event *ev;
    533   http_parser_response_data(pair->response, data);
    534   if (pair->response->ready && pair->onResponse) {
    535     ev           = calloc(1,sizeof(struct http_parser_event));
    536     ev->request  = pair->request;
    537     ev->response = pair->response;
    538     ev->pair     = pair;
    539     ev->udata    = pair->udata;
    540     pair->onResponse(ev);
    541     free(ev);
    542     pair->onResponse = NULL;
    543   }
    544 }
    545 
    546 /**
    547  * Insert data into a http_message, acting as if it's a request
    548  */
    549 void http_parser_request_data(struct http_parser_message *request, const struct buf *data) {
    550   char *index;
    551   const char *aContentLength;
    552   int iContentLength;
    553   const char *aChunkSize;
    554   int res;
    555 
    556   // Add event data to buffer
    557   if (!request->body) request->body = calloc(1, sizeof(struct buf));
    558   buf_append(request->body, data->data, data->len);
    559 
    560   while(1) {
    561     switch(request->_state) {
    562       case _HTTP_PARSER_STATE_PANIC:
    563         return;
    564       case _HTTP_PARSER_STATE_INIT:
    565 
    566         // Wait for more data if not line break found
    567         index = strstr(request->body->data, "\r\n");
    568         if (!index) return;
    569         *(index) = '\0';
    570 
    571         // Read method and path
    572         request->method  = calloc(1, 16);
    573         request->path    = calloc(1, 8192);
    574         request->version = calloc(1, 4);
    575         if (sscanf(request->body->data, "%15s %8191s HTTP/%3s", request->method, request->path, request->version) != 3) {
    576           request->_state = _HTTP_PARSER_STATE_PANIC;
    577           return;
    578         }
    579 
    580         // Remove method line
    581         http_parser_message_remove_body_string(request);
    582 
    583         // Detect query
    584         // No need to malloc, already done by sscanf
    585         index = strstr(request->path, "?");
    586         if (index) {
    587           *(index) = '\0';
    588           request->query = index + 1;
    589         }
    590 
    591         // Signal we're now reading headers
    592         request->_state = _HTTP_PARSER_STATE_HEADER;
    593         break;
    594 
    595       case _HTTP_PARSER_STATE_HEADER:
    596         if (!http_parser_message_read_header(request)) {
    597           if (
    598               http_parser_header_get(request, "content-length") ||
    599               http_parser_header_get(request, "transfer-encoding")
    600           ) {
    601             request->_state = _HTTP_PARSER_STATE_BODY;
    602           } else {
    603             request->_state = _HTTP_PARSER_STATE_DONE;
    604           }
    605         }
    606         break;
    607 
    608       case _HTTP_PARSER_STATE_BODY:
    609 
    610         // Detect chunked encoding
    611         if (request->chunksize == -1) {
    612           aChunkSize = http_parser_header_get(request, "transfer-encoding");
    613           if (aChunkSize) {
    614             if (!strcasecmp(aChunkSize, "chunked")) {
    615               request->_state = _HTTP_PARSER_STATE_BODY_CHUNKED;
    616               break;
    617             }
    618           }
    619         }
    620 
    621         // Fetch the content length
    622         aContentLength = http_parser_header_get(request, "content-length");
    623         if (!aContentLength) {
    624           request->_state = _HTTP_PARSER_STATE_DONE;
    625           break;
    626         }
    627         iContentLength = atoi(aContentLength);
    628 
    629         // Not enough data = skip
    630         if (request->body->len < iContentLength) {
    631           return;
    632         }
    633 
    634         // Change size to indicated size
    635         request->_state = _HTTP_PARSER_STATE_DONE;
    636         break;
    637 
    638       case _HTTP_PARSER_STATE_BODY_CHUNKED:
    639         res = http_parser_message_read_chunked(request);
    640 
    641         if (res == 0) {
    642           // Done
    643           request->_state = _HTTP_PARSER_STATE_DONE;
    644         } else if (res == 1) {
    645           // More data needed
    646           return;
    647         } else if (res == 2) {
    648           // Still reading
    649         }
    650 
    651         break;
    652 
    653       case _HTTP_PARSER_STATE_DONE:
    654 
    655         // Temporary buffer > direct buffer
    656         if (request->buf) {
    657           if (request->body) {
    658             buf_clear(request->body);
    659             free(request->body);
    660           }
    661           request->body = request->buf;
    662           request->buf  = NULL;
    663         }
    664 
    665         // Mark the request as ready
    666         request->ready  = 1;
    667         return;
    668     }
    669   }
    670 }
    671 
    672 /**
    673  * Insert data into a http_message, acting as if it's a request
    674  */
    675 void http_parser_response_data(struct http_parser_message *response, const struct buf *data) {
    676   char *index;
    677   char *aStatus;
    678   int iContentLength;
    679   const char *aContentLength;
    680   const char *aChunkSize;
    681   int res;
    682 
    683   // Add event data to buffer
    684   if (!response->body) response->body = calloc(1, sizeof(struct buf));
    685   buf_append(response->body, data->data, data->len);
    686 
    687   while(1) {
    688     switch(response->_state) {
    689       case _HTTP_PARSER_STATE_PANIC:
    690         return;
    691       case _HTTP_PARSER_STATE_INIT:
    692         // Wait for more data if not line break found
    693         index = strstr(response->body->data, "\r\n");
    694         if (!index) return;
    695         *(index) = '\0';
    696 
    697         // Read version and status
    698         response->version       = calloc(1, 8);
    699         response->statusMessage = calloc(1, 64);
    700         aStatus                 = calloc(1, 8);
    701         if (sscanf(response->body->data, "HTTP/%7s %7s %63[^\r\n]", response->version, aStatus, response->statusMessage) != 3) {
    702           response->_state = _HTTP_PARSER_STATE_PANIC;
    703           return;
    704         }
    705 
    706         // Turn the text status into a number
    707         response->status = atoi(aStatus);
    708         free(aStatus);
    709 
    710         // Remove status line
    711         http_parser_message_remove_body_string(response);
    712 
    713         // Signal we're now reading headers
    714         response->_state = _HTTP_PARSER_STATE_HEADER;
    715         break;
    716 
    717       case _HTTP_PARSER_STATE_HEADER:
    718         if (!http_parser_message_read_header(response)) {
    719           if (
    720               http_parser_header_get(response, "content-length") ||
    721               http_parser_header_get(response, "transfer-encoding")
    722           ) {
    723             response->_state = _HTTP_PARSER_STATE_BODY;
    724           } else {
    725             response->_state = _HTTP_PARSER_STATE_DONE;
    726           }
    727         }
    728         break;
    729 
    730       case _HTTP_PARSER_STATE_BODY:
    731         // Detect chunked encoding
    732         if (response->chunksize == -1) {
    733           aChunkSize = http_parser_header_get(response, "transfer-encoding");
    734           if (aChunkSize) {
    735             if (!strcasecmp(aChunkSize, "chunked")) {
    736               response->_state = _HTTP_PARSER_STATE_BODY_CHUNKED;
    737               break;
    738             }
    739           }
    740         }
    741 
    742         // Fetch the content length
    743         aContentLength = http_parser_header_get(response, "content-length");
    744         if (!aContentLength) {
    745           response->_state = _HTTP_PARSER_STATE_DONE;
    746           break;
    747         }
    748         iContentLength = atoi(aContentLength);
    749 
    750         // Not enough data = skip
    751         if (response->body->len < iContentLength) {
    752           return;
    753         }
    754 
    755         // Change size to indicated size
    756         response->_state = _HTTP_PARSER_STATE_DONE;
    757         break;
    758 
    759       case _HTTP_PARSER_STATE_BODY_CHUNKED:
    760         res = http_parser_message_read_chunked(response);
    761 
    762         if (res == 0) {
    763           // Done
    764           response->_state = _HTTP_PARSER_STATE_DONE;
    765         } else if (res == 1) {
    766           // More data needed
    767           return;
    768         } else if (res == 2) {
    769           // Still reading
    770         }
    771 
    772         break;
    773 
    774       case _HTTP_PARSER_STATE_DONE:
    775         // Temporary buffer > direct buffer
    776         if (response->buf) {
    777           if (response->body) {
    778             buf_clear(response->body);
    779             free(response->body);
    780           }
    781           response->body = response->buf;
    782           response->buf  = NULL;
    783         }
    784 
    785         // Mark the request as ready
    786         response->ready = 1;
    787         return;
    788     }
    789   }
    790 }
    791 
    792 #ifdef __cplusplus
    793 } // extern "C"
    794 #endif