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