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