root/sapi/cli/php_http_parser.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. php_http_parser_execute
  2. php_http_should_keep_alive
  3. php_http_method_str
  4. php_http_parser_init

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

/* [<][>][^][v][top][bottom][index][help] */