Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Upgrade http-parser to eee60127c0df551be085cc8e7983e36d7700d885

  • Loading branch information...
commit 4956e3c0a25992bfa454a43c377ac120e0ff7727 1 parent 0cb4484
ry ry authored
1  deps/http_parser/.gitignore
View
@@ -2,3 +2,4 @@ tags
*.o
test
test_g
+test_fast
6 deps/http_parser/LICENSE-MIT
View
@@ -1,4 +1,8 @@
-Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
+http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright
+Igor Sysoev.
+
+Additional changes are licensed under the same terms as NGINX and
+copyright Joyent, Inc. and other Node contributors. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
13 deps/http_parser/Makefile
View
@@ -1,11 +1,14 @@
-OPT_DEBUG=-O0 -g -Wall -Wextra -Werror -I.
-OPT_FAST=-O3 -DHTTP_PARSER_STRICT=0 -I.
+CPPFLAGS?=-Wall -Wextra -Werror -I.
+OPT_DEBUG=$(CPPFLAGS) -O0 -g -DHTTP_PARSER_STRICT=1
+OPT_FAST=$(CPPFLAGS) -O3 -DHTTP_PARSER_STRICT=0
CC?=gcc
+AR?=ar
-test: test_g
+test: test_g test_fast
./test_g
+ ./test_fast
test_g: http_parser_g.o test_g.o
$(CC) $(OPT_DEBUG) http_parser_g.o test_g.o -o $@
@@ -31,11 +34,13 @@ test_fast: http_parser.o test.c http_parser.h
test-run-timed: test_fast
while(true) do time ./test_fast > /dev/null; done
+package: http_parser.o
+ $(AR) rcs libhttp_parser.a http_parser.o
tags: http_parser.c http_parser.h test.c
ctags $^
clean:
- rm -f *.o test test_fast test_g http_parser.tar tags
+ rm -f *.o *.a test test_fast test_g http_parser.tar tags
.PHONY: clean package test-run test-run-timed test-valgrind
118 deps/http_parser/http_parser.c
View
@@ -1,4 +1,7 @@
-/* Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
+/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
+ *
+ * Additional changes are licensed under the same terms as NGINX and
+ * copyright Joyent, Inc. and other Node contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -97,6 +100,7 @@ static const char *method_strings[] =
, "NOTIFY"
, "SUBSCRIBE"
, "UNSUBSCRIBE"
+ , "PATCH"
};
@@ -186,7 +190,7 @@ static const uint8_t normal_url_char[256] = {
/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
1, 1, 1, 1, 1, 1, 1, 1,
/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
- 1, 1, 1, 1, 1, 1, 1, 0 };
+ 1, 1, 1, 1, 1, 1, 1, 0, };
enum state
@@ -240,15 +244,17 @@ enum state
, s_header_almost_done
+ , s_chunk_size_start
+ , s_chunk_size
+ , s_chunk_parameters
+ , s_chunk_size_almost_done
+
, s_headers_almost_done
/* Important: 's_headers_almost_done' must be the last 'header' state. All
* states beyond this must be 'body' states. It is used for overflow
* checking. See the PARSING_HEADER() macro.
*/
- , s_chunk_size_start
- , s_chunk_size
- , s_chunk_size_almost_done
- , s_chunk_parameters
+
, s_chunk_data
, s_chunk_data_almost_done
, s_chunk_data_done
@@ -258,7 +264,7 @@ enum state
};
-#define PARSING_HEADER(state) (state <= s_headers_almost_done && 0 == (parser->flags & F_TRAILING))
+#define PARSING_HEADER(state) (state <= s_headers_almost_done)
enum header_states
@@ -288,20 +294,24 @@ enum header_states
};
-enum flags
- { F_CHUNKED = 1 << 0
- , F_CONNECTION_KEEP_ALIVE = 1 << 1
- , F_CONNECTION_CLOSE = 1 << 2
- , F_TRAILING = 1 << 3
- , F_UPGRADE = 1 << 4
- , F_SKIPBODY = 1 << 5
- };
+/* Macros for character classes; depends on strict-mode */
+#define CR '\r'
+#define LF '\n'
+#define LOWER(c) (unsigned char)(c | 0x20)
+#define TOKEN(c) (tokens[(unsigned char)c])
+#define IS_ALPHA(c) ((c) >= 'a' && (c) <= 'z')
+#define IS_NUM(c) ((c) >= '0' && (c) <= '9')
+#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c))
-
-#define CR '\r'
-#define LF '\n'
-#define LOWER(c) (unsigned char)(c | 0x20)
-#define TOKEN(c) tokens[(unsigned char)c]
+#if HTTP_PARSER_STRICT
+#define IS_URL_CHAR(c) (normal_url_char[(unsigned char) (c)])
+#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
+#else
+#define IS_URL_CHAR(c) \
+ (normal_url_char[(unsigned char) (c)] || ((c) & 0x80))
+#define IS_HOST_CHAR(c) \
+ (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
+#endif
#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,
break;
}
- if (ch < '0' || ch > '9') goto error;
+ if (!IS_NUM(ch)) goto error;
parser->http_major *= 10;
parser->http_major += ch - '0';
@@ -489,7 +499,7 @@ size_t http_parser_execute (http_parser *parser,
/* first digit of minor HTTP version */
case s_res_first_http_minor:
- if (ch < '0' || ch > '9') goto error;
+ if (!IS_NUM(ch)) goto error;
parser->http_minor = ch - '0';
state = s_res_http_minor;
break;
@@ -502,7 +512,7 @@ size_t http_parser_execute (http_parser *parser,
break;
}
- if (ch < '0' || ch > '9') goto error;
+ if (!IS_NUM(ch)) goto error;
parser->http_minor *= 10;
parser->http_minor += ch - '0';
@@ -513,7 +523,7 @@ size_t http_parser_execute (http_parser *parser,
case s_res_first_status_code:
{
- if (ch < '0' || ch > '9') {
+ if (!IS_NUM(ch)) {
if (ch == ' ') {
break;
}
@@ -526,7 +536,7 @@ size_t http_parser_execute (http_parser *parser,
case s_res_status_code:
{
- if (ch < '0' || ch > '9') {
+ if (!IS_NUM(ch)) {
switch (ch) {
case ' ':
state = s_res_status;
@@ -578,7 +588,7 @@ size_t http_parser_execute (http_parser *parser,
CALLBACK2(message_begin);
- if (ch < 'A' || 'Z' < ch) goto error;
+ if (!IS_ALPHA(LOWER(ch))) goto error;
start_req_method_assign:
parser->method = (enum http_method) 0;
@@ -592,7 +602,9 @@ size_t http_parser_execute (http_parser *parser,
case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break;
case 'N': parser->method = HTTP_NOTIFY; break;
case 'O': parser->method = HTTP_OPTIONS; break;
- case 'P': parser->method = HTTP_POST; /* or PROPFIND or PROPPATCH or PUT */ break;
+ case 'P': parser->method = HTTP_POST;
+ /* or PROPFIND or PROPPATCH or PUT or PATCH */
+ break;
case 'R': parser->method = HTTP_REPORT; break;
case 'S': parser->method = HTTP_SUBSCRIBE; break;
case 'T': parser->method = HTTP_TRACE; break;
@@ -633,6 +645,8 @@ size_t http_parser_execute (http_parser *parser,
parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */
} else if (index == 1 && parser->method == HTTP_POST && ch == 'U') {
parser->method = HTTP_PUT;
+ } else if (index == 1 && parser->method == HTTP_POST && ch == 'A') {
+ parser->method = HTTP_PATCH;
} else if (index == 2 && parser->method == HTTP_UNLOCK && ch == 'S') {
parser->method = HTTP_UNSUBSCRIBE;
} else if (index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {
@@ -657,9 +671,13 @@ size_t http_parser_execute (http_parser *parser,
c = LOWER(ch);
- if (c >= 'a' && c <= 'z') {
+ /* Proxied requests are followed by scheme of an absolute URI (alpha).
+ * CONNECT is followed by a hostname, which begins with alphanum.
+ * All other methods are followed by '/' or '*' (handled above).
+ */
+ if (IS_ALPHA(ch) || (parser->method == HTTP_CONNECT && IS_NUM(ch))) {
MARK(url);
- state = s_req_schema;
+ state = (parser->method == HTTP_CONNECT) ? s_req_host : s_req_schema;
break;
}
@@ -670,17 +688,11 @@ size_t http_parser_execute (http_parser *parser,
{
c = LOWER(ch);
- if (c >= 'a' && c <= 'z') break;
+ if (IS_ALPHA(c)) break;
if (ch == ':') {
state = s_req_schema_slash;
break;
- } else if (ch == '.') {
- state = s_req_host;
- break;
- } else if ('0' <= ch && ch <= '9') {
- state = s_req_host;
- break;
}
goto error;
@@ -699,8 +711,7 @@ size_t http_parser_execute (http_parser *parser,
case s_req_host:
{
c = LOWER(ch);
- if (c >= 'a' && c <= 'z') break;
- if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') break;
+ if (IS_HOST_CHAR(ch)) break;
switch (ch) {
case ':':
state = s_req_port;
@@ -717,6 +728,9 @@ size_t http_parser_execute (http_parser *parser,
CALLBACK(url);
state = s_req_http_start;
break;
+ case '?':
+ state = s_req_query_string_start;
+ break;
default:
goto error;
}
@@ -725,7 +739,7 @@ size_t http_parser_execute (http_parser *parser,
case s_req_port:
{
- if (ch >= '0' && ch <= '9') break;
+ if (IS_NUM(ch)) break;
switch (ch) {
case '/':
MARK(path);
@@ -739,6 +753,9 @@ size_t http_parser_execute (http_parser *parser,
CALLBACK(url);
state = s_req_http_start;
break;
+ case '?':
+ state = s_req_query_string_start;
+ break;
default:
goto error;
}
@@ -747,7 +764,7 @@ size_t http_parser_execute (http_parser *parser,
case s_req_path:
{
- if (normal_url_char[(unsigned char)ch]) break;
+ if (IS_URL_CHAR(ch)) break;
switch (ch) {
case ' ':
@@ -785,7 +802,7 @@ size_t http_parser_execute (http_parser *parser,
case s_req_query_string_start:
{
- if (normal_url_char[(unsigned char)ch]) {
+ if (IS_URL_CHAR(ch)) {
MARK(query_string);
state = s_req_query_string;
break;
@@ -821,7 +838,7 @@ size_t http_parser_execute (http_parser *parser,
case s_req_query_string:
{
- if (normal_url_char[(unsigned char)ch]) break;
+ if (IS_URL_CHAR(ch)) break;
switch (ch) {
case '?':
@@ -858,7 +875,7 @@ size_t http_parser_execute (http_parser *parser,
case s_req_fragment_start:
{
- if (normal_url_char[(unsigned char)ch]) {
+ if (IS_URL_CHAR(ch)) {
MARK(fragment);
state = s_req_fragment;
break;
@@ -895,7 +912,7 @@ size_t http_parser_execute (http_parser *parser,
case s_req_fragment:
{
- if (normal_url_char[(unsigned char)ch]) break;
+ if (IS_URL_CHAR(ch)) break;
switch (ch) {
case ' ':
@@ -973,7 +990,7 @@ size_t http_parser_execute (http_parser *parser,
break;
}
- if (ch < '0' || ch > '9') goto error;
+ if (!IS_NUM(ch)) goto error;
parser->http_major *= 10;
parser->http_major += ch - '0';
@@ -984,7 +1001,7 @@ size_t http_parser_execute (http_parser *parser,
/* first digit of minor HTTP version */
case s_req_first_http_minor:
- if (ch < '0' || ch > '9') goto error;
+ if (!IS_NUM(ch)) goto error;
parser->http_minor = ch - '0';
state = s_req_http_minor;
break;
@@ -1004,7 +1021,7 @@ size_t http_parser_execute (http_parser *parser,
/* XXX allow spaces after digit? */
- if (ch < '0' || ch > '9') goto error;
+ if (!IS_NUM(ch)) goto error;
parser->http_minor *= 10;
parser->http_minor += ch - '0';
@@ -1237,7 +1254,7 @@ size_t http_parser_execute (http_parser *parser,
break;
case h_content_length:
- if (ch < '0' || ch > '9') goto error;
+ if (!IS_NUM(ch)) goto error;
parser->content_length = ch - '0';
break;
@@ -1286,7 +1303,7 @@ size_t http_parser_execute (http_parser *parser,
case h_content_length:
if (ch == ' ') break;
- if (ch < '0' || ch > '9') goto error;
+ if (!IS_NUM(ch)) goto error;
parser->content_length *= 10;
parser->content_length += ch - '0';
break;
@@ -1458,6 +1475,7 @@ size_t http_parser_execute (http_parser *parser,
case s_chunk_size_start:
{
+ assert(nread == 1);
assert(parser->flags & F_CHUNKED);
c = unhex[(unsigned char)ch];
@@ -1507,6 +1525,8 @@ size_t http_parser_execute (http_parser *parser,
assert(parser->flags & F_CHUNKED);
STRICT_CHECK(ch != LF);
+ nread = 0;
+
if (parser->content_length == 0) {
parser->flags |= F_TRAILING;
state = s_header_field_start;
21 deps/http_parser/http_parser.h
View
@@ -1,4 +1,4 @@
-/* Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -24,6 +24,8 @@
extern "C" {
#endif
+#define HTTP_PARSER_VERSION_MAJOR 1
+#define HTTP_PARSER_VERSION_MINOR 0
#include <sys/types.h>
#if defined(_WIN32) && !defined(__MINGW32__)
@@ -47,8 +49,6 @@ typedef int ssize_t;
*/
#ifndef HTTP_PARSER_STRICT
# define HTTP_PARSER_STRICT 1
-#else
-# define HTTP_PARSER_STRICT 0
#endif
@@ -106,16 +106,29 @@ enum http_method
, HTTP_NOTIFY
, HTTP_SUBSCRIBE
, HTTP_UNSUBSCRIBE
+ /* RFC-5789 */
+ , HTTP_PATCH
};
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
+/* Flag values for http_parser.flags field */
+enum flags
+ { F_CHUNKED = 1 << 0
+ , F_CONNECTION_KEEP_ALIVE = 1 << 1
+ , F_CONNECTION_CLOSE = 1 << 2
+ , F_TRAILING = 1 << 3
+ , F_UPGRADE = 1 << 4
+ , F_SKIPBODY = 1 << 5
+ };
+
+
struct http_parser {
/** PRIVATE **/
unsigned char type : 2;
- unsigned char flags : 6;
+ unsigned char flags : 6; /* F_* values from 'flags' enum; semi-public */
unsigned char state;
unsigned char header_state;
unsigned char index;
141 deps/http_parser/test.c
View
@@ -1,4 +1,4 @@
-/* Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -498,7 +498,7 @@ const struct message requests[] =
#define CONNECT_REQUEST 17
, {.name = "connect request"
,.type= HTTP_REQUEST
- ,.raw= "CONNECT home0.netscape.com:443 HTTP/1.0\r\n"
+ ,.raw= "CONNECT 0-home0.netscape.com:443 HTTP/1.0\r\n"
"User-agent: Mozilla/1.1N\r\n"
"Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
"\r\n"
@@ -510,7 +510,7 @@ const struct message requests[] =
,.query_string= ""
,.fragment= ""
,.request_path= ""
- ,.request_url= "home0.netscape.com:443"
+ ,.request_url= "0-home0.netscape.com:443"
,.num_headers= 2
,.upgrade=1
,.headers= { { "User-agent", "Mozilla/1.1N" }
@@ -557,7 +557,7 @@ const struct message requests[] =
,.body= ""
}
-#define MSEARCH_REQ 19
+#define MSEARCH_REQ 20
, {.name= "m-search request"
,.type= HTTP_REQUEST
,.raw= "M-SEARCH * HTTP/1.1\r\n"
@@ -582,6 +582,139 @@ const struct message requests[] =
,.body= ""
}
+#define QUERY_TERMINATED_HOST 21
+, {.name= "host terminated by a query string"
+ ,.type= HTTP_REQUEST
+ ,.raw= "GET http://hypnotoad.org?hail=all HTTP/1.1\r\n"
+ "\r\n"
+ ,.should_keep_alive= TRUE
+ ,.message_complete_on_eof= FALSE
+ ,.http_major= 1
+ ,.http_minor= 1
+ ,.method= HTTP_GET
+ ,.query_string= "hail=all"
+ ,.fragment= ""
+ ,.request_path= ""
+ ,.request_url= "http://hypnotoad.org?hail=all"
+ ,.num_headers= 0
+ ,.headers= { }
+ ,.body= ""
+ }
+
+#define QUERY_TERMINATED_HOSTPORT 22
+, {.name= "host:port terminated by a query string"
+ ,.type= HTTP_REQUEST
+ ,.raw= "GET http://hypnotoad.org:1234?hail=all HTTP/1.1\r\n"
+ "\r\n"
+ ,.should_keep_alive= TRUE
+ ,.message_complete_on_eof= FALSE
+ ,.http_major= 1
+ ,.http_minor= 1
+ ,.method= HTTP_GET
+ ,.query_string= "hail=all"
+ ,.fragment= ""
+ ,.request_path= ""
+ ,.request_url= "http://hypnotoad.org:1234?hail=all"
+ ,.num_headers= 0
+ ,.headers= { }
+ ,.body= ""
+ }
+
+#define SPACE_TERMINATED_HOSTPORT 23
+, {.name= "host:port terminated by a space"
+ ,.type= HTTP_REQUEST
+ ,.raw= "GET http://hypnotoad.org:1234 HTTP/1.1\r\n"
+ "\r\n"
+ ,.should_keep_alive= TRUE
+ ,.message_complete_on_eof= FALSE
+ ,.http_major= 1
+ ,.http_minor= 1
+ ,.method= HTTP_GET
+ ,.query_string= ""
+ ,.fragment= ""
+ ,.request_path= ""
+ ,.request_url= "http://hypnotoad.org:1234"
+ ,.num_headers= 0
+ ,.headers= { }
+ ,.body= ""
+ }
+
+#if !HTTP_PARSER_STRICT
+#define UTF8_PATH_REQ 24
+, {.name= "utf-8 path request"
+ ,.type= HTTP_REQUEST
+ ,.raw= "GET /δ¶/δt/pope?q=1#narf HTTP/1.1\r\n"
+ "Host: github.com\r\n"
+ "\r\n"
+ ,.should_keep_alive= TRUE
+ ,.message_complete_on_eof= FALSE
+ ,.http_major= 1
+ ,.http_minor= 1
+ ,.method= HTTP_GET
+ ,.query_string= "q=1"
+ ,.fragment= "narf"
+ ,.request_path= "/δ¶/δt/pope"
+ ,.request_url= "/δ¶/δt/pope?q=1#narf"
+ ,.num_headers= 1
+ ,.headers= { {"Host", "github.com" }
+ }
+ ,.body= ""
+ }
+
+#define HOSTNAME_UNDERSCORE 25
+, {.name = "hostname underscore"
+ ,.type= HTTP_REQUEST
+ ,.raw= "CONNECT home_0.netscape.com:443 HTTP/1.0\r\n"
+ "User-agent: Mozilla/1.1N\r\n"
+ "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
+ "\r\n"
+ ,.should_keep_alive= FALSE
+ ,.message_complete_on_eof= FALSE
+ ,.http_major= 1
+ ,.http_minor= 0
+ ,.method= HTTP_CONNECT
+ ,.query_string= ""
+ ,.fragment= ""
+ ,.request_path= ""
+ ,.request_url= "home_0.netscape.com:443"
+ ,.num_headers= 2
+ ,.upgrade=1
+ ,.headers= { { "User-agent", "Mozilla/1.1N" }
+ , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
+ }
+ ,.body= ""
+ }
+#endif /* !HTTP_PARSER_STRICT */
+
+#define PATCH_REQ 26
+, {.name = "PATCH request"
+ ,.type= HTTP_REQUEST
+ ,.raw= "PATCH /file.txt HTTP/1.1\r\n"
+ "Host: www.example.com\r\n"
+ "Content-Type: application/example\r\n"
+ "If-Match: \"e0023aa4e\"\r\n"
+ "Content-Length: 10\r\n"
+ "\r\n"
+ "cccccccccc"
+ ,.should_keep_alive= TRUE
+ ,.message_complete_on_eof= FALSE
+ ,.http_major= 1
+ ,.http_minor= 1
+ ,.method= HTTP_PATCH
+ ,.query_string= ""
+ ,.fragment= ""
+ ,.request_path= "/file.txt"
+ ,.request_url= "/file.txt"
+ ,.num_headers= 4
+ ,.upgrade=0
+ ,.headers= { { "Host", "www.example.com" }
+ , { "Content-Type", "application/example" }
+ , { "If-Match", "\"e0023aa4e\"" }
+ , { "Content-Length", "10" }
+ }
+ ,.body= "cccccccccc"
+ }
+
, {.name= NULL } /* sentinel */
};
Please sign in to comment.
Something went wrong with that request. Please try again.