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

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

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