PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LCOV - code coverage report
Current view: top level - sapi/cli - php_http_parser.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 0 701 0.0 %
Date: 2016-08-31 Functions: 0 4 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
       2             :  *
       3             :  * Permission is hereby granted, free of charge, to any person obtaining a copy
       4             :  * of this software and associated documentation files (the "Software"), to
       5             :  * deal in the Software without restriction, including without limitation the
       6             :  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
       7             :  * sell copies of the Software, and to permit persons to whom the Software is
       8             :  * furnished to do so, subject to the following conditions:
       9             :  *
      10             :  * The above copyright notice and this permission notice shall be included in
      11             :  * all copies or substantial portions of the Software.
      12             :  *
      13             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      14             :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      15             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      16             :  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      17             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      18             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
      19             :  * IN THE SOFTWARE.
      20             :  */
      21             : #include <assert.h>
      22             : #include <stddef.h>
      23             : #include "php_http_parser.h"
      24             : 
      25             : 
      26             : #ifndef MIN
      27             : # define MIN(a,b) ((a) < (b) ? (a) : (b))
      28             : #endif
      29             : 
      30             : 
      31             : #define CALLBACK2(FOR)                                               \
      32             : do {                                                                 \
      33             :   if (settings->on_##FOR) {                                          \
      34             :     if (0 != settings->on_##FOR(parser)) return (p - data);          \
      35             :   }                                                                  \
      36             : } while (0)
      37             : 
      38             : 
      39             : #define MARK(FOR)                                                    \
      40             : do {                                                                 \
      41             :   FOR##_mark = p;                                                    \
      42             : } while (0)
      43             : 
      44             : #define CALLBACK_NOCLEAR(FOR)                                        \
      45             : do {                                                                 \
      46             :   if (FOR##_mark) {                                                  \
      47             :     if (settings->on_##FOR) {                                        \
      48             :       if (0 != settings->on_##FOR(parser,                            \
      49             :                                  FOR##_mark,                         \
      50             :                                  p - FOR##_mark))                    \
      51             :       {                                                              \
      52             :         return (p - data);                                           \
      53             :       }                                                              \
      54             :     }                                                                \
      55             :   }                                                                  \
      56             : } while (0)
      57             : 
      58             : #ifdef PHP_WIN32
      59             : # undef CALLBACK
      60             : #endif
      61             : #define CALLBACK(FOR)                                                \
      62             : do {                                                                 \
      63             :   CALLBACK_NOCLEAR(FOR);                                             \
      64             :   FOR##_mark = NULL;                                                 \
      65             : } while (0)
      66             : 
      67             : 
      68             : #define PROXY_CONNECTION "proxy-connection"
      69             : #define CONNECTION "connection"
      70             : #define CONTENT_LENGTH "content-length"
      71             : #define TRANSFER_ENCODING "transfer-encoding"
      72             : #define UPGRADE "upgrade"
      73             : #define CHUNKED "chunked"
      74             : #define KEEP_ALIVE "keep-alive"
      75             : #define CLOSE "close"
      76             : 
      77             : 
      78             : static const char *method_strings[] =
      79             :   { "DELETE"
      80             :   , "GET"
      81             :   , "HEAD"
      82             :   , "POST"
      83             :   , "PUT"
      84             :   , "PATCH"
      85             :   , "CONNECT"
      86             :   , "OPTIONS"
      87             :   , "TRACE"
      88             :   , "COPY"
      89             :   , "LOCK"
      90             :   , "MKCOL"
      91             :   , "MOVE"
      92             :   , "MKCALENDAR"
      93             :   , "PROPFIND"
      94             :   , "PROPPATCH"
      95             :   , "SEARCH"
      96             :   , "UNLOCK"
      97             :   , "REPORT"
      98             :   , "MKACTIVITY"
      99             :   , "CHECKOUT"
     100             :   , "MERGE"
     101             :   , "M-SEARCH"
     102             :   , "NOTIFY"
     103             :   , "SUBSCRIBE"
     104             :   , "UNSUBSCRIBE"
     105             :   , "NOTIMPLEMENTED"
     106             :   };
     107             : 
     108             : 
     109             : /* Tokens as defined by rfc 2616. Also lowercases them.
     110             :  *        token       = 1*<any CHAR except CTLs or separators>
     111             :  *     separators     = "(" | ")" | "<" | ">" | "@"
     112             :  *                    | "," | ";" | ":" | "\" | <">
     113             :  *                    | "/" | "[" | "]" | "?" | "="
     114             :  *                    | "{" | "}" | SP | HT
     115             :  */
     116             : static const char tokens[256] = {
     117             : /*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
     118             :         0,       0,       0,       0,       0,       0,       0,       0,
     119             : /*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
     120             :         0,       0,       0,       0,       0,       0,       0,       0,
     121             : /*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
     122             :         0,       0,       0,       0,       0,       0,       0,       0,
     123             : /*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
     124             :         0,       0,       0,       0,       0,       0,       0,       0,
     125             : /*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
     126             :        ' ',      '!',     '"',     '#',     '$',     '%',     '&',    '\'',
     127             : /*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
     128             :         0,       0,      '*',     '+',      0,      '-',     '.',     '/',
     129             : /*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
     130             :        '0',     '1',     '2',     '3',     '4',     '5',     '6',     '7',
     131             : /*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
     132             :        '8',     '9',      0,       0,       0,       0,       0,       0,
     133             : /*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
     134             :         0,      'a',     'b',     'c',     'd',     'e',     'f',     'g',
     135             : /*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
     136             :        'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
     137             : /*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
     138             :        'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
     139             : /*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
     140             :        'x',     'y',     'z',      0,       0,       0,      '^',     '_',
     141             : /*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
     142             :        '`',     'a',     'b',     'c',     'd',     'e',     'f',     'g',
     143             : /* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
     144             :        'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
     145             : /* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
     146             :        'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
     147             : /* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
     148             :        'x',     'y',     'z',      0,      '|',     '}',     '~',       0 };
     149             : 
     150             : 
     151             : static const int8_t unhex[256] =
     152             :   {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
     153             :   ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
     154             :   ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
     155             :   , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
     156             :   ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
     157             :   ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
     158             :   ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
     159             :   ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
     160             :   };
     161             : 
     162             : 
     163             : static const uint8_t normal_url_char[256] = {
     164             : /*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
     165             :         0,       0,       0,       0,       0,       0,       0,       0,
     166             : /*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
     167             :         0,       0,       0,       0,       0,       0,       0,       0,
     168             : /*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
     169             :         0,       0,       0,       0,       0,       0,       0,       0,
     170             : /*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
     171             :         0,       0,       0,       0,       0,       0,       0,       0,
     172             : /*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
     173             :         0,       1,       1,       0,       1,       1,       1,       1,
     174             : /*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
     175             :         1,       1,       1,       1,       1,       1,       1,       1,
     176             : /*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
     177             :         1,       1,       1,       1,       1,       1,       1,       1,
     178             : /*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
     179             :         1,       1,       1,       1,       1,       1,       1,       0,
     180             : /*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
     181             :         1,       1,       1,       1,       1,       1,       1,       1,
     182             : /*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
     183             :         1,       1,       1,       1,       1,       1,       1,       1,
     184             : /*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
     185             :         1,       1,       1,       1,       1,       1,       1,       1,
     186             : /*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
     187             :         1,       1,       1,       1,       1,       1,       1,       1,
     188             : /*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
     189             :         1,       1,       1,       1,       1,       1,       1,       1,
     190             : /* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
     191             :         1,       1,       1,       1,       1,       1,       1,       1,
     192             : /* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
     193             :         1,       1,       1,       1,       1,       1,       1,       1,
     194             : /* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
     195             :         1,       1,       1,       1,       1,       1,       1,       0 };
     196             : 
     197             : 
     198             : enum state
     199             :   { s_dead = 1 /* important that this is > 0 */
     200             : 
     201             :   , s_start_req_or_res
     202             :   , s_res_or_resp_H
     203             :   , s_start_res
     204             :   , s_res_H
     205             :   , s_res_HT
     206             :   , s_res_HTT
     207             :   , s_res_HTTP
     208             :   , s_res_first_http_major
     209             :   , s_res_http_major
     210             :   , s_res_first_http_minor
     211             :   , s_res_http_minor
     212             :   , s_res_first_status_code
     213             :   , s_res_status_code
     214             :   , s_res_status
     215             :   , s_res_line_almost_done
     216             : 
     217             :   , s_start_req
     218             : 
     219             :   , s_req_method
     220             :   , s_req_spaces_before_url
     221             :   , s_req_schema
     222             :   , s_req_schema_slash
     223             :   , s_req_schema_slash_slash
     224             :   , s_req_host
     225             :   , s_req_port
     226             :   , s_req_path
     227             :   , s_req_query_string_start
     228             :   , s_req_query_string
     229             :   , s_req_fragment_start
     230             :   , s_req_fragment
     231             :   , s_req_http_start
     232             :   , s_req_http_H
     233             :   , s_req_http_HT
     234             :   , s_req_http_HTT
     235             :   , s_req_http_HTTP
     236             :   , s_req_first_http_major
     237             :   , s_req_http_major
     238             :   , s_req_first_http_minor
     239             :   , s_req_http_minor
     240             :   , s_req_line_almost_done
     241             : 
     242             :   , s_header_field_start
     243             :   , s_header_field
     244             :   , s_header_value_start
     245             :   , s_header_value
     246             : 
     247             :   , s_header_almost_done
     248             : 
     249             :   , s_headers_almost_done
     250             :   /* Important: 's_headers_almost_done' must be the last 'header' state. All
     251             :    * states beyond this must be 'body' states. It is used for overflow
     252             :    * checking. See the PARSING_HEADER() macro.
     253             :    */
     254             :   , s_chunk_size_start
     255             :   , s_chunk_size
     256             :   , s_chunk_size_almost_done
     257             :   , s_chunk_parameters
     258             :   , s_chunk_data
     259             :   , s_chunk_data_almost_done
     260             :   , s_chunk_data_done
     261             : 
     262             :   , s_body_identity
     263             :   , s_body_identity_eof
     264             :   };
     265             : 
     266             : 
     267             : #define PARSING_HEADER(state) (state <= s_headers_almost_done && 0 == (parser->flags & F_TRAILING))
     268             : 
     269             : 
     270             : enum header_states
     271             :   { h_general = 0
     272             :   , h_C
     273             :   , h_CO
     274             :   , h_CON
     275             : 
     276             :   , h_matching_connection
     277             :   , h_matching_proxy_connection
     278             :   , h_matching_content_length
     279             :   , h_matching_transfer_encoding
     280             :   , h_matching_upgrade
     281             : 
     282             :   , h_connection
     283             :   , h_content_length
     284             :   , h_transfer_encoding
     285             :   , h_upgrade
     286             : 
     287             :   , h_matching_transfer_encoding_chunked
     288             :   , h_matching_connection_keep_alive
     289             :   , h_matching_connection_close
     290             : 
     291             :   , h_transfer_encoding_chunked
     292             :   , h_connection_keep_alive
     293             :   , h_connection_close
     294             :   };
     295             : 
     296             : 
     297             : enum flags
     298             :   { F_CHUNKED               = 1 << 0
     299             :   , F_CONNECTION_KEEP_ALIVE = 1 << 1
     300             :   , F_CONNECTION_CLOSE      = 1 << 2
     301             :   , F_TRAILING              = 1 << 3
     302             :   , F_UPGRADE               = 1 << 4
     303             :   , F_SKIPBODY              = 1 << 5
     304             :   };
     305             : 
     306             : 
     307             : #define CR '\r'
     308             : #define LF '\n'
     309             : #define LOWER(c) (unsigned char)(c | 0x20)
     310             : #define TOKEN(c) tokens[(unsigned char)c]
     311             : 
     312             : 
     313             : #define start_state (parser->type == PHP_HTTP_REQUEST ? s_start_req : s_start_res)
     314             : 
     315             : 
     316             : #if HTTP_PARSER_STRICT
     317             : # define STRICT_CHECK(cond) if (cond) goto error
     318             : # define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
     319             : #else
     320             : # define STRICT_CHECK(cond)
     321             : # define NEW_MESSAGE() start_state
     322             : #endif
     323             : 
     324             : 
     325           0 : size_t php_http_parser_execute (php_http_parser *parser,
     326             :                             const php_http_parser_settings *settings,
     327             :                             const char *data,
     328             :                             size_t len)
     329             : {
     330             :   char ch;
     331             :   signed char c;
     332           0 :   const char *p = data, *pe;
     333             :   size_t to_read;
     334             : 
     335           0 :   enum state state = (enum state) parser->state;
     336           0 :   enum header_states header_state = (enum header_states) parser->header_state;
     337           0 :   uint32_t index = parser->index;
     338           0 :   uint32_t nread = parser->nread;
     339             : 
     340             :   /* technically we could combine all of these (except for url_mark) into one
     341             :      variable, saving stack space, but it seems more clear to have them
     342             :      separated. */
     343           0 :   const char *header_field_mark = 0;
     344           0 :   const char *header_value_mark = 0;
     345           0 :   const char *fragment_mark = 0;
     346           0 :   const char *query_string_mark = 0;
     347           0 :   const char *path_mark = 0;
     348           0 :   const char *url_mark = 0;
     349             : 
     350           0 :   if (len == 0) {
     351           0 :     if (state == s_body_identity_eof) {
     352           0 :       CALLBACK2(message_complete);
     353             :     }
     354           0 :     return 0;
     355             :   }
     356             : 
     357           0 :   if (state == s_header_field)
     358           0 :     header_field_mark = data;
     359           0 :   if (state == s_header_value)
     360           0 :     header_value_mark = data;
     361           0 :   if (state == s_req_fragment)
     362           0 :     fragment_mark = data;
     363           0 :   if (state == s_req_query_string)
     364           0 :     query_string_mark = data;
     365           0 :   if (state == s_req_path)
     366           0 :     path_mark = data;
     367           0 :   if (state == s_req_path || state == s_req_schema || state == s_req_schema_slash
     368             :       || state == s_req_schema_slash_slash || state == s_req_port
     369             :       || state == s_req_query_string_start || state == s_req_query_string
     370             :       || state == s_req_host
     371             :       || state == s_req_fragment_start || state == s_req_fragment)
     372           0 :     url_mark = data;
     373             : 
     374           0 :   for (p=data, pe=data+len; p != pe; p++) {
     375           0 :     ch = *p;
     376             : 
     377           0 :     if (PARSING_HEADER(state)) {
     378           0 :       ++nread;
     379             :       /* Buffer overflow attack */
     380           0 :       if (nread > PHP_HTTP_MAX_HEADER_SIZE) goto error;
     381             :     }
     382             : 
     383           0 :     switch (state) {
     384             : 
     385             :       case s_dead:
     386             :         /* this state is used after a 'Connection: close' message
     387             :          * the parser will error out if it reads another message
     388             :          */
     389           0 :         goto error;
     390             : 
     391             :       case s_start_req_or_res:
     392             :       {
     393           0 :         if (ch == CR || ch == LF)
     394             :           break;
     395           0 :         parser->flags = 0;
     396           0 :         parser->content_length = -1;
     397             : 
     398           0 :         CALLBACK2(message_begin);
     399             : 
     400           0 :         if (ch == 'H')
     401           0 :           state = s_res_or_resp_H;
     402             :         else {
     403           0 :           parser->type = PHP_HTTP_REQUEST;
     404           0 :           goto start_req_method_assign;
     405             :         }
     406           0 :         break;
     407             :       }
     408             : 
     409             :       case s_res_or_resp_H:
     410           0 :         if (ch == 'T') {
     411           0 :           parser->type = PHP_HTTP_RESPONSE;
     412           0 :           state = s_res_HT;
     413             :         } else {
     414           0 :           if (ch != 'E') goto error;
     415           0 :           parser->type = PHP_HTTP_REQUEST;
     416           0 :           parser->method = PHP_HTTP_HEAD;
     417           0 :           index = 2;
     418           0 :           state = s_req_method;
     419             :         }
     420           0 :         break;
     421             : 
     422             :       case s_start_res:
     423             :       {
     424           0 :         parser->flags = 0;
     425           0 :         parser->content_length = -1;
     426             : 
     427           0 :         CALLBACK2(message_begin);
     428             : 
     429           0 :         switch (ch) {
     430             :           case 'H':
     431           0 :             state = s_res_H;
     432           0 :             break;
     433             : 
     434             :           case CR:
     435             :           case LF:
     436           0 :             break;
     437             : 
     438             :           default:
     439           0 :             goto error;
     440             :         }
     441           0 :         break;
     442             :       }
     443             : 
     444             :       case s_res_H:
     445             :         STRICT_CHECK(ch != 'T');
     446           0 :         state = s_res_HT;
     447           0 :         break;
     448             : 
     449             :       case s_res_HT:
     450             :         STRICT_CHECK(ch != 'T');
     451           0 :         state = s_res_HTT;
     452           0 :         break;
     453             : 
     454             :       case s_res_HTT:
     455             :         STRICT_CHECK(ch != 'P');
     456           0 :         state = s_res_HTTP;
     457           0 :         break;
     458             : 
     459             :       case s_res_HTTP:
     460             :         STRICT_CHECK(ch != '/');
     461           0 :         state = s_res_first_http_major;
     462           0 :         break;
     463             : 
     464             :       case s_res_first_http_major:
     465           0 :         if (ch < '1' || ch > '9') goto error;
     466           0 :         parser->http_major = ch - '0';
     467           0 :         state = s_res_http_major;
     468           0 :         break;
     469             : 
     470             :       /* major HTTP version or dot */
     471             :       case s_res_http_major:
     472             :       {
     473           0 :         if (ch == '.') {
     474           0 :           state = s_res_first_http_minor;
     475           0 :           break;
     476             :         }
     477             : 
     478           0 :         if (ch < '0' || ch > '9') goto error;
     479             : 
     480           0 :         parser->http_major *= 10;
     481           0 :         parser->http_major += ch - '0';
     482             : 
     483           0 :         if (parser->http_major > 999) goto error;
     484           0 :         break;
     485             :       }
     486             : 
     487             :       /* first digit of minor HTTP version */
     488             :       case s_res_first_http_minor:
     489           0 :         if (ch < '0' || ch > '9') goto error;
     490           0 :         parser->http_minor = ch - '0';
     491           0 :         state = s_res_http_minor;
     492           0 :         break;
     493             : 
     494             :       /* minor HTTP version or end of request line */
     495             :       case s_res_http_minor:
     496             :       {
     497           0 :         if (ch == ' ') {
     498           0 :           state = s_res_first_status_code;
     499           0 :           break;
     500             :         }
     501             : 
     502           0 :         if (ch < '0' || ch > '9') goto error;
     503             : 
     504           0 :         parser->http_minor *= 10;
     505           0 :         parser->http_minor += ch - '0';
     506             : 
     507           0 :         if (parser->http_minor > 999) goto error;
     508           0 :         break;
     509             :       }
     510             : 
     511             :       case s_res_first_status_code:
     512             :       {
     513           0 :         if (ch < '0' || ch > '9') {
     514           0 :           if (ch == ' ') {
     515           0 :             break;
     516             :           }
     517           0 :           goto error;
     518             :         }
     519           0 :         parser->status_code = ch - '0';
     520           0 :         state = s_res_status_code;
     521           0 :         break;
     522             :       }
     523             : 
     524             :       case s_res_status_code:
     525             :       {
     526           0 :         if (ch < '0' || ch > '9') {
     527           0 :           switch (ch) {
     528             :             case ' ':
     529           0 :               state = s_res_status;
     530           0 :               break;
     531             :             case CR:
     532           0 :               state = s_res_line_almost_done;
     533           0 :               break;
     534             :             case LF:
     535           0 :               state = s_header_field_start;
     536           0 :               break;
     537             :             default:
     538           0 :               goto error;
     539             :           }
     540           0 :           break;
     541             :         }
     542             : 
     543           0 :         parser->status_code *= 10;
     544           0 :         parser->status_code += ch - '0';
     545             : 
     546           0 :         if (parser->status_code > 999) goto error;
     547           0 :         break;
     548             :       }
     549             : 
     550             :       case s_res_status:
     551             :         /* the human readable status. e.g. "NOT FOUND"
     552             :          * we are not humans so just ignore this */
     553           0 :         if (ch == CR) {
     554           0 :           state = s_res_line_almost_done;
     555           0 :           break;
     556             :         }
     557             : 
     558           0 :         if (ch == LF) {
     559           0 :           state = s_header_field_start;
     560           0 :           break;
     561             :         }
     562           0 :         break;
     563             : 
     564             :       case s_res_line_almost_done:
     565             :         STRICT_CHECK(ch != LF);
     566           0 :         state = s_header_field_start;
     567           0 :         break;
     568             : 
     569             :       case s_start_req:
     570             :       {
     571           0 :         if (ch == CR || ch == LF)
     572             :           break;
     573           0 :         parser->flags = 0;
     574           0 :         parser->content_length = -1;
     575             : 
     576           0 :         CALLBACK2(message_begin);
     577             : 
     578           0 :         if (ch < 'A' || 'Z' < ch) goto error;
     579             : 
     580             :       start_req_method_assign:
     581           0 :         parser->method = (enum php_http_method) 0;
     582           0 :         index = 1;
     583           0 :         switch (ch) {
     584           0 :           case 'C': parser->method = PHP_HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
     585           0 :           case 'D': parser->method = PHP_HTTP_DELETE; break;
     586           0 :           case 'G': parser->method = PHP_HTTP_GET; break;
     587           0 :           case 'H': parser->method = PHP_HTTP_HEAD; break;
     588           0 :           case 'L': parser->method = PHP_HTTP_LOCK; break;
     589           0 :           case 'M': parser->method = PHP_HTTP_MKCOL; /* or MOVE, MKCALENDAR, MKACTIVITY, MERGE, M-SEARCH */ break;
     590           0 :           case 'N': parser->method = PHP_HTTP_NOTIFY; break;
     591           0 :           case 'O': parser->method = PHP_HTTP_OPTIONS; break;
     592           0 :           case 'P': parser->method = PHP_HTTP_POST; /* or PROPFIND or PROPPATCH or PUT */ break;
     593           0 :           case 'R': parser->method = PHP_HTTP_REPORT; break;
     594           0 :           case 'S': parser->method = PHP_HTTP_SUBSCRIBE; /* or SEARCH */ break;
     595           0 :           case 'T': parser->method = PHP_HTTP_TRACE; break;
     596           0 :           case 'U': parser->method = PHP_HTTP_UNLOCK; /* or UNSUBSCRIBE */ break;
     597           0 :           default: parser->method = PHP_HTTP_NOT_IMPLEMENTED; break;
     598             :         }
     599           0 :         state = s_req_method;
     600           0 :         break;
     601             :       }
     602             :       case s_req_method:
     603             :       {
     604             :         const char *matcher;
     605           0 :         if (ch == '\0')
     606           0 :           goto error;
     607             : 
     608           0 :         matcher = method_strings[parser->method];
     609           0 :         if (ch == ' ') {
     610           0 :           if (parser->method != PHP_HTTP_NOT_IMPLEMENTED && matcher[index] != '\0') {
     611           0 :             parser->method = PHP_HTTP_NOT_IMPLEMENTED;
     612             :           }
     613           0 :           state = s_req_spaces_before_url;
     614           0 :         } else if (parser->method == PHP_HTTP_NOT_IMPLEMENTED || ch == matcher[index]) {
     615             :           ; /* nada */
     616           0 :         } else if (parser->method == PHP_HTTP_CONNECT) {
     617           0 :           if (index == 1 && ch == 'H') {
     618           0 :             parser->method = PHP_HTTP_CHECKOUT;
     619           0 :           } else if (index == 2  && ch == 'P') {
     620           0 :             parser->method = PHP_HTTP_COPY;
     621             :           } else {
     622           0 :             parser->method = PHP_HTTP_NOT_IMPLEMENTED;
     623             :           }
     624           0 :         } else if (parser->method == PHP_HTTP_MKCOL) {
     625           0 :           if (index == 1 && ch == 'O') {
     626           0 :             parser->method = PHP_HTTP_MOVE;
     627           0 :           } else if (index == 3 && ch == 'A') {
     628           0 :             parser->method = PHP_HTTP_MKCALENDAR;
     629           0 :           } else if (index == 1 && ch == 'E') {
     630           0 :             parser->method = PHP_HTTP_MERGE;
     631           0 :           } else if (index == 1 && ch == '-') {
     632           0 :             parser->method = PHP_HTTP_MSEARCH;
     633           0 :           } else if (index == 2 && ch == 'A') {
     634           0 :             parser->method = PHP_HTTP_MKACTIVITY;
     635             :           } else {
     636           0 :             parser->method = PHP_HTTP_NOT_IMPLEMENTED;
     637             :           }
     638           0 :         } else if (index == 1 && parser->method == PHP_HTTP_POST && ch == 'R') {
     639           0 :           parser->method = PHP_HTTP_PROPFIND; /* or HTTP_PROPPATCH */
     640           0 :         } else if (index == 1 && parser->method == PHP_HTTP_POST && ch == 'U') {
     641           0 :           parser->method = PHP_HTTP_PUT;
     642           0 :         } else if (index == 1 && parser->method == PHP_HTTP_POST && ch == 'A') {
     643           0 :           parser->method = PHP_HTTP_PATCH;
     644           0 :         } else if (index == 1 && parser->method == PHP_HTTP_SUBSCRIBE && ch == 'E') {
     645           0 :           parser->method = PHP_HTTP_SEARCH;
     646           0 :         } else if (index == 2 && parser->method == PHP_HTTP_UNLOCK && ch == 'S') {
     647           0 :           parser->method = PHP_HTTP_UNSUBSCRIBE;
     648           0 :         } else if (index == 4 && parser->method == PHP_HTTP_PROPFIND && ch == 'P') {
     649           0 :           parser->method = PHP_HTTP_PROPPATCH;
     650             :         } else {
     651           0 :           parser->method = PHP_HTTP_NOT_IMPLEMENTED;
     652             :         }
     653             : 
     654           0 :         ++index;
     655           0 :         break;
     656             :       }
     657             :       case s_req_spaces_before_url:
     658             :       {
     659           0 :         if (ch == ' ') break;
     660             : 
     661           0 :         if (ch == '/' || ch == '*') {
     662           0 :           MARK(url);
     663           0 :           MARK(path);
     664           0 :           state = s_req_path;
     665           0 :           break;
     666             :         }
     667             : 
     668           0 :         c = LOWER(ch);
     669             : 
     670           0 :         if (c >= 'a' && c <= 'z') {
     671           0 :           MARK(url);
     672           0 :           state = s_req_schema;
     673           0 :           break;
     674             :         }
     675             : 
     676           0 :         goto error;
     677             :       }
     678             : 
     679             :       case s_req_schema:
     680             :       {
     681           0 :         c = LOWER(ch);
     682             : 
     683           0 :         if (c >= 'a' && c <= 'z') break;
     684             : 
     685           0 :         if (ch == ':') {
     686           0 :           state = s_req_schema_slash;
     687           0 :           break;
     688           0 :         } else if (ch == '.') {
     689           0 :           state = s_req_host;
     690           0 :           break;
     691           0 :         } else if ('0' <= ch && ch <= '9') {
     692           0 :           state = s_req_host;
     693           0 :           break;
     694             :         }
     695             : 
     696           0 :         goto error;
     697             :       }
     698             : 
     699             :       case s_req_schema_slash:
     700             :         STRICT_CHECK(ch != '/');
     701           0 :         state = s_req_schema_slash_slash;
     702           0 :         break;
     703             : 
     704             :       case s_req_schema_slash_slash:
     705             :         STRICT_CHECK(ch != '/');
     706           0 :         state = s_req_host;
     707           0 :         break;
     708             : 
     709             :       case s_req_host:
     710             :       {
     711           0 :         c = LOWER(ch);
     712           0 :         if (c >= 'a' && c <= 'z') break;
     713           0 :         if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') break;
     714           0 :         switch (ch) {
     715             :           case ':':
     716           0 :             state = s_req_port;
     717           0 :             break;
     718             :           case '/':
     719           0 :             MARK(path);
     720           0 :             state = s_req_path;
     721           0 :             break;
     722             :           case ' ':
     723             :             /* The request line looks like:
     724             :              *   "GET http://foo.bar.com HTTP/1.1"
     725             :              * That is, there is no path.
     726             :              */
     727           0 :             CALLBACK(url);
     728           0 :             state = s_req_http_start;
     729           0 :             break;
     730             :           default:
     731           0 :             goto error;
     732             :         }
     733           0 :         break;
     734             :       }
     735             : 
     736             :       case s_req_port:
     737             :       {
     738           0 :         if (ch >= '0' && ch <= '9') break;
     739           0 :         switch (ch) {
     740             :           case '/':
     741           0 :             MARK(path);
     742           0 :             state = s_req_path;
     743           0 :             break;
     744             :           case ' ':
     745             :             /* The request line looks like:
     746             :              *   "GET http://foo.bar.com:1234 HTTP/1.1"
     747             :              * That is, there is no path.
     748             :              */
     749           0 :             CALLBACK(url);
     750           0 :             state = s_req_http_start;
     751           0 :             break;
     752             :           default:
     753           0 :             goto error;
     754             :         }
     755           0 :         break;
     756             :       }
     757             : 
     758             :       case s_req_path:
     759             :       {
     760           0 :         if (normal_url_char[(unsigned char)ch]) break;
     761             : 
     762           0 :         switch (ch) {
     763             :           case ' ':
     764           0 :             CALLBACK(url);
     765           0 :             CALLBACK(path);
     766           0 :             state = s_req_http_start;
     767           0 :             break;
     768             :           case CR:
     769           0 :             CALLBACK(url);
     770           0 :             CALLBACK(path);
     771           0 :             parser->http_major = 0;
     772           0 :             parser->http_minor = 9;
     773           0 :             state = s_req_line_almost_done;
     774           0 :             break;
     775             :           case LF:
     776           0 :             CALLBACK(url);
     777           0 :             CALLBACK(path);
     778           0 :             parser->http_major = 0;
     779           0 :             parser->http_minor = 9;
     780           0 :             state = s_header_field_start;
     781           0 :             break;
     782             :           case '?':
     783           0 :             CALLBACK(path);
     784           0 :             state = s_req_query_string_start;
     785           0 :             break;
     786             :           case '#':
     787           0 :             CALLBACK(path);
     788           0 :             state = s_req_fragment_start;
     789           0 :             break;
     790             :           default:
     791           0 :             goto error;
     792             :         }
     793           0 :         break;
     794             :       }
     795             : 
     796             :       case s_req_query_string_start:
     797             :       {
     798           0 :         if (normal_url_char[(unsigned char)ch]) {
     799           0 :           MARK(query_string);
     800           0 :           state = s_req_query_string;
     801           0 :           break;
     802             :         }
     803             : 
     804           0 :         switch (ch) {
     805             :           case '?':
     806           0 :             break; /* XXX ignore extra '?' ... is this right? */
     807             :           case ' ':
     808           0 :             CALLBACK(url);
     809           0 :             state = s_req_http_start;
     810           0 :             break;
     811             :           case CR:
     812           0 :             CALLBACK(url);
     813           0 :             parser->http_major = 0;
     814           0 :             parser->http_minor = 9;
     815           0 :             state = s_req_line_almost_done;
     816           0 :             break;
     817             :           case LF:
     818           0 :             CALLBACK(url);
     819           0 :             parser->http_major = 0;
     820           0 :             parser->http_minor = 9;
     821           0 :             state = s_header_field_start;
     822           0 :             break;
     823             :           case '#':
     824           0 :             state = s_req_fragment_start;
     825           0 :             break;
     826             :           default:
     827           0 :             goto error;
     828             :         }
     829           0 :         break;
     830             :       }
     831             : 
     832             :       case s_req_query_string:
     833             :       {
     834           0 :         if (normal_url_char[(unsigned char)ch]) break;
     835             : 
     836           0 :         switch (ch) {
     837             :           case '?':
     838             :             /* allow extra '?' in query string */
     839           0 :             break;
     840             :           case ' ':
     841           0 :             CALLBACK(url);
     842           0 :             CALLBACK(query_string);
     843           0 :             state = s_req_http_start;
     844           0 :             break;
     845             :           case CR:
     846           0 :             CALLBACK(url);
     847           0 :             CALLBACK(query_string);
     848           0 :             parser->http_major = 0;
     849           0 :             parser->http_minor = 9;
     850           0 :             state = s_req_line_almost_done;
     851           0 :             break;
     852             :           case LF:
     853           0 :             CALLBACK(url);
     854           0 :             CALLBACK(query_string);
     855           0 :             parser->http_major = 0;
     856           0 :             parser->http_minor = 9;
     857           0 :             state = s_header_field_start;
     858           0 :             break;
     859             :           case '#':
     860           0 :             CALLBACK(query_string);
     861           0 :             state = s_req_fragment_start;
     862           0 :             break;
     863             :           default:
     864           0 :             goto error;
     865             :         }
     866           0 :         break;
     867             :       }
     868             : 
     869             :       case s_req_fragment_start:
     870             :       {
     871           0 :         if (normal_url_char[(unsigned char)ch]) {
     872           0 :           MARK(fragment);
     873           0 :           state = s_req_fragment;
     874           0 :           break;
     875             :         }
     876             : 
     877           0 :         switch (ch) {
     878             :           case ' ':
     879           0 :             CALLBACK(url);
     880           0 :             state = s_req_http_start;
     881           0 :             break;
     882             :           case CR:
     883           0 :             CALLBACK(url);
     884           0 :             parser->http_major = 0;
     885           0 :             parser->http_minor = 9;
     886           0 :             state = s_req_line_almost_done;
     887           0 :             break;
     888             :           case LF:
     889           0 :             CALLBACK(url);
     890           0 :             parser->http_major = 0;
     891           0 :             parser->http_minor = 9;
     892           0 :             state = s_header_field_start;
     893           0 :             break;
     894             :           case '?':
     895           0 :             MARK(fragment);
     896           0 :             state = s_req_fragment;
     897           0 :             break;
     898             :           case '#':
     899           0 :             break;
     900             :           default:
     901           0 :             goto error;
     902             :         }
     903           0 :         break;
     904             :       }
     905             : 
     906             :       case s_req_fragment:
     907             :       {
     908           0 :         if (normal_url_char[(unsigned char)ch]) break;
     909             : 
     910           0 :         switch (ch) {
     911             :           case ' ':
     912           0 :             CALLBACK(url);
     913           0 :             CALLBACK(fragment);
     914           0 :             state = s_req_http_start;
     915           0 :             break;
     916             :           case CR:
     917           0 :             CALLBACK(url);
     918           0 :             CALLBACK(fragment);
     919           0 :             parser->http_major = 0;
     920           0 :             parser->http_minor = 9;
     921           0 :             state = s_req_line_almost_done;
     922           0 :             break;
     923             :           case LF:
     924           0 :             CALLBACK(url);
     925           0 :             CALLBACK(fragment);
     926           0 :             parser->http_major = 0;
     927           0 :             parser->http_minor = 9;
     928           0 :             state = s_header_field_start;
     929           0 :             break;
     930             :           case '?':
     931             :           case '#':
     932           0 :             break;
     933             :           default:
     934           0 :             goto error;
     935             :         }
     936           0 :         break;
     937             :       }
     938             : 
     939             :       case s_req_http_start:
     940           0 :         switch (ch) {
     941             :           case 'H':
     942           0 :             state = s_req_http_H;
     943           0 :             break;
     944             :           case ' ':
     945           0 :             break;
     946             :           default:
     947           0 :             goto error;
     948             :         }
     949           0 :         break;
     950             : 
     951             :       case s_req_http_H:
     952             :         STRICT_CHECK(ch != 'T');
     953           0 :         state = s_req_http_HT;
     954           0 :         break;
     955             : 
     956             :       case s_req_http_HT:
     957             :         STRICT_CHECK(ch != 'T');
     958           0 :         state = s_req_http_HTT;
     959           0 :         break;
     960             : 
     961             :       case s_req_http_HTT:
     962             :         STRICT_CHECK(ch != 'P');
     963           0 :         state = s_req_http_HTTP;
     964           0 :         break;
     965             : 
     966             :       case s_req_http_HTTP:
     967             :         STRICT_CHECK(ch != '/');
     968           0 :         state = s_req_first_http_major;
     969           0 :         break;
     970             : 
     971             :       /* first digit of major HTTP version */
     972             :       case s_req_first_http_major:
     973           0 :         if (ch < '1' || ch > '9') goto error;
     974           0 :         parser->http_major = ch - '0';
     975           0 :         state = s_req_http_major;
     976           0 :         break;
     977             : 
     978             :       /* major HTTP version or dot */
     979             :       case s_req_http_major:
     980             :       {
     981           0 :         if (ch == '.') {
     982           0 :           state = s_req_first_http_minor;
     983           0 :           break;
     984             :         }
     985             : 
     986           0 :         if (ch < '0' || ch > '9') goto error;
     987             : 
     988           0 :         parser->http_major *= 10;
     989           0 :         parser->http_major += ch - '0';
     990             : 
     991           0 :         if (parser->http_major > 999) goto error;
     992           0 :         break;
     993             :       }
     994             : 
     995             :       /* first digit of minor HTTP version */
     996             :       case s_req_first_http_minor:
     997           0 :         if (ch < '0' || ch > '9') goto error;
     998           0 :         parser->http_minor = ch - '0';
     999           0 :         state = s_req_http_minor;
    1000           0 :         break;
    1001             : 
    1002             :       /* minor HTTP version or end of request line */
    1003             :       case s_req_http_minor:
    1004             :       {
    1005           0 :         if (ch == CR) {
    1006           0 :           state = s_req_line_almost_done;
    1007           0 :           break;
    1008             :         }
    1009             : 
    1010           0 :         if (ch == LF) {
    1011           0 :           state = s_header_field_start;
    1012           0 :           break;
    1013             :         }
    1014             : 
    1015             :         /* XXX allow spaces after digit? */
    1016             : 
    1017           0 :         if (ch < '0' || ch > '9') goto error;
    1018             : 
    1019           0 :         parser->http_minor *= 10;
    1020           0 :         parser->http_minor += ch - '0';
    1021             : 
    1022           0 :         if (parser->http_minor > 999) goto error;
    1023           0 :         break;
    1024             :       }
    1025             : 
    1026             :       /* end of request line */
    1027             :       case s_req_line_almost_done:
    1028             :       {
    1029           0 :         if (ch != LF) goto error;
    1030           0 :         state = s_header_field_start;
    1031           0 :         break;
    1032             :       }
    1033             : 
    1034             :       case s_header_field_start:
    1035             :       {
    1036           0 :         if (ch == CR) {
    1037           0 :           state = s_headers_almost_done;
    1038           0 :           break;
    1039             :         }
    1040             : 
    1041           0 :         if (ch == LF) {
    1042             :           /* they might be just sending \n instead of \r\n so this would be
    1043             :            * the second \n to denote the end of headers*/
    1044           0 :           state = s_headers_almost_done;
    1045           0 :           goto headers_almost_done;
    1046             :         }
    1047             : 
    1048           0 :         c = TOKEN(ch);
    1049             : 
    1050           0 :         if (!c) goto error;
    1051             : 
    1052           0 :         MARK(header_field);
    1053             : 
    1054           0 :         index = 0;
    1055           0 :         state = s_header_field;
    1056             : 
    1057           0 :         switch (c) {
    1058             :           case 'c':
    1059           0 :             header_state = h_C;
    1060           0 :             break;
    1061             : 
    1062             :           case 'p':
    1063           0 :             header_state = h_matching_proxy_connection;
    1064           0 :             break;
    1065             : 
    1066             :           case 't':
    1067           0 :             header_state = h_matching_transfer_encoding;
    1068           0 :             break;
    1069             : 
    1070             :           case 'u':
    1071           0 :             header_state = h_matching_upgrade;
    1072           0 :             break;
    1073             : 
    1074             :           default:
    1075           0 :             header_state = h_general;
    1076             :             break;
    1077             :         }
    1078           0 :         break;
    1079             :       }
    1080             : 
    1081             :       case s_header_field:
    1082             :       {
    1083           0 :         c = TOKEN(ch);
    1084             : 
    1085           0 :         if (c) {
    1086           0 :           switch (header_state) {
    1087             :             case h_general:
    1088           0 :               break;
    1089             : 
    1090             :             case h_C:
    1091           0 :               index++;
    1092           0 :               header_state = (c == 'o' ? h_CO : h_general);
    1093           0 :               break;
    1094             : 
    1095             :             case h_CO:
    1096           0 :               index++;
    1097           0 :               header_state = (c == 'n' ? h_CON : h_general);
    1098           0 :               break;
    1099             : 
    1100             :             case h_CON:
    1101           0 :               index++;
    1102           0 :               switch (c) {
    1103             :                 case 'n':
    1104           0 :                   header_state = h_matching_connection;
    1105           0 :                   break;
    1106             :                 case 't':
    1107           0 :                   header_state = h_matching_content_length;
    1108           0 :                   break;
    1109             :                 default:
    1110           0 :                   header_state = h_general;
    1111             :                   break;
    1112             :               }
    1113           0 :               break;
    1114             : 
    1115             :             /* connection */
    1116             : 
    1117             :             case h_matching_connection:
    1118           0 :               index++;
    1119           0 :               if (index > sizeof(CONNECTION)-1
    1120           0 :                   || c != CONNECTION[index]) {
    1121           0 :                 header_state = h_general;
    1122           0 :               } else if (index == sizeof(CONNECTION)-2) {
    1123           0 :                 header_state = h_connection;
    1124             :               }
    1125           0 :               break;
    1126             : 
    1127             :             /* proxy-connection */
    1128             : 
    1129             :             case h_matching_proxy_connection:
    1130           0 :               index++;
    1131           0 :               if (index > sizeof(PROXY_CONNECTION)-1
    1132           0 :                   || c != PROXY_CONNECTION[index]) {
    1133           0 :                 header_state = h_general;
    1134           0 :               } else if (index == sizeof(PROXY_CONNECTION)-2) {
    1135           0 :                 header_state = h_connection;
    1136             :               }
    1137           0 :               break;
    1138             : 
    1139             :             /* content-length */
    1140             : 
    1141             :             case h_matching_content_length:
    1142           0 :               index++;
    1143           0 :               if (index > sizeof(CONTENT_LENGTH)-1
    1144           0 :                   || c != CONTENT_LENGTH[index]) {
    1145           0 :                 header_state = h_general;
    1146           0 :               } else if (index == sizeof(CONTENT_LENGTH)-2) {
    1147           0 :                 header_state = h_content_length;
    1148             :               }
    1149           0 :               break;
    1150             : 
    1151             :             /* transfer-encoding */
    1152             : 
    1153             :             case h_matching_transfer_encoding:
    1154           0 :               index++;
    1155           0 :               if (index > sizeof(TRANSFER_ENCODING)-1
    1156           0 :                   || c != TRANSFER_ENCODING[index]) {
    1157           0 :                 header_state = h_general;
    1158           0 :               } else if (index == sizeof(TRANSFER_ENCODING)-2) {
    1159           0 :                 header_state = h_transfer_encoding;
    1160             :               }
    1161           0 :               break;
    1162             : 
    1163             :             /* upgrade */
    1164             : 
    1165             :             case h_matching_upgrade:
    1166           0 :               index++;
    1167           0 :               if (index > sizeof(UPGRADE)-1
    1168           0 :                   || c != UPGRADE[index]) {
    1169           0 :                 header_state = h_general;
    1170           0 :               } else if (index == sizeof(UPGRADE)-2) {
    1171           0 :                 header_state = h_upgrade;
    1172             :               }
    1173           0 :               break;
    1174             : 
    1175             :             case h_connection:
    1176             :             case h_content_length:
    1177             :             case h_transfer_encoding:
    1178             :             case h_upgrade:
    1179           0 :               if (ch != ' ') header_state = h_general;
    1180           0 :               break;
    1181             : 
    1182             :             default:
    1183           0 :               assert(0 && "Unknown header_state");
    1184             :               break;
    1185             :           }
    1186           0 :           break;
    1187             :         }
    1188             : 
    1189           0 :         if (ch == ':') {
    1190           0 :           CALLBACK(header_field);
    1191           0 :           state = s_header_value_start;
    1192           0 :           break;
    1193             :         }
    1194             : 
    1195           0 :         if (ch == CR) {
    1196           0 :           state = s_header_almost_done;
    1197           0 :           CALLBACK(header_field);
    1198           0 :           break;
    1199             :         }
    1200             : 
    1201           0 :         if (ch == LF) {
    1202           0 :           CALLBACK(header_field);
    1203           0 :           state = s_header_field_start;
    1204           0 :           break;
    1205             :         }
    1206             : 
    1207           0 :         goto error;
    1208             :       }
    1209             : 
    1210             :       case s_header_value_start:
    1211             :       {
    1212           0 :         if (ch == ' ') break;
    1213             : 
    1214           0 :         MARK(header_value);
    1215             : 
    1216           0 :         state = s_header_value;
    1217           0 :         index = 0;
    1218             : 
    1219           0 :         c = LOWER(ch);
    1220             : 
    1221           0 :         if (ch == CR) {
    1222           0 :           CALLBACK(header_value);
    1223           0 :           header_state = h_general;
    1224           0 :           state = s_header_almost_done;
    1225           0 :           break;
    1226             :         }
    1227             : 
    1228           0 :         if (ch == LF) {
    1229           0 :           CALLBACK(header_value);
    1230           0 :           state = s_header_field_start;
    1231           0 :           break;
    1232             :         }
    1233             : 
    1234           0 :         switch (header_state) {
    1235             :           case h_upgrade:
    1236           0 :             parser->flags |= F_UPGRADE;
    1237           0 :             header_state = h_general;
    1238           0 :             break;
    1239             : 
    1240             :           case h_transfer_encoding:
    1241             :             /* looking for 'Transfer-Encoding: chunked' */
    1242           0 :             if ('c' == c) {
    1243           0 :               header_state = h_matching_transfer_encoding_chunked;
    1244             :             } else {
    1245           0 :               header_state = h_general;
    1246             :             }
    1247           0 :             break;
    1248             : 
    1249             :           case h_content_length:
    1250           0 :             if (ch < '0' || ch > '9') goto error;
    1251           0 :             parser->content_length = ch - '0';
    1252           0 :             break;
    1253             : 
    1254             :           case h_connection:
    1255             :             /* looking for 'Connection: keep-alive' */
    1256           0 :             if (c == 'k') {
    1257           0 :               header_state = h_matching_connection_keep_alive;
    1258             :             /* looking for 'Connection: close' */
    1259           0 :             } else if (c == 'c') {
    1260           0 :               header_state = h_matching_connection_close;
    1261             :             } else {
    1262           0 :               header_state = h_general;
    1263             :             }
    1264           0 :             break;
    1265             : 
    1266             :           default:
    1267           0 :             header_state = h_general;
    1268             :             break;
    1269             :         }
    1270           0 :         break;
    1271             :       }
    1272             : 
    1273             :       case s_header_value:
    1274             :       {
    1275           0 :         c = LOWER(ch);
    1276             : 
    1277           0 :         if (ch == CR) {
    1278           0 :           CALLBACK(header_value);
    1279           0 :           state = s_header_almost_done;
    1280           0 :           break;
    1281             :         }
    1282             : 
    1283           0 :         if (ch == LF) {
    1284           0 :           CALLBACK(header_value);
    1285           0 :           goto header_almost_done;
    1286             :         }
    1287             : 
    1288           0 :         switch (header_state) {
    1289             :           case h_general:
    1290           0 :             break;
    1291             : 
    1292             :           case h_connection:
    1293             :           case h_transfer_encoding:
    1294           0 :             assert(0 && "Shouldn't get here.");
    1295             :             break;
    1296             : 
    1297             :           case h_content_length:
    1298           0 :             if (ch == ' ') break;
    1299           0 :             if (ch < '0' || ch > '9') goto error;
    1300           0 :             parser->content_length *= 10;
    1301           0 :             parser->content_length += ch - '0';
    1302           0 :             break;
    1303             : 
    1304             :           /* Transfer-Encoding: chunked */
    1305             :           case h_matching_transfer_encoding_chunked:
    1306           0 :             index++;
    1307           0 :             if (index > sizeof(CHUNKED)-1
    1308           0 :                 || c != CHUNKED[index]) {
    1309           0 :               header_state = h_general;
    1310           0 :             } else if (index == sizeof(CHUNKED)-2) {
    1311           0 :               header_state = h_transfer_encoding_chunked;
    1312             :             }
    1313           0 :             break;
    1314             : 
    1315             :           /* looking for 'Connection: keep-alive' */
    1316             :           case h_matching_connection_keep_alive:
    1317           0 :             index++;
    1318           0 :             if (index > sizeof(KEEP_ALIVE)-1
    1319           0 :                 || c != KEEP_ALIVE[index]) {
    1320           0 :               header_state = h_general;
    1321           0 :             } else if (index == sizeof(KEEP_ALIVE)-2) {
    1322           0 :               header_state = h_connection_keep_alive;
    1323             :             }
    1324           0 :             break;
    1325             : 
    1326             :           /* looking for 'Connection: close' */
    1327             :           case h_matching_connection_close:
    1328           0 :             index++;
    1329           0 :             if (index > sizeof(CLOSE)-1 || c != CLOSE[index]) {
    1330           0 :               header_state = h_general;
    1331           0 :             } else if (index == sizeof(CLOSE)-2) {
    1332           0 :               header_state = h_connection_close;
    1333             :             }
    1334           0 :             break;
    1335             : 
    1336             :           case h_transfer_encoding_chunked:
    1337             :           case h_connection_keep_alive:
    1338             :           case h_connection_close:
    1339           0 :             if (ch != ' ') header_state = h_general;
    1340           0 :             break;
    1341             : 
    1342             :           default:
    1343           0 :             state = s_header_value;
    1344           0 :             header_state = h_general;
    1345             :             break;
    1346             :         }
    1347           0 :         break;
    1348             :       }
    1349             : 
    1350             :       case s_header_almost_done:
    1351             :       header_almost_done:
    1352             :       {
    1353             :         STRICT_CHECK(ch != LF);
    1354             : 
    1355           0 :         state = s_header_field_start;
    1356             : 
    1357           0 :         switch (header_state) {
    1358             :           case h_connection_keep_alive:
    1359           0 :             parser->flags |= F_CONNECTION_KEEP_ALIVE;
    1360           0 :             break;
    1361             :           case h_connection_close:
    1362           0 :             parser->flags |= F_CONNECTION_CLOSE;
    1363           0 :             break;
    1364             :           case h_transfer_encoding_chunked:
    1365           0 :             parser->flags |= F_CHUNKED;
    1366             :             break;
    1367             :           default:
    1368             :             break;
    1369             :         }
    1370           0 :         break;
    1371             :       }
    1372             : 
    1373             :       case s_headers_almost_done:
    1374             :       headers_almost_done:
    1375             :       {
    1376             :         STRICT_CHECK(ch != LF);
    1377             : 
    1378           0 :         if (parser->flags & F_TRAILING) {
    1379             :           /* End of a chunked request */
    1380           0 :           CALLBACK2(message_complete);
    1381           0 :           state = NEW_MESSAGE();
    1382           0 :           break;
    1383             :         }
    1384             : 
    1385           0 :         nread = 0;
    1386             : 
    1387           0 :         if (parser->flags & F_UPGRADE || parser->method == PHP_HTTP_CONNECT) {
    1388           0 :           parser->upgrade = 1;
    1389             :         }
    1390             : 
    1391             :         /* Here we call the headers_complete callback. This is somewhat
    1392             :          * different than other callbacks because if the user returns 1, we
    1393             :          * will interpret that as saying that this message has no body. This
    1394             :          * is needed for the annoying case of receiving a response to a HEAD
    1395             :          * request.
    1396             :          */
    1397           0 :         if (settings->on_headers_complete) {
    1398           0 :           switch (settings->on_headers_complete(parser)) {
    1399             :             case 0:
    1400           0 :               break;
    1401             : 
    1402             :             case 1:
    1403           0 :               parser->flags |= F_SKIPBODY;
    1404           0 :               break;
    1405             : 
    1406             :             default:
    1407           0 :               return p - data; /* Error */
    1408             :           }
    1409             :         }
    1410             : 
    1411             :         /* Exit, the rest of the connect is in a different protocol. */
    1412           0 :         if (parser->upgrade) {
    1413           0 :           CALLBACK2(message_complete);
    1414           0 :           return (p - data);
    1415             :         }
    1416             : 
    1417           0 :         if (parser->flags & F_SKIPBODY) {
    1418           0 :           CALLBACK2(message_complete);
    1419           0 :           state = NEW_MESSAGE();
    1420           0 :         } else if (parser->flags & F_CHUNKED) {
    1421             :           /* chunked encoding - ignore Content-Length header */
    1422           0 :           state = s_chunk_size_start;
    1423             :         } else {
    1424           0 :           if (parser->content_length == 0) {
    1425             :             /* Content-Length header given but zero: Content-Length: 0\r\n */
    1426           0 :             CALLBACK2(message_complete);
    1427           0 :             state = NEW_MESSAGE();
    1428           0 :           } else if (parser->content_length > 0) {
    1429             :             /* Content-Length header given and non-zero */
    1430           0 :             state = s_body_identity;
    1431             :           } else {
    1432           0 :             if (parser->type == PHP_HTTP_REQUEST || php_http_should_keep_alive(parser)) {
    1433             :               /* Assume content-length 0 - read the next */
    1434           0 :               CALLBACK2(message_complete);
    1435           0 :               state = NEW_MESSAGE();
    1436             :             } else {
    1437             :               /* Read body until EOF */
    1438           0 :               state = s_body_identity_eof;
    1439             :             }
    1440             :           }
    1441             :         }
    1442             : 
    1443           0 :         break;
    1444             :       }
    1445             : 
    1446             :       case s_body_identity:
    1447           0 :         assert(pe >= p);
    1448             : 
    1449           0 :         to_read = MIN((size_t)(pe - p), (size_t)parser->content_length);
    1450           0 :         if (to_read > 0) {
    1451           0 :           if (settings->on_body) settings->on_body(parser, p, to_read);
    1452           0 :           p += to_read - 1;
    1453           0 :           parser->content_length -= to_read;
    1454           0 :           if (parser->content_length == 0) {
    1455           0 :             CALLBACK2(message_complete);
    1456           0 :             state = NEW_MESSAGE();
    1457             :           }
    1458             :         }
    1459           0 :         break;
    1460             : 
    1461             :       /* read until EOF */
    1462             :       case s_body_identity_eof:
    1463           0 :         to_read = pe - p;
    1464           0 :         if (to_read > 0) {
    1465           0 :           if (settings->on_body) settings->on_body(parser, p, to_read);
    1466           0 :           p += to_read - 1;
    1467             :         }
    1468           0 :         break;
    1469             : 
    1470             :       case s_chunk_size_start:
    1471             :       {
    1472           0 :         assert(parser->flags & F_CHUNKED);
    1473             : 
    1474           0 :         c = unhex[(unsigned char)ch];
    1475           0 :         if (c == -1) goto error;
    1476           0 :         parser->content_length = c;
    1477           0 :         state = s_chunk_size;
    1478           0 :         break;
    1479             :       }
    1480             : 
    1481             :       case s_chunk_size:
    1482             :       {
    1483           0 :         assert(parser->flags & F_CHUNKED);
    1484             : 
    1485           0 :         if (ch == CR) {
    1486           0 :           state = s_chunk_size_almost_done;
    1487           0 :           break;
    1488             :         }
    1489             : 
    1490           0 :         c = unhex[(unsigned char)ch];
    1491             : 
    1492           0 :         if (c == -1) {
    1493           0 :           if (ch == ';' || ch == ' ') {
    1494           0 :             state = s_chunk_parameters;
    1495           0 :             break;
    1496             :           }
    1497           0 :           goto error;
    1498             :         }
    1499             : 
    1500           0 :         parser->content_length *= 16;
    1501           0 :         parser->content_length += c;
    1502           0 :         break;
    1503             :       }
    1504             : 
    1505             :       case s_chunk_parameters:
    1506             :       {
    1507           0 :         assert(parser->flags & F_CHUNKED);
    1508             :         /* just ignore this shit. TODO check for overflow */
    1509           0 :         if (ch == CR) {
    1510           0 :           state = s_chunk_size_almost_done;
    1511           0 :           break;
    1512             :         }
    1513           0 :         break;
    1514             :       }
    1515             : 
    1516             :       case s_chunk_size_almost_done:
    1517             :       {
    1518           0 :         assert(parser->flags & F_CHUNKED);
    1519             :         STRICT_CHECK(ch != LF);
    1520             : 
    1521           0 :         if (parser->content_length == 0) {
    1522           0 :           parser->flags |= F_TRAILING;
    1523           0 :           state = s_header_field_start;
    1524             :         } else {
    1525           0 :           state = s_chunk_data;
    1526             :         }
    1527           0 :         break;
    1528             :       }
    1529             : 
    1530             :       case s_chunk_data:
    1531             :       {
    1532           0 :         assert(parser->flags & F_CHUNKED);
    1533           0 :         assert(pe >= p);
    1534             : 
    1535           0 :         to_read = MIN((size_t)(pe - p), (size_t)(parser->content_length));
    1536             : 
    1537           0 :         if (to_read > 0) {
    1538           0 :           if (settings->on_body) settings->on_body(parser, p, to_read);
    1539           0 :           p += to_read - 1;
    1540             :         }
    1541             : 
    1542           0 :         if (to_read == (size_t)parser->content_length) {
    1543           0 :           state = s_chunk_data_almost_done;
    1544             :         }
    1545             : 
    1546           0 :         parser->content_length -= to_read;
    1547           0 :         break;
    1548             :       }
    1549             : 
    1550             :       case s_chunk_data_almost_done:
    1551           0 :         assert(parser->flags & F_CHUNKED);
    1552             :         STRICT_CHECK(ch != CR);
    1553           0 :         state = s_chunk_data_done;
    1554           0 :         break;
    1555             : 
    1556             :       case s_chunk_data_done:
    1557           0 :         assert(parser->flags & F_CHUNKED);
    1558             :         STRICT_CHECK(ch != LF);
    1559           0 :         state = s_chunk_size_start;
    1560           0 :         break;
    1561             : 
    1562             :       default:
    1563           0 :         assert(0 && "unhandled state");
    1564             :         goto error;
    1565             :     }
    1566             :   }
    1567             : 
    1568           0 :   CALLBACK_NOCLEAR(header_field);
    1569           0 :   CALLBACK_NOCLEAR(header_value);
    1570           0 :   CALLBACK_NOCLEAR(fragment);
    1571           0 :   CALLBACK_NOCLEAR(query_string);
    1572           0 :   CALLBACK_NOCLEAR(path);
    1573           0 :   CALLBACK_NOCLEAR(url);
    1574             : 
    1575           0 :   parser->state = state;
    1576           0 :   parser->header_state = header_state;
    1577           0 :   parser->index = index;
    1578           0 :   parser->nread = nread;
    1579             : 
    1580           0 :   return len;
    1581             : 
    1582             : error:
    1583           0 :   parser->state = s_dead;
    1584           0 :   return (p - data);
    1585             : }
    1586             : 
    1587             : 
    1588             : int
    1589           0 : php_http_should_keep_alive (php_http_parser *parser)
    1590             : {
    1591           0 :   if (parser->http_major > 0 && parser->http_minor > 0) {
    1592             :     /* HTTP/1.1 */
    1593           0 :     if (parser->flags & F_CONNECTION_CLOSE) {
    1594           0 :       return 0;
    1595             :     } else {
    1596           0 :       return 1;
    1597             :     }
    1598             :   } else {
    1599             :     /* HTTP/1.0 or earlier */
    1600           0 :     if (parser->flags & F_CONNECTION_KEEP_ALIVE) {
    1601           0 :       return 1;
    1602             :     } else {
    1603           0 :       return 0;
    1604             :     }
    1605             :   }
    1606             : }
    1607             : 
    1608             : 
    1609           0 : const char * php_http_method_str (enum php_http_method m)
    1610             : {
    1611           0 :   return method_strings[m];
    1612             : }
    1613             : 
    1614             : 
    1615             : void
    1616           0 : php_http_parser_init (php_http_parser *parser, enum php_http_parser_type t)
    1617             : {
    1618           0 :   parser->type = t;
    1619           0 :   parser->state = (t == PHP_HTTP_REQUEST ? s_start_req : (t == PHP_HTTP_RESPONSE ? s_start_res : s_start_req_or_res));
    1620           0 :   parser->nread = 0;
    1621           0 :   parser->upgrade = 0;
    1622           0 :   parser->flags = 0;
    1623           0 :   parser->method = 0;
    1624           0 : }

Generated by: LCOV version 1.10

Generated at Wed, 31 Aug 2016 04:11:20 +0000 (7 minutes ago)

Copyright © 2005-2016 The PHP Group
All rights reserved.