File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / sapi / cli / php_http_parser.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Jul 22 01:32:13 2013 UTC (10 years, 11 months ago) by misho
Branches: php, MAIN
CVS tags: v5_4_29p0, v5_4_29, v5_4_20p0, v5_4_20, v5_4_17, HEAD
5.4.17

    1: /* Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
    2:  *
    3:  * Permission is hereby granted, free of charge, to any person obtaining a copy
    4:  * of this software and associated documentation files (the "Software"), to
    5:  * deal in the Software without restriction, including without limitation the
    6:  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
    7:  * sell copies of the Software, and to permit persons to whom the Software is
    8:  * furnished to do so, subject to the following conditions:
    9:  *
   10:  * The above copyright notice and this permission notice shall be included in
   11:  * all copies or substantial portions of the Software.
   12:  *
   13:  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   14:  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   15:  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   16:  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   17:  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   18:  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
   19:  * IN THE SOFTWARE.
   20:  */
   21: #include <assert.h>
   22: #include <stddef.h>
   23: #include "php_http_parser.h"
   24: 
   25: 
   26: #ifndef MIN
   27: # define MIN(a,b) ((a) < (b) ? (a) : (b))
   28: #endif
   29: 
   30: 
   31: #define CALLBACK2(FOR)                                               \
   32: do {                                                                 \
   33:   if (settings->on_##FOR) {                                          \
   34:     if (0 != settings->on_##FOR(parser)) return (p - data);          \
   35:   }                                                                  \
   36: } while (0)
   37: 
   38: 
   39: #define MARK(FOR)                                                    \
   40: do {                                                                 \
   41:   FOR##_mark = p;                                                    \
   42: } while (0)
   43: 
   44: #define CALLBACK_NOCLEAR(FOR)                                        \
   45: do {                                                                 \
   46:   if (FOR##_mark) {                                                  \
   47:     if (settings->on_##FOR) {                                        \
   48:       if (0 != settings->on_##FOR(parser,                            \
   49:                                  FOR##_mark,                         \
   50:                                  p - FOR##_mark))                    \
   51:       {                                                              \
   52:         return (p - data);                                           \
   53:       }                                                              \
   54:     }                                                                \
   55:   }                                                                  \
   56: } while (0)
   57: 
   58: #ifdef PHP_WIN32
   59: # undef CALLBACK
   60: #endif
   61: #define CALLBACK(FOR)                                                \
   62: do {                                                                 \
   63:   CALLBACK_NOCLEAR(FOR);                                             \
   64:   FOR##_mark = NULL;                                                 \
   65: } while (0)
   66: 
   67: 
   68: #define PROXY_CONNECTION "proxy-connection"
   69: #define CONNECTION "connection"
   70: #define CONTENT_LENGTH "content-length"
   71: #define TRANSFER_ENCODING "transfer-encoding"
   72: #define UPGRADE "upgrade"
   73: #define CHUNKED "chunked"
   74: #define KEEP_ALIVE "keep-alive"
   75: #define CLOSE "close"
   76: 
   77: 
   78: static const char *method_strings[] =
   79:   { "DELETE"
   80:   , "GET"
   81:   , "HEAD"
   82:   , "POST"
   83:   , "PUT"
   84:   , "PATCH"
   85:   , "CONNECT"
   86:   , "OPTIONS"
   87:   , "TRACE"
   88:   , "COPY"
   89:   , "LOCK"
   90:   , "MKCOL"
   91:   , "MOVE"
   92:   , "PROPFIND"
   93:   , "PROPPATCH"
   94:   , "UNLOCK"
   95:   , "REPORT"
   96:   , "MKACTIVITY"
   97:   , "CHECKOUT"
   98:   , "MERGE"
   99:   , "M-SEARCH"
  100:   , "NOTIFY"
  101:   , "SUBSCRIBE"
  102:   , "UNSUBSCRIBE"
  103:   , "NOTIMPLEMENTED"
  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;
  594:           default: parser->method = PHP_HTTP_NOT_IMPLEMENTED; break;
  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];
  607:         if (ch == ' ' && (matcher[index] == '\0' || parser->method == PHP_HTTP_NOT_IMPLEMENTED)) {
  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;
  631:         } else if (index == 1 && parser->method == PHP_HTTP_POST && ch == 'A') {
  632:           parser->method = PHP_HTTP_PATCH;
  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 {
  638:           parser->method = PHP_HTTP_NOT_IMPLEMENTED;
  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>