LCOV - code coverage report
Current view: top level - ext/http - php_http_message_parser.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 259 308 84.1 %
Date: 2015-02-17 20:30:22 Functions: 14 15 93.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :     +--------------------------------------------------------------------+
       3             :     | PECL :: http                                                       |
       4             :     +--------------------------------------------------------------------+
       5             :     | Redistribution and use in source and binary forms, with or without |
       6             :     | modification, are permitted provided that the conditions mentioned |
       7             :     | in the accompanying LICENSE file are met.                          |
       8             :     +--------------------------------------------------------------------+
       9             :     | Copyright (c) 2004-2014, Michael Wallner <mike@php.net>            |
      10             :     +--------------------------------------------------------------------+
      11             : */
      12             : 
      13             : #include "php_http_api.h"
      14             : 
      15             : #ifndef DBG_PARSER
      16             : #       define DBG_PARSER 0
      17             : #endif
      18             : 
      19             : typedef struct php_http_message_parser_state_spec {
      20             :         php_http_message_parser_state_t state;
      21             :         unsigned need_data:1;
      22             : } php_http_message_parser_state_spec_t;
      23             : 
      24             : static const php_http_message_parser_state_spec_t php_http_message_parser_states[] = {
      25             :                 {PHP_HTTP_MESSAGE_PARSER_STATE_START,                   1},
      26             :                 {PHP_HTTP_MESSAGE_PARSER_STATE_HEADER,                  0},
      27             :                 {PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE,             0},
      28             :                 {PHP_HTTP_MESSAGE_PARSER_STATE_BODY,                    0},
      29             :                 {PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB,               1},
      30             :                 {PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH,             1},
      31             :                 {PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED,    1},
      32             :                 {PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE,               0},
      33             :                 {PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL,               0},
      34             :                 {PHP_HTTP_MESSAGE_PARSER_STATE_DONE,                    0}
      35             : };
      36             : 
      37             : #if DBG_PARSER
      38             : const char *php_http_message_parser_state_name(php_http_message_parser_state_t state) {
      39             :         const char *states[] = {"START", "HEADER", "HEADER_DONE", "BODY", "BODY_DUMB", "BODY_LENGTH", "BODY_CHUNK", "BODY_DONE", "UPDATE_CL", "DONE"};
      40             :         
      41             :         if (state < 0 || state > (sizeof(states)/sizeof(char*))-1) {
      42             :                 return "FAILURE";
      43             :         }
      44             :         return states[state];
      45             : }
      46             : #endif
      47             : 
      48          65 : php_http_message_parser_t *php_http_message_parser_init(php_http_message_parser_t *parser TSRMLS_DC)
      49             : {
      50          65 :         if (!parser) {
      51          16 :                 parser = emalloc(sizeof(*parser));
      52             :         }
      53          65 :         memset(parser, 0, sizeof(*parser));
      54             : 
      55             :         TSRMLS_SET_CTX(parser->ts);
      56             : 
      57          65 :         php_http_header_parser_init(&parser->header TSRMLS_CC);
      58             : 
      59          65 :         return parser;
      60             : }
      61             : 
      62       33193 : php_http_message_parser_state_t php_http_message_parser_state_push(php_http_message_parser_t *parser, unsigned argc, ...)
      63             : {
      64             :         php_http_message_parser_state_t state;
      65             :         va_list va_args;
      66             :         unsigned i;
      67             : 
      68             :         /* short circuit */
      69       33193 :         ZEND_PTR_STACK_RESIZE_IF_NEEDED((&parser->stack), argc);
      70             : 
      71       33193 :         va_start(va_args, argc);
      72       99235 :         for (i = 0; i < argc; ++i) {
      73       66042 :                 state  = va_arg(va_args, php_http_message_parser_state_t);
      74       66042 :                 zend_ptr_stack_push(&parser->stack, (void *) state);
      75             :         }
      76       33193 :         va_end(va_args);
      77             : 
      78       33193 :         return state;
      79             : }
      80             : 
      81       65910 : php_http_message_parser_state_t php_http_message_parser_state_is(php_http_message_parser_t *parser)
      82             : {
      83       65910 :         if (parser->stack.top) {
      84       65834 :                 return (php_http_message_parser_state_t) parser->stack.elements[parser->stack.top - 1];
      85             :         }
      86          76 :         return PHP_HTTP_MESSAGE_PARSER_STATE_START;
      87             : }
      88             : 
      89       66095 : php_http_message_parser_state_t php_http_message_parser_state_pop(php_http_message_parser_t *parser)
      90             : {
      91       66095 :         if (parser->stack.top) {
      92       66020 :                 return (php_http_message_parser_state_t) zend_ptr_stack_pop(&parser->stack);
      93             :         }
      94          75 :         return PHP_HTTP_MESSAGE_PARSER_STATE_START;
      95             : }
      96             : 
      97          65 : void php_http_message_parser_dtor(php_http_message_parser_t *parser)
      98             : {
      99          65 :         php_http_header_parser_dtor(&parser->header);
     100          65 :         zend_ptr_stack_destroy(&parser->stack);
     101          65 :         php_http_message_free(&parser->message);
     102          65 :         if (parser->dechunk) {
     103           4 :                 php_http_encoding_stream_free(&parser->dechunk);
     104             :         }
     105          65 :         if (parser->inflate) {
     106           2 :                 php_http_encoding_stream_free(&parser->inflate);
     107             :         }
     108          65 : }
     109             : 
     110          16 : void php_http_message_parser_free(php_http_message_parser_t **parser)
     111             : {
     112          16 :         if (*parser) {
     113          16 :                 php_http_message_parser_dtor(*parser);
     114          16 :                 efree(*parser);
     115          16 :                 *parser = NULL;
     116             :         }
     117          16 : }
     118             : 
     119          24 : php_http_message_parser_state_t php_http_message_parser_parse_stream(php_http_message_parser_t *parser, php_http_buffer_t *buf, php_stream *s, unsigned flags, php_http_message_t **message)
     120             : {
     121          24 :         php_http_message_parser_state_t state = PHP_HTTP_MESSAGE_PARSER_STATE_START;
     122             :         TSRMLS_FETCH_FROM_CTX(parser->ts);
     123             : 
     124          24 :         if (!buf->data) {
     125           7 :                 php_http_buffer_resize_ex(buf, 0x1000, 1, 0);
     126             :         }
     127             :         while (1) {
     128       32934 :                 size_t justread = 0;
     129             : #if DBG_PARSER
     130             :                 fprintf(stderr, "#SP: %s (f:%u)\n", php_http_message_parser_state_name(state), flags);
     131             : #endif
     132             :                 /* resize if needed */
     133       32934 :                 if (buf->free < 0x1000) {
     134           0 :                         php_http_buffer_resize_ex(buf, 0x1000, 1, 0);
     135             :                 }
     136       32934 :                 switch (state) {
     137             :                         case PHP_HTTP_MESSAGE_PARSER_STATE_START:
     138             :                         case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER:
     139             :                         case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE:
     140             :                                 /* read line */
     141         141 :                                 php_stream_get_line(s, buf->data + buf->used, buf->free, &justread);
     142             :                                 /* if we fail reading a whole line, try a single char */
     143         141 :                                 if (!justread) {
     144          15 :                                         int c = php_stream_getc(s);
     145             : 
     146          15 :                                         if (c != EOF) {
     147           0 :                                                 char s[1] = {c};
     148           0 :                                                 justread = php_http_buffer_append(buf, s, 1);
     149             :                                         }
     150             :                                 }
     151         141 :                                 php_http_buffer_account(buf, justread);
     152         141 :                                 break;
     153             : 
     154             :                         case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB:
     155             :                                 /* read all */
     156           0 :                                 justread = php_stream_read(s, buf->data + buf->used, buf->free);
     157           0 :                                 php_http_buffer_account(buf, justread);
     158           0 :                                 break;
     159             : 
     160             :                         case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH:
     161             :                                 /* read body_length */
     162       32774 :                                 justread = php_stream_read(s, buf->data + buf->used, MIN(buf->free, parser->body_length));
     163       32774 :                                 php_http_buffer_account(buf, justread);
     164       32774 :                                 break;
     165             : 
     166             :                         case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED:
     167             :                                 /* duh, this is very naive */
     168          11 :                                 if (parser->body_length) {
     169           3 :                                         justread = php_stream_read(s, buf->data + buf->used, MIN(parser->body_length, buf->free));
     170             : 
     171           3 :                                         php_http_buffer_account(buf, justread);
     172             : 
     173           3 :                                         parser->body_length -= justread;
     174             :                                 } else {
     175           8 :                                         php_http_buffer_resize(buf, 24);
     176           8 :                                         php_stream_get_line(s, buf->data, buf->free, &justread);
     177           8 :                                         php_http_buffer_account(buf, justread);
     178             : 
     179           8 :                                         parser->body_length = strtoul(buf->data + buf->used - justread, NULL, 16);
     180             :                                 }
     181          11 :                                 break;
     182             : 
     183             :                         case PHP_HTTP_MESSAGE_PARSER_STATE_BODY:
     184             :                         case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:
     185             :                         case PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL:
     186             :                                 /* should not occur */
     187           0 :                                 abort();
     188             :                                 break;
     189             : 
     190             :                         case PHP_HTTP_MESSAGE_PARSER_STATE_DONE:
     191             :                         case PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE:
     192          32 :                                 return php_http_message_parser_state_is(parser);
     193             :                 }
     194             : 
     195       32926 :                 if (justread) {
     196       32910 :                         state = php_http_message_parser_parse(parser, buf, flags, message);
     197          16 :                 } else if (php_stream_eof(s)) {
     198           7 :                         return php_http_message_parser_parse(parser, buf, flags | PHP_HTTP_MESSAGE_PARSER_CLEANUP, message);
     199             :                 } else {
     200           9 :                         return state;
     201             :                 }
     202       32910 :         }
     203             : 
     204             :         return PHP_HTTP_MESSAGE_PARSER_STATE_DONE;
     205             : }
     206             : 
     207             : 
     208       33051 : php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_parser_t *parser, php_http_buffer_t *buffer, unsigned flags, php_http_message_t **message)
     209             : {
     210       33051 :         char *str = NULL;
     211       33051 :         size_t len = 0;
     212       33051 :         size_t cut = 0;
     213             :         TSRMLS_FETCH_FROM_CTX(parser->ts);
     214             : 
     215      132045 :         while (buffer->used || !php_http_message_parser_states[php_http_message_parser_state_is(parser)].need_data) {
     216             : #if DBG_PARSER
     217             :                 fprintf(stderr, "#MP: %s (f: %u, t:%d, l:%zu)\n", 
     218             :                         php_http_message_parser_state_name(php_http_message_parser_state_is(parser)),
     219             :                         flags, 
     220             :                         message && *message ? (*message)->type : -1, 
     221             :                         buffer->used
     222             :                 );
     223             :                 _dpf(0, buffer->data, buffer->used);
     224             : #endif
     225             : 
     226       66095 :                 switch (php_http_message_parser_state_pop(parser))
     227             :                 {
     228             :                         case PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE:
     229           0 :                                 return php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE);
     230             : 
     231             :                         case PHP_HTTP_MESSAGE_PARSER_STATE_START:
     232             :                         {
     233          75 :                                 char *ptr = buffer->data;
     234             : 
     235         160 :                                 while (ptr - buffer->data < buffer->used && PHP_HTTP_IS_CTYPE(space, *ptr)) {
     236          10 :                                         ++ptr;
     237             :                                 }
     238             : 
     239          75 :                                 php_http_buffer_cut(buffer, 0, ptr - buffer->data);
     240             : 
     241          75 :                                 if (buffer->used) {
     242          69 :                                         php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER);
     243             :                                 }
     244          75 :                                 break;
     245             :                         }
     246             : 
     247             :                         case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER:
     248             :                         {
     249         199 :                                 unsigned header_parser_flags = (flags & PHP_HTTP_MESSAGE_PARSER_CLEANUP) ? PHP_HTTP_HEADER_PARSER_CLEANUP : 0;
     250             : 
     251         199 :                                 switch (php_http_header_parser_parse(&parser->header, buffer, header_parser_flags, *message ? &(*message)->hdrs : NULL, (php_http_info_callback_t) php_http_message_info_callback, message)) {
     252             :                                         case PHP_HTTP_HEADER_PARSER_STATE_FAILURE:
     253           6 :                                                 return PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE;
     254             : 
     255             :                                         case PHP_HTTP_HEADER_PARSER_STATE_DONE:
     256          38 :                                                 php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE);
     257          38 :                                                 break;
     258             : 
     259             :                                         default:
     260         155 :                                                 if (buffer->used || !(flags & PHP_HTTP_MESSAGE_PARSER_CLEANUP)) {
     261         130 :                                                         return php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER);
     262             :                                                 } else {
     263          25 :                                                         php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE);
     264             :                                                 }
     265             :                                 }
     266          63 :                                 break;
     267             :                         }
     268             : 
     269             :                         case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE:
     270             :                         {
     271          63 :                                 zval *h, *h_loc = NULL, *h_con = NULL, **h_cl = NULL, **h_cr = NULL, **h_te = NULL;
     272             : 
     273             :                                 /* Content-Range has higher precedence than Content-Length,
     274             :                                  * and content-length denotes the original length of the entity,
     275             :                                  * so let's *NOT* remove CR/CL, because that would fundamentally
     276             :                                  * change the meaning of the whole message
     277             :                                  */
     278          63 :                                 if ((h = php_http_message_header(*message, ZEND_STRL("Transfer-Encoding"), 1))) {
     279           4 :                                         zend_hash_update(&(*message)->hdrs, "X-Original-Transfer-Encoding", sizeof("X-Original-Transfer-Encoding"), (void *) &h, sizeof(zval *), (void *) &h_te);
     280           4 :                                         zend_hash_del(&(*message)->hdrs, "Transfer-Encoding", sizeof("Transfer-Encoding"));
     281             : 
     282             :                                         /* reset */
     283           4 :                                         MAKE_STD_ZVAL(h);
     284           4 :                                         ZVAL_LONG(h, 0);
     285           4 :                                         zend_hash_update(&(*message)->hdrs, "Content-Length", sizeof("Content-Length"), (void *) &h, sizeof(zval *), NULL);
     286          59 :                                 } else if ((h = php_http_message_header(*message, ZEND_STRL("Content-Length"), 1))) {
     287          12 :                                         zend_hash_update(&(*message)->hdrs, "X-Original-Content-Length", sizeof("X-Original-Content-Length"), (void *) &h, sizeof(zval *), (void *) &h_cl);
     288             :                                 }
     289             : 
     290          63 :                                 if ((h = php_http_message_header(*message, ZEND_STRL("Content-Range"), 1))) {
     291           3 :                                         zend_hash_find(&(*message)->hdrs, ZEND_STRS("Content-Range"), (void *) &h_cr);
     292           3 :                                         if (h != *h_cr) {
     293           0 :                                                 zend_hash_update(&(*message)->hdrs, "Content-Range", sizeof("Content-Range"), &h, sizeof(zval *), (void *) &h_cr);
     294             :                                         } else {
     295           3 :                                                 zval_ptr_dtor(&h);
     296             :                                         }
     297             :                                 }
     298             : 
     299             :                                 /* so, if curl sees a 3xx code, a Location header and a Connection:close header
     300             :                                  * it decides not to read the response body.
     301             :                                  */
     302          63 :                                 if ((flags & PHP_HTTP_MESSAGE_PARSER_EMPTY_REDIRECTS)
     303           0 :                                 &&      (*message)->type == PHP_HTTP_RESPONSE
     304           0 :                                 &&      (*message)->http.info.response.code/100 == 3
     305           0 :                                 &&      (h_loc = php_http_message_header(*message, ZEND_STRL("Location"), 1))
     306           0 :                                 &&      (h_con = php_http_message_header(*message, ZEND_STRL("Connection"), 1))
     307             :                                 ) {
     308           0 :                                         if (php_http_match(Z_STRVAL_P(h_con), "close", PHP_HTTP_MATCH_WORD)) {
     309           0 :                                                 php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_DONE);
     310           0 :                                                 zval_ptr_dtor(&h_loc);
     311           0 :                                                 zval_ptr_dtor(&h_con);
     312           0 :                                                 break;
     313             :                                         }
     314             :                                 }
     315          63 :                                 if (h_loc) {
     316           0 :                                         zval_ptr_dtor(&h_loc);
     317             :                                 }
     318          63 :                                 if (h_con) {
     319           0 :                                         zval_ptr_dtor(&h_con);
     320             :                                 }
     321             : 
     322          63 :                                 if ((h = php_http_message_header(*message, ZEND_STRL("Content-Encoding"), 1))) {
     323           2 :                                         if (php_http_match(Z_STRVAL_P(h), "gzip", PHP_HTTP_MATCH_WORD)
     324           0 :                                         ||      php_http_match(Z_STRVAL_P(h), "x-gzip", PHP_HTTP_MATCH_WORD)
     325           0 :                                         ||      php_http_match(Z_STRVAL_P(h), "deflate", PHP_HTTP_MATCH_WORD)
     326             :                                         ) {
     327           2 :                                                 if (parser->inflate) {
     328           0 :                                                         php_http_encoding_stream_reset(&parser->inflate);
     329             :                                                 } else {
     330           2 :                                                         parser->inflate = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_inflate_ops(), 0 TSRMLS_CC);
     331             :                                                 }
     332           2 :                                                 zend_hash_update(&(*message)->hdrs, "X-Original-Content-Encoding", sizeof("X-Original-Content-Encoding"), &h, sizeof(zval *), NULL);
     333           2 :                                                 zend_hash_del(&(*message)->hdrs, "Content-Encoding", sizeof("Content-Encoding"));
     334             :                                         } else {
     335           0 :                                                 zval_ptr_dtor(&h);
     336             :                                         }
     337             :                                 }
     338             : 
     339          63 :                                 if ((flags & PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES)) {
     340           0 :                                         php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB);
     341             :                                 } else {
     342          63 :                                         if (h_te) {
     343           4 :                                                 if (strstr(Z_STRVAL_PP(h_te), "chunked")) {
     344           4 :                                                         parser->dechunk = php_http_encoding_stream_init(parser->dechunk, php_http_encoding_stream_get_dechunk_ops(), 0 TSRMLS_CC);
     345           4 :                                                         php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED);
     346           4 :                                                         break;
     347             :                                                 }
     348             :                                         }
     349             : 
     350          59 :                                         if (h_cr) {
     351           3 :                                                 ulong total = 0, start = 0, end = 0;
     352             : 
     353           3 :                                                 if (!strncasecmp(Z_STRVAL_PP(h_cr), "bytes", lenof("bytes"))
     354           3 :                                                 && (    Z_STRVAL_PP(h_cr)[lenof("bytes")] == ':'
     355           3 :                                                         ||      Z_STRVAL_PP(h_cr)[lenof("bytes")] == ' '
     356           0 :                                                         ||      Z_STRVAL_PP(h_cr)[lenof("bytes")] == '='
     357             :                                                         )
     358             :                                                 ) {
     359           3 :                                                         char *total_at = NULL, *end_at = NULL;
     360           3 :                                                         char *start_at = Z_STRVAL_PP(h_cr) + sizeof("bytes");
     361             : 
     362           3 :                                                         start = strtoul(start_at, &end_at, 10);
     363           3 :                                                         if (end_at) {
     364           3 :                                                                 end = strtoul(end_at + 1, &total_at, 10);
     365           3 :                                                                 if (total_at && strncmp(total_at + 1, "*", 1)) {
     366           3 :                                                                         total = strtoul(total_at + 1, NULL, 10);
     367             :                                                                 }
     368             : 
     369           3 :                                                                 if (end >= start && (!total || end <= total)) {
     370           3 :                                                                         parser->body_length = end + 1 - start;
     371           3 :                                                                         php_http_message_parser_state_push(parser, 1, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH);
     372           3 :                                                                         break;
     373             :                                                                 }
     374             :                                                         }
     375             :                                                 }
     376             :                                         }
     377             : 
     378          56 :                                         if (h_cl) {
     379             :                                                 char *stop;
     380             : 
     381           9 :                                                 if (Z_TYPE_PP(h_cl) == IS_STRING) {
     382           9 :                                                         parser->body_length = strtoul(Z_STRVAL_PP(h_cl), &stop, 10);
     383             : 
     384           9 :                                                         if (stop != Z_STRVAL_PP(h_cl)) {
     385           9 :                                                                 php_http_message_parser_state_push(parser, 1, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH);
     386           9 :                                                                 break;
     387             :                                                         }
     388           0 :                                                 } else if (Z_TYPE_PP(h_cl) == IS_LONG) {
     389           0 :                                                         parser->body_length = Z_LVAL_PP(h_cl);
     390           0 :                                                         php_http_message_parser_state_push(parser, 1, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH);
     391           0 :                                                         break;
     392             :                                                 }
     393             :                                         }
     394             : 
     395          47 :                                         if ((*message)->type == PHP_HTTP_REQUEST) {
     396          22 :                                                 php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_DONE);
     397             :                                         } else {
     398          25 :                                                 php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB);
     399             :                                         }
     400             :                                 }
     401          47 :                                 break;
     402             :                         }
     403             : 
     404             :                         case PHP_HTTP_MESSAGE_PARSER_STATE_BODY:
     405             :                         {
     406       32849 :                                 if (len) {
     407             :                                         /* FIXME: what if we re-use the parser? */
     408       32840 :                                         if (parser->inflate) {
     409           2 :                                                 char *dec_str = NULL;
     410             :                                                 size_t dec_len;
     411             : 
     412           2 :                                                 if (SUCCESS != php_http_encoding_stream_update(parser->inflate, str, len, &dec_str, &dec_len)) {
     413           0 :                                                         return php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE);
     414             :                                                 }
     415             : 
     416           2 :                                                 if (str != buffer->data) {
     417           0 :                                                         PTR_FREE(str);
     418             :                                                 }
     419           2 :                                                 str = dec_str;
     420           2 :                                                 len = dec_len;
     421             :                                         }
     422             : 
     423       32840 :                                         php_stream_write(php_http_message_body_stream((*message)->body), str, len);
     424             : 
     425             :                                 }
     426             : 
     427       32849 :                                 if (cut) {
     428       32849 :                                         php_http_buffer_cut(buffer, 0, cut);
     429             :                                 }
     430             : 
     431       32849 :                                 if (str != buffer->data) {
     432          15 :                                         PTR_FREE(str);
     433             :                                 }
     434             : 
     435       32849 :                                 str = NULL;
     436       32849 :                                 len = 0;
     437       32849 :                                 cut = 0;
     438       32849 :                                 break;
     439             :                         }
     440             : 
     441             :                         case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB:
     442             :                         {
     443           3 :                                 str = buffer->data;
     444           3 :                                 len = buffer->used;
     445           3 :                                 cut = len;
     446             : 
     447           3 :                                 php_http_message_parser_state_push(parser, 2, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
     448           3 :                                 break;
     449             :                         }
     450             : 
     451             :                         case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH:
     452             :                         {
     453       32833 :                                 len = MIN(parser->body_length, buffer->used);
     454       32833 :                                 str = buffer->data;
     455       32833 :                                 cut = len;
     456             : 
     457       32833 :                                 parser->body_length -= len;
     458             : 
     459       32833 :                                 php_http_message_parser_state_push(parser, 2, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
     460       32833 :                                 break;
     461             :                         }
     462             : 
     463             :                         case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED:
     464             :                         {
     465             :                                 /*
     466             :                                  * - pass available data through the dechunk stream
     467             :                                  * - pass decoded data along
     468             :                                  * - if stream zeroed:
     469             :                                  *      Y:      - cut processed string out of buffer, but leave length of unprocessed dechunk stream data untouched
     470             :                                  *              - body done
     471             :                                  *      N:      - parse ahaed
     472             :                                  */
     473          13 :                                 char *dec_str = NULL;
     474             :                                 size_t dec_len;
     475             : 
     476          13 :                                 if (SUCCESS != php_http_encoding_stream_update(parser->dechunk, buffer->data, buffer->used, &dec_str, &dec_len)) {
     477           0 :                                         return PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE;
     478             :                                 }
     479             : 
     480          13 :                                 str = dec_str;
     481          13 :                                 len = dec_len;
     482             : 
     483          13 :                                 if (php_http_encoding_stream_done(parser->dechunk)) {
     484           4 :                                         cut = buffer->used - PHP_HTTP_BUFFER(parser->dechunk->ctx)->used;
     485           4 :                                         php_http_message_parser_state_push(parser, 2, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
     486             :                                 } else {
     487           9 :                                         cut = buffer->used;
     488           9 :                                         php_http_message_parser_state_push(parser, 2, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
     489             :                                 }
     490          13 :                                 break;
     491             :                         }
     492             : 
     493             :                         case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:
     494             :                         {
     495          19 :                                 php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_DONE);
     496             : 
     497          19 :                                 if (parser->dechunk && parser->dechunk->ctx) {
     498           4 :                                         char *dec_str = NULL;
     499             :                                         size_t dec_len;
     500             : 
     501           4 :                                         if (SUCCESS != php_http_encoding_stream_finish(parser->dechunk, &dec_str, &dec_len)) {
     502           0 :                                                 return php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE);
     503             :                                         }
     504           4 :                                         php_http_encoding_stream_dtor(parser->dechunk);
     505             : 
     506           4 :                                         if (dec_str && dec_len) {
     507           0 :                                                 str = dec_str;
     508           0 :                                                 len = dec_len;
     509           0 :                                                 cut = 0;
     510           0 :                                                 php_http_message_parser_state_push(parser, 2, PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
     511             :                                         }
     512             :                                 }
     513             : 
     514          19 :                                 break;
     515             :                         }
     516             : 
     517             :                         case PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL:
     518             :                         {
     519             :                                 zval *zcl;
     520           0 :                                 MAKE_STD_ZVAL(zcl);
     521           0 :                                 ZVAL_LONG(zcl, php_http_message_body_size((*message)->body));
     522           0 :                                 zend_hash_update(&(*message)->hdrs, "Content-Length", sizeof("Content-Length"), &zcl, sizeof(zval *), NULL);
     523           0 :                                 break;
     524             :                         }
     525             : 
     526             :                         case PHP_HTTP_MESSAGE_PARSER_STATE_DONE:
     527             :                         {
     528          41 :                                 char *ptr = buffer->data;
     529             : 
     530          84 :                                 while (ptr - buffer->data < buffer->used && PHP_HTTP_IS_CTYPE(space, *ptr)) {
     531           2 :                                         ++ptr;
     532             :                                 }
     533             : 
     534          41 :                                 php_http_buffer_cut(buffer, 0, ptr - buffer->data);
     535             :                                 
     536          41 :                                 if (!(flags & PHP_HTTP_MESSAGE_PARSER_GREEDY)) {
     537          16 :                                         return PHP_HTTP_MESSAGE_PARSER_STATE_DONE;
     538             :                                 }
     539          25 :                                 break;
     540             :                         }
     541             :                 }
     542             :         }
     543             : 
     544       32899 :         return php_http_message_parser_state_is(parser);
     545             : }
     546             : 
     547             : zend_class_entry *php_http_message_parser_class_entry;
     548             : static zend_object_handlers php_http_message_parser_object_handlers;
     549             : 
     550          15 : zend_object_value php_http_message_parser_object_new(zend_class_entry *ce TSRMLS_DC)
     551             : {
     552          15 :         return php_http_message_parser_object_new_ex(ce, NULL, NULL TSRMLS_CC);
     553             : }
     554             : 
     555          15 : zend_object_value php_http_message_parser_object_new_ex(zend_class_entry *ce, php_http_message_parser_t *parser, php_http_message_parser_object_t **ptr TSRMLS_DC)
     556             : {
     557             :         php_http_message_parser_object_t *o;
     558             : 
     559          15 :         o = ecalloc(1, sizeof(php_http_message_parser_object_t));
     560          15 :         zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
     561          15 :         object_properties_init((zend_object *) o, ce);
     562             : 
     563          15 :         if (ptr) {
     564           0 :                 *ptr = o;
     565             :         }
     566             : 
     567          15 :         if (parser) {
     568           0 :                 o->parser = parser;
     569             :         } else {
     570          15 :                 o->parser = php_http_message_parser_init(NULL TSRMLS_CC);
     571             :         }
     572          15 :         o->buffer = php_http_buffer_new();
     573             : 
     574          15 :         o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_http_message_parser_object_free, NULL TSRMLS_CC);
     575          15 :         o->zv.handlers = &php_http_message_parser_object_handlers;
     576             : 
     577          15 :         return o->zv;
     578             : }
     579             : 
     580          15 : void php_http_message_parser_object_free(void *object TSRMLS_DC)
     581             : {
     582          15 :         php_http_message_parser_object_t *o = (php_http_message_parser_object_t *) object;
     583             : 
     584          15 :         if (o->parser) {
     585          15 :                 php_http_message_parser_free(&o->parser);
     586             :         }
     587          15 :         if (o->buffer) {
     588          15 :                 php_http_buffer_free(&o->buffer);
     589             :         }
     590          15 :         zend_object_std_dtor((zend_object *) o TSRMLS_CC);
     591          15 :         efree(o);
     592          15 : }
     593             : 
     594             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_getState, 0, 0, 0)
     595             : ZEND_END_ARG_INFO();
     596           0 : static PHP_METHOD(HttpMessageParser, getState)
     597             : {
     598           0 :         php_http_message_parser_object_t *parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
     599             : 
     600           0 :         zend_parse_parameters_none();
     601             :         /* always return the real state */
     602           0 :         RETVAL_LONG(php_http_message_parser_state_is(parser_obj->parser));
     603           0 : }
     604             : 
     605             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_parse, 0, 0, 3)
     606             :         ZEND_ARG_INFO(0, data)
     607             :         ZEND_ARG_INFO(0, flags)
     608             :         ZEND_ARG_INFO(1, message)
     609             : ZEND_END_ARG_INFO();
     610          91 : static PHP_METHOD(HttpMessageParser, parse)
     611             : {
     612             :         php_http_message_parser_object_t *parser_obj;
     613             :         zval *zmsg;
     614             :         char *data_str;
     615             :         int data_len;
     616             :         long flags;
     617             : 
     618         182 :         php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slz", &data_str, &data_len, &flags, &zmsg), invalid_arg, return);
     619             : 
     620          91 :         parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
     621          91 :         php_http_buffer_append(parser_obj->buffer, data_str, data_len);
     622          91 :         RETVAL_LONG(php_http_message_parser_parse(parser_obj->parser, parser_obj->buffer, flags, &parser_obj->parser->message));
     623             : 
     624          91 :         zval_dtor(zmsg);
     625          91 :         if (parser_obj->parser->message) {
     626          91 :                 ZVAL_OBJVAL(zmsg, php_http_message_object_new_ex(php_http_message_class_entry, php_http_message_copy(parser_obj->parser->message, NULL), NULL TSRMLS_CC), 0);
     627             :         }
     628             : }
     629             : 
     630             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_stream, 0, 0, 3)
     631             :         ZEND_ARG_INFO(0, stream)
     632             :         ZEND_ARG_INFO(0, flags)
     633             :         ZEND_ARG_INFO(1, message)
     634             : ZEND_END_ARG_INFO();
     635          16 : static PHP_METHOD(HttpMessageParser, stream)
     636             : {
     637             :         php_http_message_parser_object_t *parser_obj;
     638             :         zend_error_handling zeh;
     639             :         zval *zmsg, *zstream;
     640             :         php_stream *s;
     641             :         long flags;
     642             : 
     643          16 :         php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rlz", &zstream, &flags, &zmsg), invalid_arg, return);
     644             : 
     645          16 :         zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh TSRMLS_CC);
     646          16 :         php_stream_from_zval(s, &zstream);
     647          16 :         zend_restore_error_handling(&zeh TSRMLS_CC);
     648             : 
     649          16 :         parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
     650          16 :         RETVAL_LONG(php_http_message_parser_parse_stream(parser_obj->parser, parser_obj->buffer, s, flags, &parser_obj->parser->message));
     651             : 
     652          16 :         zval_dtor(zmsg);
     653          16 :         if (parser_obj->parser->message) {
     654          15 :                 ZVAL_OBJVAL(zmsg, php_http_message_object_new_ex(php_http_message_class_entry, php_http_message_copy(parser_obj->parser->message, NULL), NULL TSRMLS_CC), 0);
     655             :         }
     656             : }
     657             : 
     658             : static zend_function_entry php_http_message_parser_methods[] = {
     659             :                 PHP_ME(HttpMessageParser, getState, ai_HttpMessageParser_getState, ZEND_ACC_PUBLIC)
     660             :                 PHP_ME(HttpMessageParser, parse, ai_HttpMessageParser_parse, ZEND_ACC_PUBLIC)
     661             :                 PHP_ME(HttpMessageParser, stream, ai_HttpMessageParser_stream, ZEND_ACC_PUBLIC)
     662             :                 {NULL, NULL, NULL}
     663             : };
     664             : 
     665         374 : PHP_MINIT_FUNCTION(http_message_parser)
     666             : {
     667             :         zend_class_entry ce;
     668             : 
     669         374 :         INIT_NS_CLASS_ENTRY(ce, "http\\Message", "Parser", php_http_message_parser_methods);
     670         374 :         php_http_message_parser_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
     671         374 :         memcpy(&php_http_message_parser_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
     672         374 :         php_http_message_parser_class_entry->create_object = php_http_message_parser_object_new;
     673         374 :         php_http_message_parser_object_handlers.clone_obj = NULL;
     674             : 
     675         374 :         zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("CLEANUP"), PHP_HTTP_MESSAGE_PARSER_CLEANUP TSRMLS_CC);
     676         374 :         zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("DUMB_BODIES"), PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES TSRMLS_CC);
     677         374 :         zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("EMPTY_REDIRECTS"), PHP_HTTP_MESSAGE_PARSER_EMPTY_REDIRECTS TSRMLS_CC);
     678         374 :         zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("GREEDY"), PHP_HTTP_MESSAGE_PARSER_GREEDY TSRMLS_CC);
     679             : 
     680         374 :         zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_FAILURE"), PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE TSRMLS_CC);
     681         374 :         zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_START"), PHP_HTTP_MESSAGE_PARSER_STATE_START TSRMLS_CC);
     682         374 :         zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_HEADER"), PHP_HTTP_MESSAGE_PARSER_STATE_HEADER TSRMLS_CC);
     683         374 :         zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_HEADER_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE TSRMLS_CC);
     684         374 :         zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY TSRMLS_CC);
     685         374 :         zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_DUMB"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB TSRMLS_CC);
     686         374 :         zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_LENGTH"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH TSRMLS_CC);
     687         374 :         zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_CHUNKED"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED TSRMLS_CC);
     688         374 :         zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE TSRMLS_CC);
     689         374 :         zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_UPDATE_CL"), PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL TSRMLS_CC);
     690         374 :         zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_DONE TSRMLS_CC);
     691             : 
     692         374 :         return SUCCESS;
     693             : }
     694             : 
     695             : /*
     696             :  * Local variables:
     697             :  * tab-width: 4
     698             :  * c-basic-offset: 4
     699             :  * End:
     700             :  * vim600: noet sw=4 ts=4 fdm=marker
     701             :  * vim<600: noet sw=4 ts=4
     702             :  */
     703             : 

Generated by: LCOV version 1.11