Browse Source

Upgrade http-parser to eee60127c0df551be085cc8e7983e36d7700d885

v0.7.4-release
Ryan Dahl 14 years ago
parent
commit
4956e3c0a2
  1. 1
      deps/http_parser/.gitignore
  2. 6
      deps/http_parser/LICENSE-MIT
  3. 13
      deps/http_parser/Makefile
  4. 118
      deps/http_parser/http_parser.c
  5. 21
      deps/http_parser/http_parser.h
  6. 141
      deps/http_parser/test.c

1
deps/http_parser/.gitignore

@ -2,3 +2,4 @@ tags
*.o
test
test_g
test_fast

6
deps/http_parser/LICENSE-MIT

@ -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

@ -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

@ -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

@ -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

@ -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 */
};

Loading…
Cancel
Save