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