You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1608 lines
44 KiB

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