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