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

Generated by: LCOV version 1.11