File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / sapi / cli / php_http_parser.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue May 29 12:34:35 2012 UTC (12 years, 2 months ago) by misho
Branches: php, MAIN
CVS tags: v5_4_3elwix, v5_4_17p0, HEAD
php 5.4.3+patches

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

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