Annotation of embedaddon/php/sapi/cli/php_http_parser.c, revision 1.1.1.1

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>