LCOV - code coverage report
Current view: top level - http - php_http_header_parser.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 87 96 90.6 %
Date: 2014-11-03 12:21:11 Functions: 6 7 85.7 %
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             : typedef struct php_http_header_parser_state_spec {
      16             :         php_http_header_parser_state_t state;
      17             :         unsigned need_data:1;
      18             : } php_http_header_parser_state_spec_t;
      19             : 
      20             : static const php_http_header_parser_state_spec_t php_http_header_parser_states[] = {
      21             :                 {PHP_HTTP_HEADER_PARSER_STATE_START,            1},
      22             :                 {PHP_HTTP_HEADER_PARSER_STATE_KEY,                      1},
      23             :                 {PHP_HTTP_HEADER_PARSER_STATE_VALUE,            1},
      24             :                 {PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX,         1},
      25             :                 {PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE,      0},
      26             :                 {PHP_HTTP_HEADER_PARSER_STATE_DONE,                     0}
      27             : };
      28             : 
      29         170 : php_http_header_parser_t *php_http_header_parser_init(php_http_header_parser_t *parser TSRMLS_DC)
      30             : {
      31         170 :         if (!parser) {
      32           0 :                 parser = emalloc(sizeof(*parser));
      33             :         }
      34         170 :         memset(parser, 0, sizeof(*parser));
      35             : 
      36             :         TSRMLS_SET_CTX(parser->ts);
      37             : 
      38         170 :         return parser;
      39             : }
      40             : 
      41        2240 : php_http_header_parser_state_t php_http_header_parser_state_push(php_http_header_parser_t *parser, unsigned argc, ...)
      42             : {
      43             :         va_list va_args;
      44             :         unsigned i;
      45        2240 :         php_http_header_parser_state_t state = 0;
      46             : 
      47             :         /* short circuit */
      48        2240 :         ZEND_PTR_STACK_RESIZE_IF_NEEDED((&parser->stack), argc);
      49             : 
      50        2240 :         va_start(va_args, argc);
      51        4480 :         for (i = 0; i < argc; ++i) {
      52        2240 :                 state = va_arg(va_args, php_http_header_parser_state_t);
      53        2240 :                 zend_ptr_stack_push(&parser->stack, (void *) state);
      54             :         }
      55        2240 :         va_end(va_args);
      56             : 
      57        2240 :         return state;
      58             : }
      59             : 
      60         344 : php_http_header_parser_state_t php_http_header_parser_state_is(php_http_header_parser_t *parser)
      61             : {
      62         344 :         if (parser->stack.top) {
      63         328 :                 return (php_http_header_parser_state_t) parser->stack.elements[parser->stack.top - 1];
      64             :         }
      65             : 
      66          16 :         return PHP_HTTP_HEADER_PARSER_STATE_START;
      67             : }
      68             : 
      69        2322 : php_http_header_parser_state_t php_http_header_parser_state_pop(php_http_header_parser_t *parser)
      70             : {
      71        2322 :         if (parser->stack.top) {
      72        2193 :                 return (php_http_header_parser_state_t) zend_ptr_stack_pop(&parser->stack);
      73             :         }
      74             : 
      75         129 :         return PHP_HTTP_HEADER_PARSER_STATE_START;
      76             : }
      77             : 
      78         170 : void php_http_header_parser_dtor(php_http_header_parser_t *parser)
      79             : {
      80         170 :         zend_ptr_stack_destroy(&parser->stack);
      81         170 :         php_http_info_dtor(&parser->info);
      82         170 :         STR_FREE(parser->_key.str);
      83         170 :         STR_FREE(parser->_val.str);
      84         170 : }
      85             : 
      86           0 : void php_http_header_parser_free(php_http_header_parser_t **parser)
      87             : {
      88           0 :         if (*parser) {
      89           0 :                 php_http_header_parser_dtor(*parser);
      90           0 :                 efree(*parser);
      91           0 :                 *parser = NULL;
      92             :         }
      93           0 : }
      94             : 
      95         507 : STATUS php_http_header_parser_parse(php_http_header_parser_t *parser, php_http_buffer_t *buffer, unsigned flags, HashTable *headers, php_http_info_callback_t callback_func, void *callback_arg)
      96             : {
      97             :         TSRMLS_FETCH_FROM_CTX(parser->ts);
      98             : 
      99        2923 :         while (buffer->used || !php_http_header_parser_states[php_http_header_parser_state_is(parser)].need_data) {
     100             : #if 0
     101             :                 const char *state[] = {"START", "KEY", "VALUE", "HEADER_DONE", "DONE"};
     102             :                 fprintf(stderr, "#HP: %s (avail:%zu, num:%d)\n", php_http_header_parser_state_is(parser) < 0 ? "FAILURE" : state[php_http_header_parser_state_is(parser)], buffer->used, headers?zend_hash_num_elements(headers):0);
     103             :                 _dpf(0, buffer->data, buffer->used);
     104             : #endif
     105        2322 :                 switch (php_http_header_parser_state_pop(parser)) {
     106             :                         case PHP_HTTP_HEADER_PARSER_STATE_FAILURE:
     107           0 :                                 return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE);
     108             : 
     109             :                         case PHP_HTTP_HEADER_PARSER_STATE_START: {
     110         129 :                                 char *ptr = buffer->data;
     111             : 
     112         258 :                                 while (ptr - buffer->data < buffer->used && PHP_HTTP_IS_CTYPE(space, *ptr)) {
     113           0 :                                         ++ptr;
     114             :                                 }
     115             : 
     116         129 :                                 php_http_buffer_cut(buffer, 0, ptr - buffer->data);
     117         129 :                                 php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_KEY);
     118         129 :                                 break;
     119             :                         }
     120             : 
     121             :                         case PHP_HTTP_HEADER_PARSER_STATE_KEY: {
     122         692 :                                 const char *colon, *eol_str = NULL;
     123         692 :                                 int eol_len = 0;
     124             : 
     125         692 :                                 if (buffer->data == (eol_str = php_http_locate_bin_eol(buffer->data, buffer->used, &eol_len))) {
     126             :                                         /* end of headers */
     127          82 :                                         php_http_buffer_cut(buffer, 0, eol_len);
     128          82 :                                         php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_DONE);
     129         610 :                                 } else if (php_http_info_parse(&parser->info, php_http_buffer_fix(buffer)->data TSRMLS_CC)) {
     130             :                                         /* new message starting with request/response line */
     131         121 :                                         if (callback_func) {
     132         121 :                                                 callback_func(callback_arg, &headers, &parser->info TSRMLS_CC);
     133             :                                         }
     134         121 :                                         php_http_info_dtor(&parser->info);
     135         121 :                                         php_http_buffer_cut(buffer, 0, eol_str + eol_len - buffer->data);
     136         121 :                                         php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE);
     137         489 :                                 } else if ((colon = memchr(buffer->data, ':', buffer->used)) && (!eol_str || eol_str > colon)) {
     138             :                                         /* header: string */
     139         484 :                                         parser->_key.str = estrndup(buffer->data, parser->_key.len = colon - buffer->data);
     140         484 :                                         while (PHP_HTTP_IS_CTYPE(space, *++colon) && *colon != '\n' && *colon != '\r');
     141         484 :                                         php_http_buffer_cut(buffer, 0, colon - buffer->data);
     142         484 :                                         php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE);
     143             :                                 } else {
     144             :                                         /* neither reqeust/response line nor header: string */
     145           5 :                                         return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE);
     146             :                                 }
     147         687 :                                 break;
     148             :                         }
     149             : 
     150             :                         case PHP_HTTP_HEADER_PARSER_STATE_VALUE: {
     151             :                                 const char *eol_str;
     152             :                                 int eol_len;
     153             : 
     154             : #define SET_ADD_VAL(slen, eol_len) \
     155             :         do { \
     156             :                 const char *ptr = buffer->data; \
     157             :                 size_t len = slen; \
     158             :                  \
     159             :                 while (len > 0 && PHP_HTTP_IS_CTYPE(space, *ptr)) { \
     160             :                         ++ptr; \
     161             :                         --len; \
     162             :                 } \
     163             :                 while (len > 0 && PHP_HTTP_IS_CTYPE(space, ptr[len - 1])) { \
     164             :                         --len; \
     165             :                 } \
     166             :                  \
     167             :                 if (len > 0) { \
     168             :                         if (parser->_val.str) { \
     169             :                                 parser->_val.str = erealloc(parser->_val.str, parser->_val.len + len + 2); \
     170             :                                 parser->_val.str[parser->_val.len++] = ' '; \
     171             :                                 memcpy(&parser->_val.str[parser->_val.len], ptr, len); \
     172             :                                 parser->_val.len += len; \
     173             :                                 parser->_val.str[parser->_val.len] = '\0'; \
     174             :                         } else { \
     175             :                                 parser->_val.len = len; \
     176             :                                 parser->_val.str = estrndup(ptr, len); \
     177             :                         } \
     178             :                 } \
     179             :                 php_http_buffer_cut(buffer, 0, slen + eol_len); \
     180             :         } while (0)
     181             : 
     182         489 :                                 if ((eol_str = php_http_locate_bin_eol(buffer->data, buffer->used, &eol_len))) {
     183         487 :                                         SET_ADD_VAL(eol_str - buffer->data, eol_len);
     184             : 
     185         487 :                                         if (buffer->used) {
     186         144 :                                                 if (*buffer->data != '\t' && *buffer->data != ' ') {
     187         141 :                                                         php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE);
     188         304 :                                                         break;
     189             :                                                 } else {
     190           3 :                                                         php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE);
     191           3 :                                                         break;
     192             :                                                 }
     193             :                                         }
     194             :                                 }
     195             : 
     196         345 :                                 if (flags & PHP_HTTP_HEADER_PARSER_CLEANUP) {
     197          19 :                                         if (buffer->used) {
     198           2 :                                                 SET_ADD_VAL(buffer->used, 0);
     199             :                                         }
     200          19 :                                         php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE);
     201             :                                 } else {
     202         326 :                                         return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX);
     203             :                                 }
     204          19 :                                 break;
     205             :                         }
     206             : 
     207             :                         case PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX:
     208         326 :                                 if (*buffer->data == ' ' || *buffer->data == '\t') {
     209           3 :                                         php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE);
     210             :                                 } else {
     211         323 :                                         php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE);
     212             :                                 }
     213         326 :                                 break;
     214             : 
     215             :                         case PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE:
     216         604 :                                 if (parser->_key.str && parser->_val.str) {
     217             :                                         zval array, **exist;
     218             : 
     219         483 :                                         if (!headers && callback_func) {
     220           1 :                                                 callback_func(callback_arg, &headers, NULL TSRMLS_CC);
     221             :                                         }
     222             : 
     223         483 :                                         INIT_PZVAL_ARRAY(&array, headers);
     224         483 :                                         php_http_pretty_key(parser->_key.str, parser->_key.len, 1, 1);
     225         483 :                                         if (SUCCESS == zend_symtable_find(headers, parser->_key.str, parser->_key.len + 1, (void *) &exist)) {
     226           8 :                                                 convert_to_array(*exist);
     227           8 :                                                 add_next_index_stringl(*exist, parser->_val.str, parser->_val.len, 0);
     228             :                                         } else {
     229         475 :                                                 add_assoc_stringl_ex(&array, parser->_key.str, parser->_key.len + 1, parser->_val.str, parser->_val.len, 0);
     230             :                                         }
     231         483 :                                         parser->_val.str = NULL;
     232             :                                 }
     233             : 
     234         604 :                                 STR_SET(parser->_key.str, NULL);
     235         604 :                                 STR_SET(parser->_val.str, NULL);
     236             : 
     237         604 :                                 php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_KEY);
     238         604 :                                 break;
     239             : 
     240             :                         case PHP_HTTP_HEADER_PARSER_STATE_DONE:
     241          82 :                                 return PHP_HTTP_HEADER_PARSER_STATE_DONE;
     242             :                 }
     243             :         }
     244             : 
     245          94 :         return php_http_header_parser_state_is(parser);
     246             : }
     247             : 
     248             : /*
     249             :  * Local variables:
     250             :  * tab-width: 4
     251             :  * c-basic-offset: 4
     252             :  * End:
     253             :  * vim600: noet sw=4 ts=4 fdm=marker
     254             :  * vim<600: noet sw=4 ts=4
     255             :  */
     256             : 

Generated by: LCOV version 1.11