1- /* Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
1+ /* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
2+ *
3+ * Additional changes are licensed under the same terms as NGINX and
4+ * copyright Joyent, Inc. and other Node contributors. All rights reserved.
25 *
36 * Permission is hereby granted, free of charge, to any person obtaining a copy
47 * of this software and associated documentation files (the "Software"), to
@@ -97,6 +100,7 @@ static const char *method_strings[] =
97100 , "NOTIFY"
98101 , "SUBSCRIBE"
99102 , "UNSUBSCRIBE"
103+ , "PATCH"
100104 };
101105
102106
@@ -186,7 +190,7 @@ static const uint8_t normal_url_char[256] = {
186190/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
187191 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
188192/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
189- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 };
193+ 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , };
190194
191195
192196enum state
@@ -240,15 +244,17 @@ enum state
240244
241245 , s_header_almost_done
242246
247+ , s_chunk_size_start
248+ , s_chunk_size
249+ , s_chunk_parameters
250+ , s_chunk_size_almost_done
251+
243252 , s_headers_almost_done
244253 /* Important: 's_headers_almost_done' must be the last 'header' state. All
245254 * states beyond this must be 'body' states. It is used for overflow
246255 * checking. See the PARSING_HEADER() macro.
247256 */
248- , s_chunk_size_start
249- , s_chunk_size
250- , s_chunk_size_almost_done
251- , s_chunk_parameters
257+
252258 , s_chunk_data
253259 , s_chunk_data_almost_done
254260 , s_chunk_data_done
@@ -258,7 +264,7 @@ enum state
258264 };
259265
260266
261- #define PARSING_HEADER (state ) (state <= s_headers_almost_done && 0 == (parser->flags & F_TRAILING) )
267+ #define PARSING_HEADER (state ) (state <= s_headers_almost_done)
262268
263269
264270enum header_states
@@ -288,20 +294,24 @@ enum header_states
288294 };
289295
290296
291- enum flags
292- { F_CHUNKED = 1 << 0
293- , F_CONNECTION_KEEP_ALIVE = 1 << 1
294- , F_CONNECTION_CLOSE = 1 << 2
295- , F_TRAILING = 1 << 3
296- , F_UPGRADE = 1 << 4
297- , F_SKIPBODY = 1 << 5
298- };
297+ /* Macros for character classes; depends on strict-mode */
298+ #define CR '\r'
299+ #define LF '\n'
300+ #define LOWER ( c ) (unsigned char)(c | 0x20)
301+ #define TOKEN ( c ) (tokens[(unsigned char)c])
302+ #define IS_ALPHA ( c ) ((c) >= 'a' && (c) <= 'z')
303+ #define IS_NUM ( c ) ((c) >= '0' && (c) <= '9')
304+ #define IS_ALPHANUM ( c ) (IS_ALPHA(c) || IS_NUM(c))
299305
300-
301- #define CR '\r'
302- #define LF '\n'
303- #define LOWER (c ) (unsigned char)(c | 0x20)
304- #define TOKEN (c ) tokens[(unsigned char)c]
306+ #if HTTP_PARSER_STRICT
307+ #define IS_URL_CHAR (c ) (normal_url_char[(unsigned char) (c)])
308+ #define IS_HOST_CHAR (c ) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
309+ #else
310+ #define IS_URL_CHAR (c ) \
311+ (normal_url_char[(unsigned char) (c)] || ((c) & 0x80))
312+ #define IS_HOST_CHAR (c ) \
313+ (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
314+ #endif
305315
306316
307317#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
@@ -478,7 +488,7 @@ size_t http_parser_execute (http_parser *parser,
478488 break ;
479489 }
480490
481- if (ch < '0' || ch > '9' ) goto error ;
491+ if (! IS_NUM ( ch ) ) goto error ;
482492
483493 parser -> http_major *= 10 ;
484494 parser -> http_major += ch - '0' ;
@@ -489,7 +499,7 @@ size_t http_parser_execute (http_parser *parser,
489499
490500 /* first digit of minor HTTP version */
491501 case s_res_first_http_minor :
492- if (ch < '0' || ch > '9' ) goto error ;
502+ if (! IS_NUM ( ch ) ) goto error ;
493503 parser -> http_minor = ch - '0' ;
494504 state = s_res_http_minor ;
495505 break ;
@@ -502,7 +512,7 @@ size_t http_parser_execute (http_parser *parser,
502512 break ;
503513 }
504514
505- if (ch < '0' || ch > '9' ) goto error ;
515+ if (! IS_NUM ( ch ) ) goto error ;
506516
507517 parser -> http_minor *= 10 ;
508518 parser -> http_minor += ch - '0' ;
@@ -513,7 +523,7 @@ size_t http_parser_execute (http_parser *parser,
513523
514524 case s_res_first_status_code :
515525 {
516- if (ch < '0' || ch > '9' ) {
526+ if (! IS_NUM ( ch ) ) {
517527 if (ch == ' ' ) {
518528 break ;
519529 }
@@ -526,7 +536,7 @@ size_t http_parser_execute (http_parser *parser,
526536
527537 case s_res_status_code :
528538 {
529- if (ch < '0' || ch > '9' ) {
539+ if (! IS_NUM ( ch ) ) {
530540 switch (ch ) {
531541 case ' ' :
532542 state = s_res_status ;
@@ -578,7 +588,7 @@ size_t http_parser_execute (http_parser *parser,
578588
579589 CALLBACK2 (message_begin );
580590
581- if (ch < 'A' || 'Z' < ch ) goto error ;
591+ if (! IS_ALPHA ( LOWER ( ch )) ) goto error ;
582592
583593 start_req_method_assign :
584594 parser -> method = (enum http_method ) 0 ;
@@ -592,7 +602,9 @@ size_t http_parser_execute (http_parser *parser,
592602 case 'M' : parser -> method = HTTP_MKCOL ; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break ;
593603 case 'N' : parser -> method = HTTP_NOTIFY ; break ;
594604 case 'O' : parser -> method = HTTP_OPTIONS ; break ;
595- case 'P' : parser -> method = HTTP_POST ; /* or PROPFIND or PROPPATCH or PUT */ break ;
605+ case 'P' : parser -> method = HTTP_POST ;
606+ /* or PROPFIND or PROPPATCH or PUT or PATCH */
607+ break ;
596608 case 'R' : parser -> method = HTTP_REPORT ; break ;
597609 case 'S' : parser -> method = HTTP_SUBSCRIBE ; break ;
598610 case 'T' : parser -> method = HTTP_TRACE ; break ;
@@ -633,6 +645,8 @@ size_t http_parser_execute (http_parser *parser,
633645 parser -> method = HTTP_PROPFIND ; /* or HTTP_PROPPATCH */
634646 } else if (index == 1 && parser -> method == HTTP_POST && ch == 'U' ) {
635647 parser -> method = HTTP_PUT ;
648+ } else if (index == 1 && parser -> method == HTTP_POST && ch == 'A' ) {
649+ parser -> method = HTTP_PATCH ;
636650 } else if (index == 2 && parser -> method == HTTP_UNLOCK && ch == 'S' ) {
637651 parser -> method = HTTP_UNSUBSCRIBE ;
638652 } else if (index == 4 && parser -> method == HTTP_PROPFIND && ch == 'P' ) {
@@ -657,9 +671,13 @@ size_t http_parser_execute (http_parser *parser,
657671
658672 c = LOWER (ch );
659673
660- if (c >= 'a' && c <= 'z' ) {
674+ /* Proxied requests are followed by scheme of an absolute URI (alpha).
675+ * CONNECT is followed by a hostname, which begins with alphanum.
676+ * All other methods are followed by '/' or '*' (handled above).
677+ */
678+ if (IS_ALPHA (ch ) || (parser -> method == HTTP_CONNECT && IS_NUM (ch ))) {
661679 MARK (url );
662- state = s_req_schema ;
680+ state = ( parser -> method == HTTP_CONNECT ) ? s_req_host : s_req_schema ;
663681 break ;
664682 }
665683
@@ -670,17 +688,11 @@ size_t http_parser_execute (http_parser *parser,
670688 {
671689 c = LOWER (ch );
672690
673- if (c >= 'a' && c <= 'z' ) break ;
691+ if (IS_ALPHA ( c ) ) break ;
674692
675693 if (ch == ':' ) {
676694 state = s_req_schema_slash ;
677695 break ;
678- } else if (ch == '.' ) {
679- state = s_req_host ;
680- break ;
681- } else if ('0' <= ch && ch <= '9' ) {
682- state = s_req_host ;
683- break ;
684696 }
685697
686698 goto error ;
@@ -699,8 +711,7 @@ size_t http_parser_execute (http_parser *parser,
699711 case s_req_host :
700712 {
701713 c = LOWER (ch );
702- if (c >= 'a' && c <= 'z' ) break ;
703- if ((ch >= '0' && ch <= '9' ) || ch == '.' || ch == '-' ) break ;
714+ if (IS_HOST_CHAR (ch )) break ;
704715 switch (ch ) {
705716 case ':' :
706717 state = s_req_port ;
@@ -717,6 +728,9 @@ size_t http_parser_execute (http_parser *parser,
717728 CALLBACK (url );
718729 state = s_req_http_start ;
719730 break ;
731+ case '?' :
732+ state = s_req_query_string_start ;
733+ break ;
720734 default :
721735 goto error ;
722736 }
@@ -725,7 +739,7 @@ size_t http_parser_execute (http_parser *parser,
725739
726740 case s_req_port :
727741 {
728- if (ch >= '0' && ch <= '9' ) break ;
742+ if (IS_NUM ( ch ) ) break ;
729743 switch (ch ) {
730744 case '/' :
731745 MARK (path );
@@ -739,6 +753,9 @@ size_t http_parser_execute (http_parser *parser,
739753 CALLBACK (url );
740754 state = s_req_http_start ;
741755 break ;
756+ case '?' :
757+ state = s_req_query_string_start ;
758+ break ;
742759 default :
743760 goto error ;
744761 }
@@ -747,7 +764,7 @@ size_t http_parser_execute (http_parser *parser,
747764
748765 case s_req_path :
749766 {
750- if (normal_url_char [( unsigned char ) ch ] ) break ;
767+ if (IS_URL_CHAR ( ch ) ) break ;
751768
752769 switch (ch ) {
753770 case ' ' :
@@ -785,7 +802,7 @@ size_t http_parser_execute (http_parser *parser,
785802
786803 case s_req_query_string_start :
787804 {
788- if (normal_url_char [( unsigned char ) ch ] ) {
805+ if (IS_URL_CHAR ( ch ) ) {
789806 MARK (query_string );
790807 state = s_req_query_string ;
791808 break ;
@@ -821,7 +838,7 @@ size_t http_parser_execute (http_parser *parser,
821838
822839 case s_req_query_string :
823840 {
824- if (normal_url_char [( unsigned char ) ch ] ) break ;
841+ if (IS_URL_CHAR ( ch ) ) break ;
825842
826843 switch (ch ) {
827844 case '?' :
@@ -858,7 +875,7 @@ size_t http_parser_execute (http_parser *parser,
858875
859876 case s_req_fragment_start :
860877 {
861- if (normal_url_char [( unsigned char ) ch ] ) {
878+ if (IS_URL_CHAR ( ch ) ) {
862879 MARK (fragment );
863880 state = s_req_fragment ;
864881 break ;
@@ -895,7 +912,7 @@ size_t http_parser_execute (http_parser *parser,
895912
896913 case s_req_fragment :
897914 {
898- if (normal_url_char [( unsigned char ) ch ] ) break ;
915+ if (IS_URL_CHAR ( ch ) ) break ;
899916
900917 switch (ch ) {
901918 case ' ' :
@@ -973,7 +990,7 @@ size_t http_parser_execute (http_parser *parser,
973990 break ;
974991 }
975992
976- if (ch < '0' || ch > '9' ) goto error ;
993+ if (! IS_NUM ( ch ) ) goto error ;
977994
978995 parser -> http_major *= 10 ;
979996 parser -> http_major += ch - '0' ;
@@ -984,7 +1001,7 @@ size_t http_parser_execute (http_parser *parser,
9841001
9851002 /* first digit of minor HTTP version */
9861003 case s_req_first_http_minor :
987- if (ch < '0' || ch > '9' ) goto error ;
1004+ if (! IS_NUM ( ch ) ) goto error ;
9881005 parser -> http_minor = ch - '0' ;
9891006 state = s_req_http_minor ;
9901007 break ;
@@ -1004,7 +1021,7 @@ size_t http_parser_execute (http_parser *parser,
10041021
10051022 /* XXX allow spaces after digit? */
10061023
1007- if (ch < '0' || ch > '9' ) goto error ;
1024+ if (! IS_NUM ( ch ) ) goto error ;
10081025
10091026 parser -> http_minor *= 10 ;
10101027 parser -> http_minor += ch - '0' ;
@@ -1237,7 +1254,7 @@ size_t http_parser_execute (http_parser *parser,
12371254 break ;
12381255
12391256 case h_content_length :
1240- if (ch < '0' || ch > '9' ) goto error ;
1257+ if (! IS_NUM ( ch ) ) goto error ;
12411258 parser -> content_length = ch - '0' ;
12421259 break ;
12431260
@@ -1286,7 +1303,7 @@ size_t http_parser_execute (http_parser *parser,
12861303
12871304 case h_content_length :
12881305 if (ch == ' ' ) break ;
1289- if (ch < '0' || ch > '9' ) goto error ;
1306+ if (! IS_NUM ( ch ) ) goto error ;
12901307 parser -> content_length *= 10 ;
12911308 parser -> content_length += ch - '0' ;
12921309 break ;
@@ -1458,6 +1475,7 @@ size_t http_parser_execute (http_parser *parser,
14581475
14591476 case s_chunk_size_start :
14601477 {
1478+ assert (nread == 1 );
14611479 assert (parser -> flags & F_CHUNKED );
14621480
14631481 c = unhex [(unsigned char )ch ];
@@ -1507,6 +1525,8 @@ size_t http_parser_execute (http_parser *parser,
15071525 assert (parser -> flags & F_CHUNKED );
15081526 STRICT_CHECK (ch != LF );
15091527
1528+ nread = 0 ;
1529+
15101530 if (parser -> content_length == 0 ) {
15111531 parser -> flags |= F_TRAILING ;
15121532 state = s_header_field_start ;
0 commit comments