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   , "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 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   const char *p = data, *pe;
 333   size_t to_read;
 334 
 335   enum state state = (enum state) parser->state;
 336   enum header_states header_state = (enum header_states) parser->header_state;
 337   uint32_t index = parser->index;
 338   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   const char *header_field_mark = 0;
 344   const char *header_value_mark = 0;
 345   const char *fragment_mark = 0;
 346   const char *query_string_mark = 0;
 347   const char *path_mark = 0;
 348   const char *url_mark = 0;
 349 
 350   if (len == 0) {
 351     if (state == s_body_identity_eof) {
 352       CALLBACK2(message_complete);
 353     }
 354     return 0;
 355   }
 356 
 357   if (state == s_header_field)
 358     header_field_mark = data;
 359   if (state == s_header_value)
 360     header_value_mark = data;
 361   if (state == s_req_fragment)
 362     fragment_mark = data;
 363   if (state == s_req_query_string)
 364     query_string_mark = data;
 365   if (state == s_req_path)
 366     path_mark = data;
 367   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     url_mark = data;
 373 
 374   for (p=data, pe=data+len; p != pe; p++) {
 375     ch = *p;
 376 
 377     if (PARSING_HEADER(state)) {
 378       ++nread;
 379       /* Buffer overflow attack */
 380       if (nread > PHP_HTTP_MAX_HEADER_SIZE) goto error;
 381     }
 382 
 383     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         goto error;
 390 
 391       case s_start_req_or_res:
 392       {
 393         if (ch == CR || ch == LF)
 394           break;
 395         parser->flags = 0;
 396         parser->content_length = -1;
 397 
 398         CALLBACK2(message_begin);
 399 
 400         if (ch == 'H')
 401           state = s_res_or_resp_H;
 402         else {
 403           parser->type = PHP_HTTP_REQUEST;
 404           goto start_req_method_assign;
 405         }
 406         break;
 407       }
 408 
 409       case s_res_or_resp_H:
 410         if (ch == 'T') {
 411           parser->type = PHP_HTTP_RESPONSE;
 412           state = s_res_HT;
 413         } else {
 414           if (ch != 'E') goto error;
 415           parser->type = PHP_HTTP_REQUEST;
 416           parser->method = PHP_HTTP_HEAD;
 417           index = 2;
 418           state = s_req_method;
 419         }
 420         break;
 421 
 422       case s_start_res:
 423       {
 424         parser->flags = 0;
 425         parser->content_length = -1;
 426 
 427         CALLBACK2(message_begin);
 428 
 429         switch (ch) {
 430           case 'H':
 431             state = s_res_H;
 432             break;
 433 
 434           case CR:
 435           case LF:
 436             break;
 437 
 438           default:
 439             goto error;
 440         }
 441         break;
 442       }
 443 
 444       case s_res_H:
 445         STRICT_CHECK(ch != 'T');
 446         state = s_res_HT;
 447         break;
 448 
 449       case s_res_HT:
 450         STRICT_CHECK(ch != 'T');
 451         state = s_res_HTT;
 452         break;
 453 
 454       case s_res_HTT:
 455         STRICT_CHECK(ch != 'P');
 456         state = s_res_HTTP;
 457         break;
 458 
 459       case s_res_HTTP:
 460         STRICT_CHECK(ch != '/');
 461         state = s_res_first_http_major;
 462         break;
 463 
 464       case s_res_first_http_major:
 465         if (ch < '1' || ch > '9') goto error;
 466         parser->http_major = ch - '0';
 467         state = s_res_http_major;
 468         break;
 469 
 470       /* major HTTP version or dot */
 471       case s_res_http_major:
 472       {
 473         if (ch == '.') {
 474           state = s_res_first_http_minor;
 475           break;
 476         }
 477 
 478         if (ch < '0' || ch > '9') goto error;
 479 
 480         parser->http_major *= 10;
 481         parser->http_major += ch - '0';
 482 
 483         if (parser->http_major > 999) goto error;
 484         break;
 485       }
 486 
 487       /* first digit of minor HTTP version */
 488       case s_res_first_http_minor:
 489         if (ch < '0' || ch > '9') goto error;
 490         parser->http_minor = ch - '0';
 491         state = s_res_http_minor;
 492         break;
 493 
 494       /* minor HTTP version or end of request line */
 495       case s_res_http_minor:
 496       {
 497         if (ch == ' ') {
 498           state = s_res_first_status_code;
 499           break;
 500         }
 501 
 502         if (ch < '0' || ch > '9') goto error;
 503 
 504         parser->http_minor *= 10;
 505         parser->http_minor += ch - '0';
 506 
 507         if (parser->http_minor > 999) goto error;
 508         break;
 509       }
 510 
 511       case s_res_first_status_code:
 512       {
 513         if (ch < '0' || ch > '9') {
 514           if (ch == ' ') {
 515             break;
 516           }
 517           goto error;
 518         }
 519         parser->status_code = ch - '0';
 520         state = s_res_status_code;
 521         break;
 522       }
 523 
 524       case s_res_status_code:
 525       {
 526         if (ch < '0' || ch > '9') {
 527           switch (ch) {
 528             case ' ':
 529               state = s_res_status;
 530               break;
 531             case CR:
 532               state = s_res_line_almost_done;
 533               break;
 534             case LF:
 535               state = s_header_field_start;
 536               break;
 537             default:
 538               goto error;
 539           }
 540           break;
 541         }
 542 
 543         parser->status_code *= 10;
 544         parser->status_code += ch - '0';
 545 
 546         if (parser->status_code > 999) goto error;
 547         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         if (ch == CR) {
 554           state = s_res_line_almost_done;
 555           break;
 556         }
 557 
 558         if (ch == LF) {
 559           state = s_header_field_start;
 560           break;
 561         }
 562         break;
 563 
 564       case s_res_line_almost_done:
 565         STRICT_CHECK(ch != LF);
 566         state = s_header_field_start;
 567         break;
 568 
 569       case s_start_req:
 570       {
 571         if (ch == CR || ch == LF)
 572           break;
 573         parser->flags = 0;
 574         parser->content_length = -1;
 575 
 576         CALLBACK2(message_begin);
 577 
 578         if (ch < 'A' || 'Z' < ch) goto error;
 579 
 580       start_req_method_assign:
 581         parser->method = (enum php_http_method) 0;
 582         index = 1;
 583         switch (ch) {
 584           case 'C': parser->method = PHP_HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
 585           case 'D': parser->method = PHP_HTTP_DELETE; break;
 586           case 'G': parser->method = PHP_HTTP_GET; break;
 587           case 'H': parser->method = PHP_HTTP_HEAD; break;
 588           case 'L': parser->method = PHP_HTTP_LOCK; break;
 589           case 'M': parser->method = PHP_HTTP_MKCOL; /* or MOVE, MKCALENDAR, MKACTIVITY, MERGE, M-SEARCH */ break;
 590           case 'N': parser->method = PHP_HTTP_NOTIFY; break;
 591           case 'O': parser->method = PHP_HTTP_OPTIONS; break;
 592           case 'P': parser->method = PHP_HTTP_POST; /* or PROPFIND or PROPPATCH or PUT */ break;
 593           case 'R': parser->method = PHP_HTTP_REPORT; break;
 594           case 'S': parser->method = PHP_HTTP_SUBSCRIBE; /* or SEARCH */ break;
 595           case 'T': parser->method = PHP_HTTP_TRACE; break;
 596           case 'U': parser->method = PHP_HTTP_UNLOCK; /* or UNSUBSCRIBE */ break;
 597           default: parser->method = PHP_HTTP_NOT_IMPLEMENTED; break;
 598         }
 599         state = s_req_method;
 600         break;
 601       }
 602       case s_req_method:
 603       {
 604         const char *matcher;
 605         if (ch == '\0')
 606           goto error;
 607 
 608         matcher = method_strings[parser->method];
 609         if (ch == ' ') {
 610           if (parser->method != PHP_HTTP_NOT_IMPLEMENTED && matcher[index] != '\0') {
 611             parser->method = PHP_HTTP_NOT_IMPLEMENTED;
 612           }
 613           state = s_req_spaces_before_url;
 614         } else if (parser->method == PHP_HTTP_NOT_IMPLEMENTED || ch == matcher[index]) {
 615           ; /* nada */
 616         } else if (parser->method == PHP_HTTP_CONNECT) {
 617           if (index == 1 && ch == 'H') {
 618             parser->method = PHP_HTTP_CHECKOUT;
 619           } else if (index == 2  && ch == 'P') {
 620             parser->method = PHP_HTTP_COPY;
 621           } else {
 622             parser->method = PHP_HTTP_NOT_IMPLEMENTED;
 623           }
 624         } else if (parser->method == PHP_HTTP_MKCOL) {
 625           if (index == 1 && ch == 'O') {
 626             parser->method = PHP_HTTP_MOVE;
 627           } else if (index == 3 && ch == 'A') {
 628             parser->method = PHP_HTTP_MKCALENDAR;
 629           } else if (index == 1 && ch == 'E') {
 630             parser->method = PHP_HTTP_MERGE;
 631           } else if (index == 1 && ch == '-') {
 632             parser->method = PHP_HTTP_MSEARCH;
 633           } else if (index == 2 && ch == 'A') {
 634             parser->method = PHP_HTTP_MKACTIVITY;
 635           } else {
 636             parser->method = PHP_HTTP_NOT_IMPLEMENTED;
 637           }
 638         } else if (index == 1 && parser->method == PHP_HTTP_POST && ch == 'R') {
 639           parser->method = PHP_HTTP_PROPFIND; /* or HTTP_PROPPATCH */
 640         } else if (index == 1 && parser->method == PHP_HTTP_POST && ch == 'U') {
 641           parser->method = PHP_HTTP_PUT;
 642         } else if (index == 1 && parser->method == PHP_HTTP_POST && ch == 'A') {
 643           parser->method = PHP_HTTP_PATCH;
 644         } else if (index == 1 && parser->method == PHP_HTTP_SUBSCRIBE && ch == 'E') {
 645           parser->method = PHP_HTTP_SEARCH;
 646         } else if (index == 2 && parser->method == PHP_HTTP_UNLOCK && ch == 'S') {
 647           parser->method = PHP_HTTP_UNSUBSCRIBE;
 648         } else if (index == 4 && parser->method == PHP_HTTP_PROPFIND && ch == 'P') {
 649           parser->method = PHP_HTTP_PROPPATCH;
 650         } else {
 651           parser->method = PHP_HTTP_NOT_IMPLEMENTED;
 652         }
 653 
 654         ++index;
 655         break;
 656       }
 657       case s_req_spaces_before_url:
 658       {
 659         if (ch == ' ') break;
 660 
 661         if (ch == '/' || ch == '*') {
 662           MARK(url);
 663           MARK(path);
 664           state = s_req_path;
 665           break;
 666         }
 667 
 668         c = LOWER(ch);
 669 
 670         if (c >= 'a' && c <= 'z') {
 671           MARK(url);
 672           state = s_req_schema;
 673           break;
 674         }
 675 
 676         goto error;
 677       }
 678 
 679       case s_req_schema:
 680       {
 681         c = LOWER(ch);
 682 
 683         if (c >= 'a' && c <= 'z') break;
 684 
 685         if (ch == ':') {
 686           state = s_req_schema_slash;
 687           break;
 688         } else if (ch == '.') {
 689           state = s_req_host;
 690           break;
 691         } else if ('0' <= ch && ch <= '9') {
 692           state = s_req_host;
 693           break;
 694         }
 695 
 696         goto error;
 697       }
 698 
 699       case s_req_schema_slash:
 700         STRICT_CHECK(ch != '/');
 701         state = s_req_schema_slash_slash;
 702         break;
 703 
 704       case s_req_schema_slash_slash:
 705         STRICT_CHECK(ch != '/');
 706         state = s_req_host;
 707         break;
 708 
 709       case s_req_host:
 710       {
 711         c = LOWER(ch);
 712         if (c >= 'a' && c <= 'z') break;
 713         if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') break;
 714         switch (ch) {
 715           case ':':
 716             state = s_req_port;
 717             break;
 718           case '/':
 719             MARK(path);
 720             state = s_req_path;
 721             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             CALLBACK(url);
 728             state = s_req_http_start;
 729             break;
 730           default:
 731             goto error;
 732         }
 733         break;
 734       }
 735 
 736       case s_req_port:
 737       {
 738         if (ch >= '0' && ch <= '9') break;
 739         switch (ch) {
 740           case '/':
 741             MARK(path);
 742             state = s_req_path;
 743             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             CALLBACK(url);
 750             state = s_req_http_start;
 751             break;
 752           default:
 753             goto error;
 754         }
 755         break;
 756       }
 757 
 758       case s_req_path:
 759       {
 760         if (normal_url_char[(unsigned char)ch]) break;
 761 
 762         switch (ch) {
 763           case ' ':
 764             CALLBACK(url);
 765             CALLBACK(path);
 766             state = s_req_http_start;
 767             break;
 768           case CR:
 769             CALLBACK(url);
 770             CALLBACK(path);
 771             parser->http_major = 0;
 772             parser->http_minor = 9;
 773             state = s_req_line_almost_done;
 774             break;
 775           case LF:
 776             CALLBACK(url);
 777             CALLBACK(path);
 778             parser->http_major = 0;
 779             parser->http_minor = 9;
 780             state = s_header_field_start;
 781             break;
 782           case '?':
 783             CALLBACK(path);
 784             state = s_req_query_string_start;
 785             break;
 786           case '#':
 787             CALLBACK(path);
 788             state = s_req_fragment_start;
 789             break;
 790           default:
 791             goto error;
 792         }
 793         break;
 794       }
 795 
 796       case s_req_query_string_start:
 797       {
 798         if (normal_url_char[(unsigned char)ch]) {
 799           MARK(query_string);
 800           state = s_req_query_string;
 801           break;
 802         }
 803 
 804         switch (ch) {
 805           case '?':
 806             break; /* XXX ignore extra '?' ... is this right? */
 807           case ' ':
 808             CALLBACK(url);
 809             state = s_req_http_start;
 810             break;
 811           case CR:
 812             CALLBACK(url);
 813             parser->http_major = 0;
 814             parser->http_minor = 9;
 815             state = s_req_line_almost_done;
 816             break;
 817           case LF:
 818             CALLBACK(url);
 819             parser->http_major = 0;
 820             parser->http_minor = 9;
 821             state = s_header_field_start;
 822             break;
 823           case '#':
 824             state = s_req_fragment_start;
 825             break;
 826           default:
 827             goto error;
 828         }
 829         break;
 830       }
 831 
 832       case s_req_query_string:
 833       {
 834         if (normal_url_char[(unsigned char)ch]) break;
 835 
 836         switch (ch) {
 837           case '?':
 838             /* allow extra '?' in query string */
 839             break;
 840           case ' ':
 841             CALLBACK(url);
 842             CALLBACK(query_string);
 843             state = s_req_http_start;
 844             break;
 845           case CR:
 846             CALLBACK(url);
 847             CALLBACK(query_string);
 848             parser->http_major = 0;
 849             parser->http_minor = 9;
 850             state = s_req_line_almost_done;
 851             break;
 852           case LF:
 853             CALLBACK(url);
 854             CALLBACK(query_string);
 855             parser->http_major = 0;
 856             parser->http_minor = 9;
 857             state = s_header_field_start;
 858             break;
 859           case '#':
 860             CALLBACK(query_string);
 861             state = s_req_fragment_start;
 862             break;
 863           default:
 864             goto error;
 865         }
 866         break;
 867       }
 868 
 869       case s_req_fragment_start:
 870       {
 871         if (normal_url_char[(unsigned char)ch]) {
 872           MARK(fragment);
 873           state = s_req_fragment;
 874           break;
 875         }
 876 
 877         switch (ch) {
 878           case ' ':
 879             CALLBACK(url);
 880             state = s_req_http_start;
 881             break;
 882           case CR:
 883             CALLBACK(url);
 884             parser->http_major = 0;
 885             parser->http_minor = 9;
 886             state = s_req_line_almost_done;
 887             break;
 888           case LF:
 889             CALLBACK(url);
 890             parser->http_major = 0;
 891             parser->http_minor = 9;
 892             state = s_header_field_start;
 893             break;
 894           case '?':
 895             MARK(fragment);
 896             state = s_req_fragment;
 897             break;
 898           case '#':
 899             break;
 900           default:
 901             goto error;
 902         }
 903         break;
 904       }
 905 
 906       case s_req_fragment:
 907       {
 908         if (normal_url_char[(unsigned char)ch]) break;
 909 
 910         switch (ch) {
 911           case ' ':
 912             CALLBACK(url);
 913             CALLBACK(fragment);
 914             state = s_req_http_start;
 915             break;
 916           case CR:
 917             CALLBACK(url);
 918             CALLBACK(fragment);
 919             parser->http_major = 0;
 920             parser->http_minor = 9;
 921             state = s_req_line_almost_done;
 922             break;
 923           case LF:
 924             CALLBACK(url);
 925             CALLBACK(fragment);
 926             parser->http_major = 0;
 927             parser->http_minor = 9;
 928             state = s_header_field_start;
 929             break;
 930           case '?':
 931           case '#':
 932             break;
 933           default:
 934             goto error;
 935         }
 936         break;
 937       }
 938 
 939       case s_req_http_start:
 940         switch (ch) {
 941           case 'H':
 942             state = s_req_http_H;
 943             break;
 944           case ' ':
 945             break;
 946           default:
 947             goto error;
 948         }
 949         break;
 950 
 951       case s_req_http_H:
 952         STRICT_CHECK(ch != 'T');
 953         state = s_req_http_HT;
 954         break;
 955 
 956       case s_req_http_HT:
 957         STRICT_CHECK(ch != 'T');
 958         state = s_req_http_HTT;
 959         break;
 960 
 961       case s_req_http_HTT:
 962         STRICT_CHECK(ch != 'P');
 963         state = s_req_http_HTTP;
 964         break;
 965 
 966       case s_req_http_HTTP:
 967         STRICT_CHECK(ch != '/');
 968         state = s_req_first_http_major;
 969         break;
 970 
 971       /* first digit of major HTTP version */
 972       case s_req_first_http_major:
 973         if (ch < '1' || ch > '9') goto error;
 974         parser->http_major = ch - '0';
 975         state = s_req_http_major;
 976         break;
 977 
 978       /* major HTTP version or dot */
 979       case s_req_http_major:
 980       {
 981         if (ch == '.') {
 982           state = s_req_first_http_minor;
 983           break;
 984         }
 985 
 986         if (ch < '0' || ch > '9') goto error;
 987 
 988         parser->http_major *= 10;
 989         parser->http_major += ch - '0';
 990 
 991         if (parser->http_major > 999) goto error;
 992         break;
 993       }
 994 
 995       /* first digit of minor HTTP version */
 996       case s_req_first_http_minor:
 997         if (ch < '0' || ch > '9') goto error;
 998         parser->http_minor = ch - '0';
 999         state = s_req_http_minor;
1000         break;
1001 
1002       /* minor HTTP version or end of request line */
1003       case s_req_http_minor:
1004       {
1005         if (ch == CR) {
1006           state = s_req_line_almost_done;
1007           break;
1008         }
1009 
1010         if (ch == LF) {
1011           state = s_header_field_start;
1012           break;
1013         }
1014 
1015         /* XXX allow spaces after digit? */
1016 
1017         if (ch < '0' || ch > '9') goto error;
1018 
1019         parser->http_minor *= 10;
1020         parser->http_minor += ch - '0';
1021 
1022         if (parser->http_minor > 999) goto error;
1023         break;
1024       }
1025 
1026       /* end of request line */
1027       case s_req_line_almost_done:
1028       {
1029         if (ch != LF) goto error;
1030         state = s_header_field_start;
1031         break;
1032       }
1033 
1034       case s_header_field_start:
1035       {
1036         if (ch == CR) {
1037           state = s_headers_almost_done;
1038           break;
1039         }
1040 
1041         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           state = s_headers_almost_done;
1045           goto headers_almost_done;
1046         }
1047 
1048         c = TOKEN(ch);
1049 
1050         if (!c) goto error;
1051 
1052         MARK(header_field);
1053 
1054         index = 0;
1055         state = s_header_field;
1056 
1057         switch (c) {
1058           case 'c':
1059             header_state = h_C;
1060             break;
1061 
1062           case 'p':
1063             header_state = h_matching_proxy_connection;
1064             break;
1065 
1066           case 't':
1067             header_state = h_matching_transfer_encoding;
1068             break;
1069 
1070           case 'u':
1071             header_state = h_matching_upgrade;
1072             break;
1073 
1074           default:
1075             header_state = h_general;
1076             break;
1077         }
1078         break;
1079       }
1080 
1081       case s_header_field:
1082       {
1083         c = TOKEN(ch);
1084 
1085         if (c) {
1086           switch (header_state) {
1087             case h_general:
1088               break;
1089 
1090             case h_C:
1091               index++;
1092               header_state = (c == 'o' ? h_CO : h_general);
1093               break;
1094 
1095             case h_CO:
1096               index++;
1097               header_state = (c == 'n' ? h_CON : h_general);
1098               break;
1099 
1100             case h_CON:
1101               index++;
1102               switch (c) {
1103                 case 'n':
1104                   header_state = h_matching_connection;
1105                   break;
1106                 case 't':
1107                   header_state = h_matching_content_length;
1108                   break;
1109                 default:
1110                   header_state = h_general;
1111                   break;
1112               }
1113               break;
1114 
1115             /* connection */
1116 
1117             case h_matching_connection:
1118               index++;
1119               if (index > sizeof(CONNECTION)-1
1120                   || c != CONNECTION[index]) {
1121                 header_state = h_general;
1122               } else if (index == sizeof(CONNECTION)-2) {
1123                 header_state = h_connection;
1124               }
1125               break;
1126 
1127             /* proxy-connection */
1128 
1129             case h_matching_proxy_connection:
1130               index++;
1131               if (index > sizeof(PROXY_CONNECTION)-1
1132                   || c != PROXY_CONNECTION[index]) {
1133                 header_state = h_general;
1134               } else if (index == sizeof(PROXY_CONNECTION)-2) {
1135                 header_state = h_connection;
1136               }
1137               break;
1138 
1139             /* content-length */
1140 
1141             case h_matching_content_length:
1142               index++;
1143               if (index > sizeof(CONTENT_LENGTH)-1
1144                   || c != CONTENT_LENGTH[index]) {
1145                 header_state = h_general;
1146               } else if (index == sizeof(CONTENT_LENGTH)-2) {
1147                 header_state = h_content_length;
1148               }
1149               break;
1150 
1151             /* transfer-encoding */
1152 
1153             case h_matching_transfer_encoding:
1154               index++;
1155               if (index > sizeof(TRANSFER_ENCODING)-1
1156                   || c != TRANSFER_ENCODING[index]) {
1157                 header_state = h_general;
1158               } else if (index == sizeof(TRANSFER_ENCODING)-2) {
1159                 header_state = h_transfer_encoding;
1160               }
1161               break;
1162 
1163             /* upgrade */
1164 
1165             case h_matching_upgrade:
1166               index++;
1167               if (index > sizeof(UPGRADE)-1
1168                   || c != UPGRADE[index]) {
1169                 header_state = h_general;
1170               } else if (index == sizeof(UPGRADE)-2) {
1171                 header_state = h_upgrade;
1172               }
1173               break;
1174 
1175             case h_connection:
1176             case h_content_length:
1177             case h_transfer_encoding:
1178             case h_upgrade:
1179               if (ch != ' ') header_state = h_general;
1180               break;
1181 
1182             default:
1183               assert(0 && "Unknown header_state");
1184               break;
1185           }
1186           break;
1187         }
1188 
1189         if (ch == ':') {
1190           CALLBACK(header_field);
1191           state = s_header_value_start;
1192           break;
1193         }
1194 
1195         if (ch == CR) {
1196           state = s_header_almost_done;
1197           CALLBACK(header_field);
1198           break;
1199         }
1200 
1201         if (ch == LF) {
1202           CALLBACK(header_field);
1203           state = s_header_field_start;
1204           break;
1205         }
1206 
1207         goto error;
1208       }
1209 
1210       case s_header_value_start:
1211       {
1212         if (ch == ' ') break;
1213 
1214         MARK(header_value);
1215 
1216         state = s_header_value;
1217         index = 0;
1218 
1219         c = LOWER(ch);
1220 
1221         if (ch == CR) {
1222           CALLBACK(header_value);
1223           header_state = h_general;
1224           state = s_header_almost_done;
1225           break;
1226         }
1227 
1228         if (ch == LF) {
1229           CALLBACK(header_value);
1230           state = s_header_field_start;
1231           break;
1232         }
1233 
1234         switch (header_state) {
1235           case h_upgrade:
1236             parser->flags |= F_UPGRADE;
1237             header_state = h_general;
1238             break;
1239 
1240           case h_transfer_encoding:
1241             /* looking for 'Transfer-Encoding: chunked' */
1242             if ('c' == c) {
1243               header_state = h_matching_transfer_encoding_chunked;
1244             } else {
1245               header_state = h_general;
1246             }
1247             break;
1248 
1249           case h_content_length:
1250             if (ch < '0' || ch > '9') goto error;
1251             parser->content_length = ch - '0';
1252             break;
1253 
1254           case h_connection:
1255             /* looking for 'Connection: keep-alive' */
1256             if (c == 'k') {
1257               header_state = h_matching_connection_keep_alive;
1258             /* looking for 'Connection: close' */
1259             } else if (c == 'c') {
1260               header_state = h_matching_connection_close;
1261             } else {
1262               header_state = h_general;
1263             }
1264             break;
1265 
1266           default:
1267             header_state = h_general;
1268             break;
1269         }
1270         break;
1271       }
1272 
1273       case s_header_value:
1274       {
1275         c = LOWER(ch);
1276 
1277         if (ch == CR) {
1278           CALLBACK(header_value);
1279           state = s_header_almost_done;
1280           break;
1281         }
1282 
1283         if (ch == LF) {
1284           CALLBACK(header_value);
1285           goto header_almost_done;
1286         }
1287 
1288         switch (header_state) {
1289           case h_general:
1290             break;
1291 
1292           case h_connection:
1293           case h_transfer_encoding:
1294             assert(0 && "Shouldn't get here.");
1295             break;
1296 
1297           case h_content_length:
1298             if (ch == ' ') break;
1299             if (ch < '0' || ch > '9') goto error;
1300             parser->content_length *= 10;
1301             parser->content_length += ch - '0';
1302             break;
1303 
1304           /* Transfer-Encoding: chunked */
1305           case h_matching_transfer_encoding_chunked:
1306             index++;
1307             if (index > sizeof(CHUNKED)-1
1308                 || c != CHUNKED[index]) {
1309               header_state = h_general;
1310             } else if (index == sizeof(CHUNKED)-2) {
1311               header_state = h_transfer_encoding_chunked;
1312             }
1313             break;
1314 
1315           /* looking for 'Connection: keep-alive' */
1316           case h_matching_connection_keep_alive:
1317             index++;
1318             if (index > sizeof(KEEP_ALIVE)-1
1319                 || c != KEEP_ALIVE[index]) {
1320               header_state = h_general;
1321             } else if (index == sizeof(KEEP_ALIVE)-2) {
1322               header_state = h_connection_keep_alive;
1323             }
1324             break;
1325 
1326           /* looking for 'Connection: close' */
1327           case h_matching_connection_close:
1328             index++;
1329             if (index > sizeof(CLOSE)-1 || c != CLOSE[index]) {
1330               header_state = h_general;
1331             } else if (index == sizeof(CLOSE)-2) {
1332               header_state = h_connection_close;
1333             }
1334             break;
1335 
1336           case h_transfer_encoding_chunked:
1337           case h_connection_keep_alive:
1338           case h_connection_close:
1339             if (ch != ' ') header_state = h_general;
1340             break;
1341 
1342           default:
1343             state = s_header_value;
1344             header_state = h_general;
1345             break;
1346         }
1347         break;
1348       }
1349 
1350       case s_header_almost_done:
1351       header_almost_done:
1352       {
1353         STRICT_CHECK(ch != LF);
1354 
1355         state = s_header_field_start;
1356 
1357         switch (header_state) {
1358           case h_connection_keep_alive:
1359             parser->flags |= F_CONNECTION_KEEP_ALIVE;
1360             break;
1361           case h_connection_close:
1362             parser->flags |= F_CONNECTION_CLOSE;
1363             break;
1364           case h_transfer_encoding_chunked:
1365             parser->flags |= F_CHUNKED;
1366             break;
1367           default:
1368             break;
1369         }
1370         break;
1371       }
1372 
1373       case s_headers_almost_done:
1374       headers_almost_done:
1375       {
1376         STRICT_CHECK(ch != LF);
1377 
1378         if (parser->flags & F_TRAILING) {
1379           /* End of a chunked request */
1380           CALLBACK2(message_complete);
1381           state = NEW_MESSAGE();
1382           break;
1383         }
1384 
1385         nread = 0;
1386 
1387         if (parser->flags & F_UPGRADE || parser->method == PHP_HTTP_CONNECT) {
1388           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         if (settings->on_headers_complete) {
1398           switch (settings->on_headers_complete(parser)) {
1399             case 0:
1400               break;
1401 
1402             case 1:
1403               parser->flags |= F_SKIPBODY;
1404               break;
1405 
1406             default:
1407               return p - data; /* Error */
1408           }
1409         }
1410 
1411         /* Exit, the rest of the connect is in a different protocol. */
1412         if (parser->upgrade) {
1413           CALLBACK2(message_complete);
1414           return (p - data);
1415         }
1416 
1417         if (parser->flags & F_SKIPBODY) {
1418           CALLBACK2(message_complete);
1419           state = NEW_MESSAGE();
1420         } else if (parser->flags & F_CHUNKED) {
1421           /* chunked encoding - ignore Content-Length header */
1422           state = s_chunk_size_start;
1423         } else {
1424           if (parser->content_length == 0) {
1425             /* Content-Length header given but zero: Content-Length: 0\r\n */
1426             CALLBACK2(message_complete);
1427             state = NEW_MESSAGE();
1428           } else if (parser->content_length > 0) {
1429             /* Content-Length header given and non-zero */
1430             state = s_body_identity;
1431           } else {
1432             if (parser->type == PHP_HTTP_REQUEST || php_http_should_keep_alive(parser)) {
1433               /* Assume content-length 0 - read the next */
1434               CALLBACK2(message_complete);
1435               state = NEW_MESSAGE();
1436             } else {
1437               /* Read body until EOF */
1438               state = s_body_identity_eof;
1439             }
1440           }
1441         }
1442 
1443         break;
1444       }
1445 
1446       case s_body_identity:
1447         assert(pe >= p);
1448 
1449         to_read = MIN((size_t)(pe - p), (size_t)parser->content_length);
1450         if (to_read > 0) {
1451           if (settings->on_body) settings->on_body(parser, p, to_read);
1452           p += to_read - 1;
1453           parser->content_length -= to_read;
1454           if (parser->content_length == 0) {
1455             CALLBACK2(message_complete);
1456             state = NEW_MESSAGE();
1457           }
1458         }
1459         break;
1460 
1461       /* read until EOF */
1462       case s_body_identity_eof:
1463         to_read = pe - p;
1464         if (to_read > 0) {
1465           if (settings->on_body) settings->on_body(parser, p, to_read);
1466           p += to_read - 1;
1467         }
1468         break;
1469 
1470       case s_chunk_size_start:
1471       {
1472         assert(parser->flags & F_CHUNKED);
1473 
1474         c = unhex[(unsigned char)ch];
1475         if (c == -1) goto error;
1476         parser->content_length = c;
1477         state = s_chunk_size;
1478         break;
1479       }
1480 
1481       case s_chunk_size:
1482       {
1483         assert(parser->flags & F_CHUNKED);
1484 
1485         if (ch == CR) {
1486           state = s_chunk_size_almost_done;
1487           break;
1488         }
1489 
1490         c = unhex[(unsigned char)ch];
1491 
1492         if (c == -1) {
1493           if (ch == ';' || ch == ' ') {
1494             state = s_chunk_parameters;
1495             break;
1496           }
1497           goto error;
1498         }
1499 
1500         parser->content_length *= 16;
1501         parser->content_length += c;
1502         break;
1503       }
1504 
1505       case s_chunk_parameters:
1506       {
1507         assert(parser->flags & F_CHUNKED);
1508         /* just ignore this shit. TODO check for overflow */
1509         if (ch == CR) {
1510           state = s_chunk_size_almost_done;
1511           break;
1512         }
1513         break;
1514       }
1515 
1516       case s_chunk_size_almost_done:
1517       {
1518         assert(parser->flags & F_CHUNKED);
1519         STRICT_CHECK(ch != LF);
1520 
1521         if (parser->content_length == 0) {
1522           parser->flags |= F_TRAILING;
1523           state = s_header_field_start;
1524         } else {
1525           state = s_chunk_data;
1526         }
1527         break;
1528       }
1529 
1530       case s_chunk_data:
1531       {
1532         assert(parser->flags & F_CHUNKED);
1533         assert(pe >= p);
1534 
1535         to_read = MIN((size_t)(pe - p), (size_t)(parser->content_length));
1536 
1537         if (to_read > 0) {
1538           if (settings->on_body) settings->on_body(parser, p, to_read);
1539           p += to_read - 1;
1540         }
1541 
1542         if (to_read == parser->content_length) {
1543           state = s_chunk_data_almost_done;
1544         }
1545 
1546         parser->content_length -= to_read;
1547         break;
1548       }
1549 
1550       case s_chunk_data_almost_done:
1551         assert(parser->flags & F_CHUNKED);
1552         STRICT_CHECK(ch != CR);
1553         state = s_chunk_data_done;
1554         break;
1555 
1556       case s_chunk_data_done:
1557         assert(parser->flags & F_CHUNKED);
1558         STRICT_CHECK(ch != LF);
1559         state = s_chunk_size_start;
1560         break;
1561 
1562       default:
1563         assert(0 && "unhandled state");
1564         goto error;
1565     }
1566   }
1567 
1568   CALLBACK_NOCLEAR(header_field);
1569   CALLBACK_NOCLEAR(header_value);
1570   CALLBACK_NOCLEAR(fragment);
1571   CALLBACK_NOCLEAR(query_string);
1572   CALLBACK_NOCLEAR(path);
1573   CALLBACK_NOCLEAR(url);
1574 
1575   parser->state = state;
1576   parser->header_state = header_state;
1577   parser->index = index;
1578   parser->nread = nread;
1579 
1580   return len;
1581 
1582 error:
1583   parser->state = s_dead;
1584   return (p - data);
1585 }
1586 
1587 
1588 int
1589 php_http_should_keep_alive (php_http_parser *parser)
1590 {
1591   if (parser->http_major > 0 && parser->http_minor > 0) {
1592     /* HTTP/1.1 */
1593     if (parser->flags & F_CONNECTION_CLOSE) {
1594       return 0;
1595     } else {
1596       return 1;
1597     }
1598   } else {
1599     /* HTTP/1.0 or earlier */
1600     if (parser->flags & F_CONNECTION_KEEP_ALIVE) {
1601       return 1;
1602     } else {
1603       return 0;
1604     }
1605   }
1606 }
1607 
1608 
1609 const char * php_http_method_str (enum php_http_method m)
1610 {
1611   return method_strings[m];
1612 }
1613 
1614 
1615 void
1616 php_http_parser_init (php_http_parser *parser, enum php_http_parser_type t)
1617 {
1618   parser->type = t;
1619   parser->state = (t == PHP_HTTP_REQUEST ? s_start_req : (t == PHP_HTTP_RESPONSE ? s_start_res : s_start_req_or_res));
1620   parser->nread = 0;
1621   parser->upgrade = 0;
1622   parser->flags = 0;
1623   parser->method = 0;
1624 }

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