Browse Source

Upgrade http_parser to 965f91bc76b2d1601e23

v0.7.4-release
Ryan Dahl 14 years ago
parent
commit
c3ccbea5cd
  1. 38
      deps/http_parser/Makefile
  2. 4
      deps/http_parser/README.md
  3. 334
      deps/http_parser/http_parser.c
  4. 50
      deps/http_parser/http_parser.gyp
  5. 94
      deps/http_parser/http_parser.h
  6. 376
      deps/http_parser/test.c
  7. 12
      src/node_http_parser.cc

38
deps/http_parser/Makefile

@ -1,39 +1,45 @@
CPPFLAGS?=-Wall -Wextra -Werror -I.
OPT_DEBUG=$(CPPFLAGS) -O0 -g -DHTTP_PARSER_STRICT=1
OPT_FAST=$(CPPFLAGS) -O3 -DHTTP_PARSER_STRICT=0
CC?=gcc CC?=gcc
AR?=ar AR?=ar
CPPFLAGS += -I.
CPPFLAGS_DEBUG = $(CPPFLAGS) -DHTTP_PARSER_STRICT=1 -DHTTP_PARSER_DEBUG=1
CPPFLAGS_DEBUG += $(CPPFLAGS_DEBUG_EXTRA)
CPPFLAGS_FAST = $(CPPFLAGS) -DHTTP_PARSER_STRICT=0 -DHTTP_PARSER_DEBUG=0
CPPFLAGS_FAST += $(CPPFLAGS_FAST_EXTRA)
CFLAGS += -Wall -Wextra -Werror
CFLAGS_DEBUG = $(CFLAGS) -O0 -g $(CFLAGS_DEBUG_EXTRA)
CFLAGS_FAST = $(CFLAGS) -O3 $(CFLAGS_FAST_EXTRA)
test: test_g test_fast test: test_g test_fast
./test_g ./test_g
./test_fast ./test_fast
test_g: http_parser_g.o test_g.o test_g: http_parser_g.o test_g.o
$(CC) $(OPT_DEBUG) http_parser_g.o test_g.o -o $@ $(CC) $(CFLAGS_DEBUG) $(LDFLAGS) http_parser_g.o test_g.o -o $@
test_g.o: test.c http_parser.h Makefile test_g.o: test.c http_parser.h Makefile
$(CC) $(OPT_DEBUG) -c test.c -o $@ $(CC) $(CPPFLAGS_DEBUG) $(CFLAGS_DEBUG) -c test.c -o $@
test.o: test.c http_parser.h Makefile
$(CC) $(OPT_FAST) -c test.c -o $@
http_parser_g.o: http_parser.c http_parser.h Makefile http_parser_g.o: http_parser.c http_parser.h Makefile
$(CC) $(OPT_DEBUG) -c http_parser.c -o $@ $(CC) $(CPPFLAGS_DEBUG) $(CFLAGS_DEBUG) -c http_parser.c -o $@
test-valgrind: test_g test_fast: http_parser.o test.o http_parser.h
valgrind ./test_g $(CC) $(CFLAGS_FAST) $(LDFLAGS) http_parser.o test.o -o $@
http_parser.o: http_parser.c http_parser.h Makefile test.o: test.c http_parser.h Makefile
$(CC) $(OPT_FAST) -c http_parser.c $(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) -c test.c -o $@
test_fast: http_parser.o test.c http_parser.h http_parser.o: http_parser.c http_parser.h Makefile
$(CC) $(OPT_FAST) http_parser.o test.c -o $@ $(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) -c http_parser.c
test-run-timed: test_fast test-run-timed: test_fast
while(true) do time ./test_fast > /dev/null; done while(true) do time ./test_fast > /dev/null; done
test-valgrind: test_g
valgrind ./test_g
package: http_parser.o package: http_parser.o
$(AR) rcs libhttp_parser.a http_parser.o $(AR) rcs libhttp_parser.a http_parser.o

4
deps/http_parser/README.md

@ -24,7 +24,7 @@ The parser extracts the following information from HTTP messages:
* Response status code * Response status code
* Transfer-Encoding * Transfer-Encoding
* HTTP version * HTTP version
* Request path, query string, fragment * Request URL
* Message body * Message body
@ -126,7 +126,7 @@ There are two types of callbacks:
* notification `typedef int (*http_cb) (http_parser*);` * notification `typedef int (*http_cb) (http_parser*);`
Callbacks: on_message_begin, on_headers_complete, on_message_complete. Callbacks: on_message_begin, on_headers_complete, on_message_complete.
* data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);` * data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);`
Callbacks: (requests only) on_path, on_query_string, on_uri, on_fragment, Callbacks: (requests only) on_uri,
(common) on_header_field, on_header_value, on_body; (common) on_header_field, on_header_value, on_body;
Callbacks must return 0 on success. Returning a non-zero value indicates Callbacks must return 0 on success. Returning a non-zero value indicates

334
deps/http_parser/http_parser.c

@ -31,10 +31,27 @@
#endif #endif
#if HTTP_PARSER_DEBUG
#define SET_ERRNO(e) \
do { \
parser->http_errno = (e); \
parser->error_lineno = __LINE__; \
} while (0)
#else
#define SET_ERRNO(e) \
do { \
parser->http_errno = (e); \
} while(0)
#endif
#define CALLBACK2(FOR) \ #define CALLBACK2(FOR) \
do { \ do { \
if (settings->on_##FOR) { \ if (settings->on_##FOR) { \
if (0 != settings->on_##FOR(parser)) return (p - data); \ if (0 != settings->on_##FOR(parser)) { \
SET_ERRNO(HPE_CB_##FOR); \
return (p - data); \
} \
} \ } \
} while (0) } while (0)
@ -44,7 +61,7 @@ do { \
FOR##_mark = p; \ FOR##_mark = p; \
} while (0) } while (0)
#define CALLBACK_NOCLEAR(FOR) \ #define CALLBACK(FOR) \
do { \ do { \
if (FOR##_mark) { \ if (FOR##_mark) { \
if (settings->on_##FOR) { \ if (settings->on_##FOR) { \
@ -52,20 +69,15 @@ do { \
FOR##_mark, \ FOR##_mark, \
p - FOR##_mark)) \ p - FOR##_mark)) \
{ \ { \
SET_ERRNO(HPE_CB_##FOR); \
return (p - data); \ return (p - data); \
} \ } \
} \ } \
FOR##_mark = NULL; \
} \ } \
} while (0) } while (0)
#define CALLBACK(FOR) \
do { \
CALLBACK_NOCLEAR(FOR); \
FOR##_mark = NULL; \
} while (0)
#define PROXY_CONNECTION "proxy-connection" #define PROXY_CONNECTION "proxy-connection"
#define CONNECTION "connection" #define CONNECTION "connection"
#define CONTENT_LENGTH "content-length" #define CONTENT_LENGTH "content-length"
@ -241,6 +253,7 @@ enum state
, s_header_field , s_header_field
, s_header_value_start , s_header_value_start
, s_header_value , s_header_value
, s_header_value_lws
, s_header_almost_done , s_header_almost_done
@ -299,7 +312,7 @@ enum header_states
#define LF '\n' #define LF '\n'
#define LOWER(c) (unsigned char)(c | 0x20) #define LOWER(c) (unsigned char)(c | 0x20)
#define TOKEN(c) (tokens[(unsigned char)c]) #define TOKEN(c) (tokens[(unsigned char)c])
#define IS_ALPHA(c) ((c) >= 'a' && (c) <= 'z') #define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z')
#define IS_NUM(c) ((c) >= '0' && (c) <= '9') #define IS_NUM(c) ((c) >= '0' && (c) <= '9')
#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c)) #define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c))
@ -318,7 +331,13 @@ enum header_states
#if HTTP_PARSER_STRICT #if HTTP_PARSER_STRICT
# define STRICT_CHECK(cond) if (cond) goto error # define STRICT_CHECK(cond) \
do { \
if (cond) { \
SET_ERRNO(HPE_STRICT); \
goto error; \
} \
} while (0)
# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead) # define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
#else #else
# define STRICT_CHECK(cond) # define STRICT_CHECK(cond)
@ -326,20 +345,39 @@ enum header_states
#endif #endif
/* Map errno values to strings for human-readable output */
#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s },
static struct {
const char *name;
const char *description;
} http_strerror_tab[] = {
HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)
};
#undef HTTP_STRERROR_GEN
size_t http_parser_execute (http_parser *parser, size_t http_parser_execute (http_parser *parser,
const http_parser_settings *settings, const http_parser_settings *settings,
const char *data, const char *data,
size_t len) size_t len)
{ {
char c, ch; char c, ch;
int8_t unhex_val;
const char *p = data, *pe; const char *p = data, *pe;
int64_t to_read; int64_t to_read;
enum state state;
enum state state = (enum state) parser->state; enum header_states header_state;
enum header_states header_state = (enum header_states) parser->header_state;
uint64_t index = parser->index; uint64_t index = parser->index;
uint64_t nread = parser->nread; uint64_t nread = parser->nread;
/* We're in an error state. Don't bother doing anything. */
if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
return 0;
}
state = (enum state) parser->state;
header_state = (enum header_states) parser->header_state;
if (len == 0) { if (len == 0) {
switch (state) { switch (state) {
case s_body_identity_eof: case s_body_identity_eof:
@ -353,7 +391,8 @@ size_t http_parser_execute (http_parser *parser,
return 0; return 0;
default: default:
return 1; // error SET_ERRNO(HPE_INVALID_EOF_STATE);
return 1;
} }
} }
@ -362,21 +401,12 @@ size_t http_parser_execute (http_parser *parser,
separated. */ separated. */
const char *header_field_mark = 0; const char *header_field_mark = 0;
const char *header_value_mark = 0; const char *header_value_mark = 0;
const char *fragment_mark = 0;
const char *query_string_mark = 0;
const char *path_mark = 0;
const char *url_mark = 0; const char *url_mark = 0;
if (state == s_header_field) if (state == s_header_field)
header_field_mark = data; header_field_mark = data;
if (state == s_header_value) if (state == s_header_value)
header_value_mark = data; header_value_mark = data;
if (state == s_req_fragment)
fragment_mark = data;
if (state == s_req_query_string)
query_string_mark = data;
if (state == s_req_path)
path_mark = data;
if (state == s_req_path || state == s_req_schema || state == s_req_schema_slash if (state == s_req_path || state == s_req_schema || state == s_req_schema_slash
|| state == s_req_schema_slash_slash || state == s_req_port || state == s_req_schema_slash_slash || state == s_req_port
|| state == s_req_query_string_start || state == s_req_query_string || state == s_req_query_string_start || state == s_req_query_string
@ -390,7 +420,10 @@ size_t http_parser_execute (http_parser *parser,
if (PARSING_HEADER(state)) { if (PARSING_HEADER(state)) {
++nread; ++nread;
/* Buffer overflow attack */ /* Buffer overflow attack */
if (nread > HTTP_MAX_HEADER_SIZE) goto error; if (nread > HTTP_MAX_HEADER_SIZE) {
SET_ERRNO(HPE_HEADER_OVERFLOW);
goto error;
}
} }
switch (state) { switch (state) {
@ -399,6 +432,7 @@ size_t http_parser_execute (http_parser *parser,
/* this state is used after a 'Connection: close' message /* this state is used after a 'Connection: close' message
* the parser will error out if it reads another message * the parser will error out if it reads another message
*/ */
SET_ERRNO(HPE_CLOSED_CONNECTION);
goto error; goto error;
case s_start_req_or_res: case s_start_req_or_res:
@ -424,7 +458,11 @@ size_t http_parser_execute (http_parser *parser,
parser->type = HTTP_RESPONSE; parser->type = HTTP_RESPONSE;
state = s_res_HT; state = s_res_HT;
} else { } else {
if (ch != 'E') goto error; if (ch != 'E') {
SET_ERRNO(HPE_INVALID_CONSTANT);
goto error;
}
parser->type = HTTP_REQUEST; parser->type = HTTP_REQUEST;
parser->method = HTTP_HEAD; parser->method = HTTP_HEAD;
index = 2; index = 2;
@ -449,6 +487,7 @@ size_t http_parser_execute (http_parser *parser,
break; break;
default: default:
SET_ERRNO(HPE_INVALID_CONSTANT);
goto error; goto error;
} }
break; break;
@ -475,7 +514,11 @@ size_t http_parser_execute (http_parser *parser,
break; break;
case s_res_first_http_major: case s_res_first_http_major:
if (ch < '1' || ch > '9') goto error; if (ch < '1' || ch > '9') {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
parser->http_major = ch - '0'; parser->http_major = ch - '0';
state = s_res_http_major; state = s_res_http_major;
break; break;
@ -488,18 +531,29 @@ size_t http_parser_execute (http_parser *parser,
break; break;
} }
if (!IS_NUM(ch)) goto error; if (!IS_NUM(ch)) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
parser->http_major *= 10; parser->http_major *= 10;
parser->http_major += ch - '0'; parser->http_major += ch - '0';
if (parser->http_major > 999) goto error; if (parser->http_major > 999) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
break; break;
} }
/* first digit of minor HTTP version */ /* first digit of minor HTTP version */
case s_res_first_http_minor: case s_res_first_http_minor:
if (!IS_NUM(ch)) goto error; if (!IS_NUM(ch)) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
parser->http_minor = ch - '0'; parser->http_minor = ch - '0';
state = s_res_http_minor; state = s_res_http_minor;
break; break;
@ -512,12 +566,19 @@ size_t http_parser_execute (http_parser *parser,
break; break;
} }
if (!IS_NUM(ch)) goto error; if (!IS_NUM(ch)) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
parser->http_minor *= 10; parser->http_minor *= 10;
parser->http_minor += ch - '0'; parser->http_minor += ch - '0';
if (parser->http_minor > 999) goto error; if (parser->http_minor > 999) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
break; break;
} }
@ -527,6 +588,8 @@ size_t http_parser_execute (http_parser *parser,
if (ch == ' ') { if (ch == ' ') {
break; break;
} }
SET_ERRNO(HPE_INVALID_STATUS);
goto error; goto error;
} }
parser->status_code = ch - '0'; parser->status_code = ch - '0';
@ -548,6 +611,7 @@ size_t http_parser_execute (http_parser *parser,
state = s_header_field_start; state = s_header_field_start;
break; break;
default: default:
SET_ERRNO(HPE_INVALID_STATUS);
goto error; goto error;
} }
break; break;
@ -556,7 +620,11 @@ size_t http_parser_execute (http_parser *parser,
parser->status_code *= 10; parser->status_code *= 10;
parser->status_code += ch - '0'; parser->status_code += ch - '0';
if (parser->status_code > 999) goto error; if (parser->status_code > 999) {
SET_ERRNO(HPE_INVALID_STATUS);
goto error;
}
break; break;
} }
@ -588,7 +656,10 @@ size_t http_parser_execute (http_parser *parser,
CALLBACK2(message_begin); CALLBACK2(message_begin);
if (!IS_ALPHA(LOWER(ch))) goto error; if (!IS_ALPHA(ch)) {
SET_ERRNO(HPE_INVALID_METHOD);
goto error;
}
start_req_method_assign: start_req_method_assign:
parser->method = (enum http_method) 0; parser->method = (enum http_method) 0;
@ -609,7 +680,9 @@ size_t http_parser_execute (http_parser *parser,
case 'S': parser->method = HTTP_SUBSCRIBE; break; case 'S': parser->method = HTTP_SUBSCRIBE; break;
case 'T': parser->method = HTTP_TRACE; break; case 'T': parser->method = HTTP_TRACE; break;
case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break; case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break;
default: goto error; default:
SET_ERRNO(HPE_INVALID_METHOD);
goto error;
} }
state = s_req_method; state = s_req_method;
break; break;
@ -617,8 +690,10 @@ size_t http_parser_execute (http_parser *parser,
case s_req_method: case s_req_method:
{ {
if (ch == '\0') if (ch == '\0') {
SET_ERRNO(HPE_INVALID_METHOD);
goto error; goto error;
}
const char *matcher = method_strings[parser->method]; const char *matcher = method_strings[parser->method];
if (ch == ' ' && matcher[index] == '\0') { if (ch == ' ' && matcher[index] == '\0') {
@ -630,6 +705,8 @@ size_t http_parser_execute (http_parser *parser,
parser->method = HTTP_CHECKOUT; parser->method = HTTP_CHECKOUT;
} else if (index == 2 && ch == 'P') { } else if (index == 2 && ch == 'P') {
parser->method = HTTP_COPY; parser->method = HTTP_COPY;
} else {
goto error;
} }
} else if (parser->method == HTTP_MKCOL) { } else if (parser->method == HTTP_MKCOL) {
if (index == 1 && ch == 'O') { if (index == 1 && ch == 'O') {
@ -640,18 +717,25 @@ size_t http_parser_execute (http_parser *parser,
parser->method = HTTP_MSEARCH; parser->method = HTTP_MSEARCH;
} else if (index == 2 && ch == 'A') { } else if (index == 2 && ch == 'A') {
parser->method = HTTP_MKACTIVITY; parser->method = HTTP_MKACTIVITY;
} else {
goto error;
}
} else if (index == 1 && parser->method == HTTP_POST) {
if (ch == 'R') {
parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */
} else if (ch == 'U') {
parser->method = HTTP_PUT;
} else if (ch == 'A') {
parser->method = HTTP_PATCH;
} else {
goto error;
} }
} else if (index == 1 && parser->method == HTTP_POST && ch == 'R') {
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') { } else if (index == 2 && parser->method == HTTP_UNLOCK && ch == 'S') {
parser->method = HTTP_UNSUBSCRIBE; parser->method = HTTP_UNSUBSCRIBE;
} else if (index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') { } else if (index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {
parser->method = HTTP_PROPPATCH; parser->method = HTTP_PROPPATCH;
} else { } else {
SET_ERRNO(HPE_INVALID_METHOD);
goto error; goto error;
} }
@ -664,13 +748,10 @@ size_t http_parser_execute (http_parser *parser,
if (ch == '/' || ch == '*') { if (ch == '/' || ch == '*') {
MARK(url); MARK(url);
MARK(path);
state = s_req_path; state = s_req_path;
break; break;
} }
c = LOWER(ch);
/* Proxied requests are followed by scheme of an absolute URI (alpha). /* Proxied requests are followed by scheme of an absolute URI (alpha).
* CONNECT is followed by a hostname, which begins with alphanum. * CONNECT is followed by a hostname, which begins with alphanum.
* All other methods are followed by '/' or '*' (handled above). * All other methods are followed by '/' or '*' (handled above).
@ -681,20 +762,20 @@ size_t http_parser_execute (http_parser *parser,
break; break;
} }
SET_ERRNO(HPE_INVALID_URL);
goto error; goto error;
} }
case s_req_schema: case s_req_schema:
{ {
c = LOWER(ch); if (IS_ALPHA(ch)) break;
if (IS_ALPHA(c)) break;
if (ch == ':') { if (ch == ':') {
state = s_req_schema_slash; state = s_req_schema_slash;
break; break;
} }
SET_ERRNO(HPE_INVALID_URL);
goto error; goto error;
} }
@ -710,14 +791,12 @@ size_t http_parser_execute (http_parser *parser,
case s_req_host: case s_req_host:
{ {
c = LOWER(ch);
if (IS_HOST_CHAR(ch)) break; if (IS_HOST_CHAR(ch)) break;
switch (ch) { switch (ch) {
case ':': case ':':
state = s_req_port; state = s_req_port;
break; break;
case '/': case '/':
MARK(path);
state = s_req_path; state = s_req_path;
break; break;
case ' ': case ' ':
@ -732,6 +811,7 @@ size_t http_parser_execute (http_parser *parser,
state = s_req_query_string_start; state = s_req_query_string_start;
break; break;
default: default:
SET_ERRNO(HPE_INVALID_HOST);
goto error; goto error;
} }
break; break;
@ -742,7 +822,6 @@ size_t http_parser_execute (http_parser *parser,
if (IS_NUM(ch)) break; if (IS_NUM(ch)) break;
switch (ch) { switch (ch) {
case '/': case '/':
MARK(path);
state = s_req_path; state = s_req_path;
break; break;
case ' ': case ' ':
@ -757,6 +836,7 @@ size_t http_parser_execute (http_parser *parser,
state = s_req_query_string_start; state = s_req_query_string_start;
break; break;
default: default:
SET_ERRNO(HPE_INVALID_PORT);
goto error; goto error;
} }
break; break;
@ -769,32 +849,28 @@ size_t http_parser_execute (http_parser *parser,
switch (ch) { switch (ch) {
case ' ': case ' ':
CALLBACK(url); CALLBACK(url);
CALLBACK(path);
state = s_req_http_start; state = s_req_http_start;
break; break;
case CR: case CR:
CALLBACK(url); CALLBACK(url);
CALLBACK(path);
parser->http_major = 0; parser->http_major = 0;
parser->http_minor = 9; parser->http_minor = 9;
state = s_req_line_almost_done; state = s_req_line_almost_done;
break; break;
case LF: case LF:
CALLBACK(url); CALLBACK(url);
CALLBACK(path);
parser->http_major = 0; parser->http_major = 0;
parser->http_minor = 9; parser->http_minor = 9;
state = s_header_field_start; state = s_header_field_start;
break; break;
case '?': case '?':
CALLBACK(path);
state = s_req_query_string_start; state = s_req_query_string_start;
break; break;
case '#': case '#':
CALLBACK(path);
state = s_req_fragment_start; state = s_req_fragment_start;
break; break;
default: default:
SET_ERRNO(HPE_INVALID_PATH);
goto error; goto error;
} }
break; break;
@ -803,7 +879,6 @@ size_t http_parser_execute (http_parser *parser,
case s_req_query_string_start: case s_req_query_string_start:
{ {
if (IS_URL_CHAR(ch)) { if (IS_URL_CHAR(ch)) {
MARK(query_string);
state = s_req_query_string; state = s_req_query_string;
break; break;
} }
@ -831,6 +906,7 @@ size_t http_parser_execute (http_parser *parser,
state = s_req_fragment_start; state = s_req_fragment_start;
break; break;
default: default:
SET_ERRNO(HPE_INVALID_QUERY_STRING);
goto error; goto error;
} }
break; break;
@ -846,28 +922,25 @@ size_t http_parser_execute (http_parser *parser,
break; break;
case ' ': case ' ':
CALLBACK(url); CALLBACK(url);
CALLBACK(query_string);
state = s_req_http_start; state = s_req_http_start;
break; break;
case CR: case CR:
CALLBACK(url); CALLBACK(url);
CALLBACK(query_string);
parser->http_major = 0; parser->http_major = 0;
parser->http_minor = 9; parser->http_minor = 9;
state = s_req_line_almost_done; state = s_req_line_almost_done;
break; break;
case LF: case LF:
CALLBACK(url); CALLBACK(url);
CALLBACK(query_string);
parser->http_major = 0; parser->http_major = 0;
parser->http_minor = 9; parser->http_minor = 9;
state = s_header_field_start; state = s_header_field_start;
break; break;
case '#': case '#':
CALLBACK(query_string);
state = s_req_fragment_start; state = s_req_fragment_start;
break; break;
default: default:
SET_ERRNO(HPE_INVALID_QUERY_STRING);
goto error; goto error;
} }
break; break;
@ -876,7 +949,6 @@ size_t http_parser_execute (http_parser *parser,
case s_req_fragment_start: case s_req_fragment_start:
{ {
if (IS_URL_CHAR(ch)) { if (IS_URL_CHAR(ch)) {
MARK(fragment);
state = s_req_fragment; state = s_req_fragment;
break; break;
} }
@ -899,12 +971,12 @@ size_t http_parser_execute (http_parser *parser,
state = s_header_field_start; state = s_header_field_start;
break; break;
case '?': case '?':
MARK(fragment);
state = s_req_fragment; state = s_req_fragment;
break; break;
case '#': case '#':
break; break;
default: default:
SET_ERRNO(HPE_INVALID_FRAGMENT);
goto error; goto error;
} }
break; break;
@ -917,19 +989,16 @@ size_t http_parser_execute (http_parser *parser,
switch (ch) { switch (ch) {
case ' ': case ' ':
CALLBACK(url); CALLBACK(url);
CALLBACK(fragment);
state = s_req_http_start; state = s_req_http_start;
break; break;
case CR: case CR:
CALLBACK(url); CALLBACK(url);
CALLBACK(fragment);
parser->http_major = 0; parser->http_major = 0;
parser->http_minor = 9; parser->http_minor = 9;
state = s_req_line_almost_done; state = s_req_line_almost_done;
break; break;
case LF: case LF:
CALLBACK(url); CALLBACK(url);
CALLBACK(fragment);
parser->http_major = 0; parser->http_major = 0;
parser->http_minor = 9; parser->http_minor = 9;
state = s_header_field_start; state = s_header_field_start;
@ -938,6 +1007,7 @@ size_t http_parser_execute (http_parser *parser,
case '#': case '#':
break; break;
default: default:
SET_ERRNO(HPE_INVALID_FRAGMENT);
goto error; goto error;
} }
break; break;
@ -951,6 +1021,7 @@ size_t http_parser_execute (http_parser *parser,
case ' ': case ' ':
break; break;
default: default:
SET_ERRNO(HPE_INVALID_CONSTANT);
goto error; goto error;
} }
break; break;
@ -977,7 +1048,11 @@ size_t http_parser_execute (http_parser *parser,
/* first digit of major HTTP version */ /* first digit of major HTTP version */
case s_req_first_http_major: case s_req_first_http_major:
if (ch < '1' || ch > '9') goto error; if (ch < '1' || ch > '9') {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
parser->http_major = ch - '0'; parser->http_major = ch - '0';
state = s_req_http_major; state = s_req_http_major;
break; break;
@ -990,18 +1065,29 @@ size_t http_parser_execute (http_parser *parser,
break; break;
} }
if (!IS_NUM(ch)) goto error; if (!IS_NUM(ch)) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
parser->http_major *= 10; parser->http_major *= 10;
parser->http_major += ch - '0'; parser->http_major += ch - '0';
if (parser->http_major > 999) goto error; if (parser->http_major > 999) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
break; break;
} }
/* first digit of minor HTTP version */ /* first digit of minor HTTP version */
case s_req_first_http_minor: case s_req_first_http_minor:
if (!IS_NUM(ch)) goto error; if (!IS_NUM(ch)) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
parser->http_minor = ch - '0'; parser->http_minor = ch - '0';
state = s_req_http_minor; state = s_req_http_minor;
break; break;
@ -1021,24 +1107,36 @@ size_t http_parser_execute (http_parser *parser,
/* XXX allow spaces after digit? */ /* XXX allow spaces after digit? */
if (!IS_NUM(ch)) goto error; if (!IS_NUM(ch)) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
parser->http_minor *= 10; parser->http_minor *= 10;
parser->http_minor += ch - '0'; parser->http_minor += ch - '0';
if (parser->http_minor > 999) goto error; if (parser->http_minor > 999) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
break; break;
} }
/* end of request line */ /* end of request line */
case s_req_line_almost_done: case s_req_line_almost_done:
{ {
if (ch != LF) goto error; if (ch != LF) {
SET_ERRNO(HPE_LF_EXPECTED);
goto error;
}
state = s_header_field_start; state = s_header_field_start;
break; break;
} }
case s_header_field_start: case s_header_field_start:
header_field_start:
{ {
if (ch == CR) { if (ch == CR) {
state = s_headers_almost_done; state = s_headers_almost_done;
@ -1054,7 +1152,10 @@ size_t http_parser_execute (http_parser *parser,
c = TOKEN(ch); c = TOKEN(ch);
if (!c) goto error; if (!c) {
SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
goto error;
}
MARK(header_field); MARK(header_field);
@ -1211,20 +1312,19 @@ size_t http_parser_execute (http_parser *parser,
break; break;
} }
SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
goto error; goto error;
} }
case s_header_value_start: case s_header_value_start:
{ {
if (ch == ' ') break; if (ch == ' ' || ch == '\t') break;
MARK(header_value); MARK(header_value);
state = s_header_value; state = s_header_value;
index = 0; index = 0;
c = LOWER(ch);
if (ch == CR) { if (ch == CR) {
CALLBACK(header_value); CALLBACK(header_value);
header_state = h_general; header_state = h_general;
@ -1238,6 +1338,8 @@ size_t http_parser_execute (http_parser *parser,
break; break;
} }
c = LOWER(ch);
switch (header_state) { switch (header_state) {
case h_upgrade: case h_upgrade:
parser->flags |= F_UPGRADE; parser->flags |= F_UPGRADE;
@ -1254,7 +1356,11 @@ size_t http_parser_execute (http_parser *parser,
break; break;
case h_content_length: case h_content_length:
if (!IS_NUM(ch)) goto error; if (!IS_NUM(ch)) {
SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
goto error;
}
parser->content_length = ch - '0'; parser->content_length = ch - '0';
break; break;
@ -1279,7 +1385,6 @@ size_t http_parser_execute (http_parser *parser,
case s_header_value: case s_header_value:
{ {
c = LOWER(ch);
if (ch == CR) { if (ch == CR) {
CALLBACK(header_value); CALLBACK(header_value);
@ -1292,6 +1397,8 @@ size_t http_parser_execute (http_parser *parser,
goto header_almost_done; goto header_almost_done;
} }
c = LOWER(ch);
switch (header_state) { switch (header_state) {
case h_general: case h_general:
break; break;
@ -1303,7 +1410,11 @@ size_t http_parser_execute (http_parser *parser,
case h_content_length: case h_content_length:
if (ch == ' ') break; if (ch == ' ') break;
if (!IS_NUM(ch)) goto error; if (!IS_NUM(ch)) {
SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
goto error;
}
parser->content_length *= 10; parser->content_length *= 10;
parser->content_length += ch - '0'; parser->content_length += ch - '0';
break; break;
@ -1359,7 +1470,7 @@ size_t http_parser_execute (http_parser *parser,
{ {
STRICT_CHECK(ch != LF); STRICT_CHECK(ch != LF);
state = s_header_field_start; state = s_header_value_lws;
switch (header_state) { switch (header_state) {
case h_connection_keep_alive: case h_connection_keep_alive:
@ -1377,6 +1488,18 @@ size_t http_parser_execute (http_parser *parser,
break; break;
} }
case s_header_value_lws:
{
if (ch == ' ' || ch == '\t')
state = s_header_value_start;
else
{
state = s_header_field_start;
goto header_field_start;
}
break;
}
case s_headers_almost_done: case s_headers_almost_done:
headers_almost_done: headers_almost_done:
{ {
@ -1412,6 +1535,7 @@ size_t http_parser_execute (http_parser *parser,
default: default:
parser->state = state; parser->state = state;
SET_ERRNO(HPE_CB_headers_complete);
return p - data; /* Error */ return p - data; /* Error */
} }
} }
@ -1419,7 +1543,7 @@ size_t http_parser_execute (http_parser *parser,
/* Exit, the rest of the connect is in a different protocol. */ /* Exit, the rest of the connect is in a different protocol. */
if (parser->upgrade) { if (parser->upgrade) {
CALLBACK2(message_complete); CALLBACK2(message_complete);
return (p - data); return (p - data) + 1;
} }
if (parser->flags & F_SKIPBODY) { if (parser->flags & F_SKIPBODY) {
@ -1478,9 +1602,13 @@ size_t http_parser_execute (http_parser *parser,
assert(nread == 1); assert(nread == 1);
assert(parser->flags & F_CHUNKED); assert(parser->flags & F_CHUNKED);
c = unhex[(unsigned char)ch]; unhex_val = unhex[(unsigned char)ch];
if (c == -1) goto error; if (unhex_val == -1) {
parser->content_length = c; SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
goto error;
}
parser->content_length = unhex_val;
state = s_chunk_size; state = s_chunk_size;
break; break;
} }
@ -1494,18 +1622,20 @@ size_t http_parser_execute (http_parser *parser,
break; break;
} }
c = unhex[(unsigned char)ch]; unhex_val = unhex[(unsigned char)ch];
if (c == -1) { if (unhex_val == -1) {
if (ch == ';' || ch == ' ') { if (ch == ';' || ch == ' ') {
state = s_chunk_parameters; state = s_chunk_parameters;
break; break;
} }
SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
goto error; goto error;
} }
parser->content_length *= 16; parser->content_length *= 16;
parser->content_length += c; parser->content_length += unhex_val;
break; break;
} }
@ -1569,16 +1699,14 @@ size_t http_parser_execute (http_parser *parser,
default: default:
assert(0 && "unhandled state"); assert(0 && "unhandled state");
SET_ERRNO(HPE_INVALID_INTERNAL_STATE);
goto error; goto error;
} }
} }
CALLBACK_NOCLEAR(header_field); CALLBACK(header_field);
CALLBACK_NOCLEAR(header_value); CALLBACK(header_value);
CALLBACK_NOCLEAR(fragment); CALLBACK(url);
CALLBACK_NOCLEAR(query_string);
CALLBACK_NOCLEAR(path);
CALLBACK_NOCLEAR(url);
parser->state = state; parser->state = state;
parser->header_state = header_state; parser->header_state = header_state;
@ -1588,7 +1716,10 @@ size_t http_parser_execute (http_parser *parser,
return len; return len;
error: error:
parser->state = s_dead; if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {
SET_ERRNO(HPE_UNKNOWN);
}
return (p - data); return (p - data);
} }
@ -1629,4 +1760,17 @@ http_parser_init (http_parser *parser, enum http_parser_type t)
parser->upgrade = 0; parser->upgrade = 0;
parser->flags = 0; parser->flags = 0;
parser->method = 0; parser->method = 0;
parser->http_errno = 0;
}
const char *
http_errno_name(enum http_errno err) {
assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));
return http_strerror_tab[err].name;
}
const char *
http_errno_description(enum http_errno err) {
assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));
return http_strerror_tab[err].description;
} }

50
deps/http_parser/http_parser.gyp

@ -0,0 +1,50 @@
# This file is used with the GYP meta build system.
# http://code.google.com/p/gyp/
# To build try this:
# svn co http://gyp.googlecode.com/svn/trunk gyp
# ./gyp/gyp -f make --depth=`pwd` http_parser.gyp
# ./out/Debug/test
{
'target_defaults': {
'configurations': {
'Debug': {
'defines': [ 'DEBUG', '_DEBUG' ]
},
'Release': {
'defines': [ 'NDEBUG' ]
}
}
},
'targets': [
{
'target_name': 'http_parser',
'type': 'static_library',
'include_dirs': [ '.' ],
'direct_dependent_settings': {
'include_dirs': [ '.' ],
},
'defines': [ 'HTTP_PARSER_STRICT=0' ],
'sources': [ './http_parser.c', ],
'conditions': [
['OS=="win"', {
'msvs_settings': {
'VCCLCompilerTool': {
# Compile as C++. http_parser.c is actually C99, but C++ is
# close enough in this case.
'CompileAs': 2,
},
},
}]
],
},
{
'target_name': 'test',
'type': 'executable',
'dependencies': [ 'http_parser' ],
'sources': [ 'test.c' ]
}
]
}

94
deps/http_parser/http_parser.h

@ -28,7 +28,7 @@ extern "C" {
#define HTTP_PARSER_VERSION_MINOR 0 #define HTTP_PARSER_VERSION_MINOR 0
#include <sys/types.h> #include <sys/types.h>
#if defined(_WIN32) && !defined(__MINGW32__) #if defined(_WIN32) && !defined(__MINGW32__) && !defined(_MSC_VER)
typedef __int8 int8_t; typedef __int8 int8_t;
typedef unsigned __int8 uint8_t; typedef unsigned __int8 uint8_t;
typedef __int16 int16_t; typedef __int16 int16_t;
@ -51,6 +51,13 @@ typedef int ssize_t;
# define HTTP_PARSER_STRICT 1 # define HTTP_PARSER_STRICT 1
#endif #endif
/* Compile with -DHTTP_PARSER_DEBUG=1 to add extra debugging information to
* the error reporting facility.
*/
#ifndef HTTP_PARSER_DEBUG
# define HTTP_PARSER_DEBUG 0
#endif
/* Maximium header size allowed */ /* Maximium header size allowed */
#define HTTP_MAX_HEADER_SIZE (80*1024) #define HTTP_MAX_HEADER_SIZE (80*1024)
@ -58,6 +65,7 @@ typedef int ssize_t;
typedef struct http_parser http_parser; typedef struct http_parser http_parser;
typedef struct http_parser_settings http_parser_settings; typedef struct http_parser_settings http_parser_settings;
typedef struct http_parser_result http_parser_result;
/* Callbacks should return non-zero to indicate an error. The parser will /* Callbacks should return non-zero to indicate an error. The parser will
@ -125,6 +133,72 @@ enum flags
}; };
/* Map for errno-related constants
*
* The provided argument should be a macro that takes 2 arguments.
*/
#define HTTP_ERRNO_MAP(XX) \
/* No error */ \
XX(OK, "success") \
\
/* Callback-related errors */ \
XX(CB_message_begin, "the on_message_begin callback failed") \
XX(CB_path, "the on_path callback failed") \
XX(CB_query_string, "the on_query_string callback failed") \
XX(CB_url, "the on_url callback failed") \
XX(CB_fragment, "the on_fragment callback failed") \
XX(CB_header_field, "the on_header_field callback failed") \
XX(CB_header_value, "the on_header_value callback failed") \
XX(CB_headers_complete, "the on_headers_complete callback failed") \
XX(CB_body, "the on_body callback failed") \
XX(CB_message_complete, "the on_message_complete callback failed") \
\
/* Parsing-related errors */ \
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
XX(HEADER_OVERFLOW, \
"too many header bytes seen; overflow detected") \
XX(CLOSED_CONNECTION, \
"data received after completed connection: close message") \
XX(INVALID_VERSION, "invalid HTTP version") \
XX(INVALID_STATUS, "invalid HTTP status code") \
XX(INVALID_METHOD, "invalid HTTP method") \
XX(INVALID_URL, "invalid URL") \
XX(INVALID_HOST, "invalid host") \
XX(INVALID_PORT, "invalid port") \
XX(INVALID_PATH, "invalid path") \
XX(INVALID_QUERY_STRING, "invalid query string") \
XX(INVALID_FRAGMENT, "invalid fragment") \
XX(LF_EXPECTED, "LF character expected") \
XX(INVALID_HEADER_TOKEN, "invalid character in header") \
XX(INVALID_CONTENT_LENGTH, \
"invalid character in content-length header") \
XX(INVALID_CHUNK_SIZE, \
"invalid character in chunk size header") \
XX(INVALID_CONSTANT, "invalid constant string") \
XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
XX(STRICT, "strict mode assertion failed") \
XX(UNKNOWN, "an unknown error occurred")
/* Define HPE_* values for each errno value above */
#define HTTP_ERRNO_GEN(n, s) HPE_##n,
enum http_errno {
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
};
#undef HTTP_ERRNO_GEN
/* Get an http_errno value from an http_parser */
#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
/* Get the line number that generated the current error */
#if HTTP_PARSER_DEBUG
#define HTTP_PARSER_ERRNO_LINE(p) ((p)->error_lineno)
#else
#define HTTP_PARSER_ERRNO_LINE(p) 0
#endif
struct http_parser { struct http_parser {
/** PRIVATE **/ /** PRIVATE **/
unsigned char type : 2; unsigned char type : 2;
@ -141,13 +215,18 @@ struct http_parser {
unsigned short http_minor; unsigned short http_minor;
unsigned short status_code; /* responses only */ unsigned short status_code; /* responses only */
unsigned char method; /* requests only */ unsigned char method; /* requests only */
unsigned char http_errno : 7;
/* 1 = Upgrade header was present and the parser has exited because of that. /* 1 = Upgrade header was present and the parser has exited because of that.
* 0 = No upgrade header present. * 0 = No upgrade header present.
* Should be checked when http_parser_execute() returns in addition to * Should be checked when http_parser_execute() returns in addition to
* error checking. * error checking.
*/ */
char upgrade; char upgrade : 1;
#if HTTP_PARSER_DEBUG
uint32_t error_lineno;
#endif
/** PUBLIC **/ /** PUBLIC **/
void *data; /* A pointer to get hook to the "connection" or "socket" object */ void *data; /* A pointer to get hook to the "connection" or "socket" object */
@ -156,10 +235,7 @@ struct http_parser {
struct http_parser_settings { struct http_parser_settings {
http_cb on_message_begin; http_cb on_message_begin;
http_data_cb on_path;
http_data_cb on_query_string;
http_data_cb on_url; http_data_cb on_url;
http_data_cb on_fragment;
http_data_cb on_header_field; http_data_cb on_header_field;
http_data_cb on_header_value; http_data_cb on_header_value;
http_cb on_headers_complete; http_cb on_headers_complete;
@ -186,7 +262,13 @@ size_t http_parser_execute(http_parser *parser,
int http_should_keep_alive(http_parser *parser); int http_should_keep_alive(http_parser *parser);
/* Returns a string version of the HTTP method. */ /* Returns a string version of the HTTP method. */
const char *http_method_str(enum http_method); const char *http_method_str(enum http_method m);
/* Return a string name of the given error */
const char *http_errno_name(enum http_errno err);
/* Return a string description of the given error */
const char *http_errno_description(enum http_errno err);
#ifdef __cplusplus #ifdef __cplusplus
} }

376
deps/http_parser/test.c

@ -44,10 +44,7 @@ struct message {
enum http_parser_type type; enum http_parser_type type;
enum http_method method; enum http_method method;
int status_code; int status_code;
char request_path[MAX_ELEMENT_SIZE];
char request_url[MAX_ELEMENT_SIZE]; char request_url[MAX_ELEMENT_SIZE];
char fragment[MAX_ELEMENT_SIZE];
char query_string[MAX_ELEMENT_SIZE];
char body[MAX_ELEMENT_SIZE]; char body[MAX_ELEMENT_SIZE];
size_t body_size; size_t body_size;
int num_headers; int num_headers;
@ -55,7 +52,7 @@ struct message {
char headers [MAX_HEADERS][2][MAX_ELEMENT_SIZE]; char headers [MAX_HEADERS][2][MAX_ELEMENT_SIZE];
int should_keep_alive; int should_keep_alive;
int upgrade; const char *upgrade; // upgraded body
unsigned short http_major; unsigned short http_major;
unsigned short http_minor; unsigned short http_minor;
@ -86,9 +83,6 @@ const struct message requests[] =
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_GET ,.method= HTTP_GET
,.query_string= ""
,.fragment= ""
,.request_path= "/test"
,.request_url= "/test" ,.request_url= "/test"
,.num_headers= 3 ,.num_headers= 3
,.headers= ,.headers=
@ -117,9 +111,6 @@ const struct message requests[] =
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_GET ,.method= HTTP_GET
,.query_string= ""
,.fragment= ""
,.request_path= "/favicon.ico"
,.request_url= "/favicon.ico" ,.request_url= "/favicon.ico"
,.num_headers= 8 ,.num_headers= 8
,.headers= ,.headers=
@ -146,9 +137,6 @@ const struct message requests[] =
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_GET ,.method= HTTP_GET
,.query_string= ""
,.fragment= ""
,.request_path= "/dumbfuck"
,.request_url= "/dumbfuck" ,.request_url= "/dumbfuck"
,.num_headers= 1 ,.num_headers= 1
,.headers= ,.headers=
@ -167,9 +155,6 @@ const struct message requests[] =
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_GET ,.method= HTTP_GET
,.query_string= "page=1"
,.fragment= "posts-17408"
,.request_path= "/forums/1/topics/2375"
/* XXX request url does include fragment? */ /* XXX request url does include fragment? */
,.request_url= "/forums/1/topics/2375?page=1#posts-17408" ,.request_url= "/forums/1/topics/2375?page=1#posts-17408"
,.num_headers= 0 ,.num_headers= 0
@ -186,9 +171,6 @@ const struct message requests[] =
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_GET ,.method= HTTP_GET
,.query_string= ""
,.fragment= ""
,.request_path= "/get_no_headers_no_body/world"
,.request_url= "/get_no_headers_no_body/world" ,.request_url= "/get_no_headers_no_body/world"
,.num_headers= 0 ,.num_headers= 0
,.body= "" ,.body= ""
@ -205,9 +187,6 @@ const struct message requests[] =
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_GET ,.method= HTTP_GET
,.query_string= ""
,.fragment= ""
,.request_path= "/get_one_header_no_body"
,.request_url= "/get_one_header_no_body" ,.request_url= "/get_one_header_no_body"
,.num_headers= 1 ,.num_headers= 1
,.headers= ,.headers=
@ -228,9 +207,6 @@ const struct message requests[] =
,.http_major= 1 ,.http_major= 1
,.http_minor= 0 ,.http_minor= 0
,.method= HTTP_GET ,.method= HTTP_GET
,.query_string= ""
,.fragment= ""
,.request_path= "/get_funky_content_length_body_hello"
,.request_url= "/get_funky_content_length_body_hello" ,.request_url= "/get_funky_content_length_body_hello"
,.num_headers= 1 ,.num_headers= 1
,.headers= ,.headers=
@ -253,9 +229,6 @@ const struct message requests[] =
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_POST ,.method= HTTP_POST
,.query_string= "q=search"
,.fragment= "hey"
,.request_path= "/post_identity_body_world"
,.request_url= "/post_identity_body_world?q=search#hey" ,.request_url= "/post_identity_body_world?q=search#hey"
,.num_headers= 3 ,.num_headers= 3
,.headers= ,.headers=
@ -280,9 +253,6 @@ const struct message requests[] =
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_POST ,.method= HTTP_POST
,.query_string= ""
,.fragment= ""
,.request_path= "/post_chunked_all_your_base"
,.request_url= "/post_chunked_all_your_base" ,.request_url= "/post_chunked_all_your_base"
,.num_headers= 1 ,.num_headers= 1
,.headers= ,.headers=
@ -306,9 +276,6 @@ const struct message requests[] =
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_POST ,.method= HTTP_POST
,.query_string= ""
,.fragment= ""
,.request_path= "/two_chunks_mult_zero_end"
,.request_url= "/two_chunks_mult_zero_end" ,.request_url= "/two_chunks_mult_zero_end"
,.num_headers= 1 ,.num_headers= 1
,.headers= ,.headers=
@ -334,9 +301,6 @@ const struct message requests[] =
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_POST ,.method= HTTP_POST
,.query_string= ""
,.fragment= ""
,.request_path= "/chunked_w_trailing_headers"
,.request_url= "/chunked_w_trailing_headers" ,.request_url= "/chunked_w_trailing_headers"
,.num_headers= 3 ,.num_headers= 3
,.headers= ,.headers=
@ -362,9 +326,6 @@ const struct message requests[] =
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_POST ,.method= HTTP_POST
,.query_string= ""
,.fragment= ""
,.request_path= "/chunked_w_bullshit_after_length"
,.request_url= "/chunked_w_bullshit_after_length" ,.request_url= "/chunked_w_bullshit_after_length"
,.num_headers= 1 ,.num_headers= 1
,.headers= ,.headers=
@ -382,9 +343,6 @@ const struct message requests[] =
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_GET ,.method= HTTP_GET
,.query_string= "foo=\"bar\""
,.fragment= ""
,.request_path= "/with_\"stupid\"_quotes"
,.request_url= "/with_\"stupid\"_quotes?foo=\"bar\"" ,.request_url= "/with_\"stupid\"_quotes?foo=\"bar\""
,.num_headers= 0 ,.num_headers= 0
,.headers= { } ,.headers= { }
@ -408,9 +366,6 @@ const struct message requests[] =
,.http_major= 1 ,.http_major= 1
,.http_minor= 0 ,.http_minor= 0
,.method= HTTP_GET ,.method= HTTP_GET
,.query_string= ""
,.fragment= ""
,.request_path= "/test"
,.request_url= "/test" ,.request_url= "/test"
,.num_headers= 3 ,.num_headers= 3
,.headers= { { "Host", "0.0.0.0:5000" } ,.headers= { { "Host", "0.0.0.0:5000" }
@ -431,9 +386,6 @@ const struct message requests[] =
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_GET ,.method= HTTP_GET
,.query_string= "foo=bar?baz"
,.fragment= ""
,.request_path= "/test.cgi"
,.request_url= "/test.cgi?foo=bar?baz" ,.request_url= "/test.cgi?foo=bar?baz"
,.num_headers= 0 ,.num_headers= 0
,.headers= {} ,.headers= {}
@ -452,9 +404,6 @@ const struct message requests[] =
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_GET ,.method= HTTP_GET
,.query_string= ""
,.fragment= ""
,.request_path= "/test"
,.request_url= "/test" ,.request_url= "/test"
,.num_headers= 0 ,.num_headers= 0
,.headers= { } ,.headers= { }
@ -473,17 +422,15 @@ const struct message requests[] =
"Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n"
"Origin: http://example.com\r\n" "Origin: http://example.com\r\n"
"\r\n" "\r\n"
"Hot diggity dogg"
,.should_keep_alive= TRUE ,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE ,.message_complete_on_eof= FALSE
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_GET ,.method= HTTP_GET
,.query_string= ""
,.fragment= ""
,.request_path= "/demo"
,.request_url= "/demo" ,.request_url= "/demo"
,.num_headers= 7 ,.num_headers= 7
,.upgrade=1 ,.upgrade="Hot diggity dogg"
,.headers= { { "Host", "example.com" } ,.headers= { { "Host", "example.com" }
, { "Connection", "Upgrade" } , { "Connection", "Upgrade" }
, { "Sec-WebSocket-Key2", "12998 5 Y3 1 .P00" } , { "Sec-WebSocket-Key2", "12998 5 Y3 1 .P00" }
@ -502,17 +449,16 @@ const struct message requests[] =
"User-agent: Mozilla/1.1N\r\n" "User-agent: Mozilla/1.1N\r\n"
"Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n" "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
"\r\n" "\r\n"
"some data\r\n"
"and yet even more data"
,.should_keep_alive= FALSE ,.should_keep_alive= FALSE
,.message_complete_on_eof= FALSE ,.message_complete_on_eof= FALSE
,.http_major= 1 ,.http_major= 1
,.http_minor= 0 ,.http_minor= 0
,.method= HTTP_CONNECT ,.method= HTTP_CONNECT
,.query_string= ""
,.fragment= ""
,.request_path= ""
,.request_url= "0-home0.netscape.com:443" ,.request_url= "0-home0.netscape.com:443"
,.num_headers= 2 ,.num_headers= 2
,.upgrade=1 ,.upgrade="some data\r\nand yet even more data"
,.headers= { { "User-agent", "Mozilla/1.1N" } ,.headers= { { "User-agent", "Mozilla/1.1N" }
, { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" } , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
} }
@ -529,9 +475,6 @@ const struct message requests[] =
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_REPORT ,.method= HTTP_REPORT
,.query_string= ""
,.fragment= ""
,.request_path= "/test"
,.request_url= "/test" ,.request_url= "/test"
,.num_headers= 0 ,.num_headers= 0
,.headers= {} ,.headers= {}
@ -548,9 +491,6 @@ const struct message requests[] =
,.http_major= 0 ,.http_major= 0
,.http_minor= 9 ,.http_minor= 9
,.method= HTTP_GET ,.method= HTTP_GET
,.query_string= ""
,.fragment= ""
,.request_path= "/"
,.request_url= "/" ,.request_url= "/"
,.num_headers= 0 ,.num_headers= 0
,.headers= {} ,.headers= {}
@ -570,9 +510,6 @@ const struct message requests[] =
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_MSEARCH ,.method= HTTP_MSEARCH
,.query_string= ""
,.fragment= ""
,.request_path= "*"
,.request_url= "*" ,.request_url= "*"
,.num_headers= 3 ,.num_headers= 3
,.headers= { { "HOST", "239.255.255.250:1900" } ,.headers= { { "HOST", "239.255.255.250:1900" }
@ -582,6 +519,32 @@ const struct message requests[] =
,.body= "" ,.body= ""
} }
#define LINE_FOLDING_IN_HEADER 20
, {.name= "line folding in header value"
,.type= HTTP_REQUEST
,.raw= "GET / HTTP/1.1\r\n"
"Line1: abc\r\n"
"\tdef\r\n"
" ghi\r\n"
"\t\tjkl\r\n"
" mno \r\n"
"\t \tqrs\r\n"
"Line2: \t line2\t\r\n"
"\r\n"
,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE
,.http_major= 1
,.http_minor= 1
,.method= HTTP_GET
,.request_url= "/"
,.num_headers= 2
,.headers= { { "Line1", "abcdefghijklmno qrs" }
, { "Line2", "line2\t" }
}
,.body= ""
}
#define QUERY_TERMINATED_HOST 21 #define QUERY_TERMINATED_HOST 21
, {.name= "host terminated by a query string" , {.name= "host terminated by a query string"
,.type= HTTP_REQUEST ,.type= HTTP_REQUEST
@ -592,9 +555,6 @@ const struct message requests[] =
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_GET ,.method= HTTP_GET
,.query_string= "hail=all"
,.fragment= ""
,.request_path= ""
,.request_url= "http://hypnotoad.org?hail=all" ,.request_url= "http://hypnotoad.org?hail=all"
,.num_headers= 0 ,.num_headers= 0
,.headers= { } ,.headers= { }
@ -611,9 +571,6 @@ const struct message requests[] =
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_GET ,.method= HTTP_GET
,.query_string= "hail=all"
,.fragment= ""
,.request_path= ""
,.request_url= "http://hypnotoad.org:1234?hail=all" ,.request_url= "http://hypnotoad.org:1234?hail=all"
,.num_headers= 0 ,.num_headers= 0
,.headers= { } ,.headers= { }
@ -630,9 +587,6 @@ const struct message requests[] =
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_GET ,.method= HTTP_GET
,.query_string= ""
,.fragment= ""
,.request_path= ""
,.request_url= "http://hypnotoad.org:1234" ,.request_url= "http://hypnotoad.org:1234"
,.num_headers= 0 ,.num_headers= 0
,.headers= { } ,.headers= { }
@ -651,9 +605,6 @@ const struct message requests[] =
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_GET ,.method= HTTP_GET
,.query_string= "q=1"
,.fragment= "narf"
,.request_path= "/δ¶/δt/pope"
,.request_url= "/δ¶/δt/pope?q=1#narf" ,.request_url= "/δ¶/δt/pope?q=1#narf"
,.num_headers= 1 ,.num_headers= 1
,.headers= { {"Host", "github.com" } ,.headers= { {"Host", "github.com" }
@ -673,12 +624,9 @@ const struct message requests[] =
,.http_major= 1 ,.http_major= 1
,.http_minor= 0 ,.http_minor= 0
,.method= HTTP_CONNECT ,.method= HTTP_CONNECT
,.query_string= ""
,.fragment= ""
,.request_path= ""
,.request_url= "home_0.netscape.com:443" ,.request_url= "home_0.netscape.com:443"
,.num_headers= 2 ,.num_headers= 2
,.upgrade=1 ,.upgrade=""
,.headers= { { "User-agent", "Mozilla/1.1N" } ,.headers= { { "User-agent", "Mozilla/1.1N" }
, { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" } , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
} }
@ -701,12 +649,8 @@ const struct message requests[] =
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_PATCH ,.method= HTTP_PATCH
,.query_string= ""
,.fragment= ""
,.request_path= "/file.txt"
,.request_url= "/file.txt" ,.request_url= "/file.txt"
,.num_headers= 4 ,.num_headers= 4
,.upgrade=0
,.headers= { { "Host", "www.example.com" } ,.headers= { { "Host", "www.example.com" }
, { "Content-Type", "application/example" } , { "Content-Type", "application/example" }
, { "If-Match", "\"e0023aa4e\"" } , { "If-Match", "\"e0023aa4e\"" }
@ -715,6 +659,27 @@ const struct message requests[] =
,.body= "cccccccccc" ,.body= "cccccccccc"
} }
#define CONNECT_CAPS_REQUEST 27
, {.name = "connect caps request"
,.type= HTTP_REQUEST
,.raw= "CONNECT HOME0.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
,.request_url= "HOME0.NETSCAPE.COM:443"
,.num_headers= 2
,.upgrade=""
,.headers= { { "User-agent", "Mozilla/1.1N" }
, { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
}
,.body= ""
}
, {.name= NULL } /* sentinel */ , {.name= NULL } /* sentinel */
}; };
@ -1080,14 +1045,6 @@ const struct message responses[] =
, {.name= NULL } /* sentinel */ , {.name= NULL } /* sentinel */
}; };
int
request_path_cb (http_parser *p, const char *buf, size_t len)
{
assert(p == parser);
strncat(messages[num_messages].request_path, buf, len);
return 0;
}
int int
request_url_cb (http_parser *p, const char *buf, size_t len) request_url_cb (http_parser *p, const char *buf, size_t len)
{ {
@ -1096,22 +1053,6 @@ request_url_cb (http_parser *p, const char *buf, size_t len)
return 0; return 0;
} }
int
query_string_cb (http_parser *p, const char *buf, size_t len)
{
assert(p == parser);
strncat(messages[num_messages].query_string, buf, len);
return 0;
}
int
fragment_cb (http_parser *p, const char *buf, size_t len)
{
assert(p == parser);
strncat(messages[num_messages].fragment, buf, len);
return 0;
}
int int
header_field_cb (http_parser *p, const char *buf, size_t len) header_field_cb (http_parser *p, const char *buf, size_t len)
{ {
@ -1205,10 +1146,7 @@ static http_parser_settings settings =
{.on_message_begin = message_begin_cb {.on_message_begin = message_begin_cb
,.on_header_field = header_field_cb ,.on_header_field = header_field_cb
,.on_header_value = header_value_cb ,.on_header_value = header_value_cb
,.on_path = request_path_cb
,.on_url = request_url_cb ,.on_url = request_url_cb
,.on_fragment = fragment_cb
,.on_query_string = query_string_cb
,.on_body = body_cb ,.on_body = body_cb
,.on_headers_complete = headers_complete_cb ,.on_headers_complete = headers_complete_cb
,.on_message_complete = message_complete_cb ,.on_message_complete = message_complete_cb
@ -1218,10 +1156,7 @@ static http_parser_settings settings_count_body =
{.on_message_begin = message_begin_cb {.on_message_begin = message_begin_cb
,.on_header_field = header_field_cb ,.on_header_field = header_field_cb
,.on_header_value = header_value_cb ,.on_header_value = header_value_cb
,.on_path = request_path_cb
,.on_url = request_url_cb ,.on_url = request_url_cb
,.on_fragment = fragment_cb
,.on_query_string = query_string_cb
,.on_body = count_body_cb ,.on_body = count_body_cb
,.on_headers_complete = headers_complete_cb ,.on_headers_complete = headers_complete_cb
,.on_message_complete = message_complete_cb ,.on_message_complete = message_complete_cb
@ -1231,10 +1166,7 @@ static http_parser_settings settings_null =
{.on_message_begin = 0 {.on_message_begin = 0
,.on_header_field = 0 ,.on_header_field = 0
,.on_header_value = 0 ,.on_header_value = 0
,.on_path = 0
,.on_url = 0 ,.on_url = 0
,.on_fragment = 0
,.on_query_string = 0
,.on_body = 0 ,.on_body = 0
,.on_headers_complete = 0 ,.on_headers_complete = 0
,.on_message_complete = 0 ,.on_message_complete = 0
@ -1284,7 +1216,13 @@ check_str_eq (const struct message *m,
const char *prop, const char *prop,
const char *expected, const char *expected,
const char *found) { const char *found) {
if (0 != strcmp(expected, found)) { if ((expected == NULL) != (found == NULL)) {
printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
printf("expected %s\n", (expected == NULL) ? "NULL" : expected);
printf(" found %s\n", (found == NULL) ? "NULL" : found);
return 0;
}
if (expected != NULL && 0 != strcmp(expected, found)) {
printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name); printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
printf("expected '%s'\n", expected); printf("expected '%s'\n", expected);
printf(" found '%s'\n", found); printf(" found '%s'\n", found);
@ -1337,9 +1275,6 @@ message_eq (int index, const struct message *expected)
assert(m->message_complete_cb_called); assert(m->message_complete_cb_called);
MESSAGE_CHECK_STR_EQ(expected, m, request_path);
MESSAGE_CHECK_STR_EQ(expected, m, query_string);
MESSAGE_CHECK_STR_EQ(expected, m, fragment);
MESSAGE_CHECK_STR_EQ(expected, m, request_url); MESSAGE_CHECK_STR_EQ(expected, m, request_url);
if (expected->body_size) { if (expected->body_size) {
MESSAGE_CHECK_NUM_EQ(expected, m, body_size); MESSAGE_CHECK_NUM_EQ(expected, m, body_size);
@ -1357,13 +1292,81 @@ message_eq (int index, const struct message *expected)
if (!r) return 0; if (!r) return 0;
} }
MESSAGE_CHECK_STR_EQ(expected, m, upgrade);
return 1; return 1;
} }
/* Given a sequence of varargs messages, return the number of them that the
* parser should successfully parse, taking into account that upgraded
* messages prevent all subsequent messages from being parsed.
*/
size_t
count_parsed_messages(const size_t nmsgs, ...) {
size_t i;
va_list ap;
va_start(ap, nmsgs);
for (i = 0; i < nmsgs; i++) {
struct message *m = va_arg(ap, struct message *);
if (m->upgrade) {
va_end(ap);
return i + 1;
}
}
va_end(ap);
return nmsgs;
}
/* Given a sequence of bytes and the number of these that we were able to
* parse, verify that upgrade bodies are correct.
*/
void
upgrade_message_fix(char *body, const size_t nread, const size_t nmsgs, ...) {
va_list ap;
size_t i;
size_t off = 0;
va_start(ap, nmsgs);
for (i = 0; i < nmsgs; i++) {
struct message *m = va_arg(ap, struct message *);
off += strlen(m->raw);
if (m->upgrade) {
off -= strlen(m->upgrade);
/* Check the portion of the response after its specified upgrade */
if (!check_str_eq(m, "upgrade", body + off, body + nread)) {
exit(1);
}
/* Fix up the response so that message_eq() will verify the beginning
* of the upgrade */
*(body + nread + strlen(m->upgrade)) = '\0';
messages[num_messages -1 ].upgrade = body + nread;
va_end(ap);
return;
}
}
va_end(ap);
printf("\n\n*** Error: expected a message with upgrade ***\n");
exit(1);
}
static void static void
print_error (const char *raw, size_t error_location) print_error (const char *raw, size_t error_location)
{ {
fprintf(stderr, "\n*** parse error ***\n\n"); fprintf(stderr, "\n*** %s:%d -- %s ***\n\n",
"http_parser.c", HTTP_PARSER_ERRNO_LINE(parser),
http_errno_description(HTTP_PARSER_ERRNO(parser)));
int this_line = 0, char_len = 0; int this_line = 0, char_len = 0;
size_t i, j, len = strlen(raw), error_location_line = 0; size_t i, j, len = strlen(raw), error_location_line = 0;
@ -1418,7 +1421,10 @@ test_message (const struct message *message)
if (msg1len) { if (msg1len) {
read = parse(msg1, msg1len); read = parse(msg1, msg1len);
if (message->upgrade && parser->upgrade) goto test; if (message->upgrade && parser->upgrade) {
messages[num_messages - 1].upgrade = msg1 + read;
goto test;
}
if (read != msg1len) { if (read != msg1len) {
print_error(msg1, read); print_error(msg1, read);
@ -1429,7 +1435,10 @@ test_message (const struct message *message)
read = parse(msg2, msg2len); read = parse(msg2, msg2len);
if (message->upgrade && parser->upgrade) goto test; if (message->upgrade && parser->upgrade) {
messages[num_messages - 1].upgrade = msg2 + read;
goto test;
}
if (read != msg2len) { if (read != msg2len) {
print_error(msg2, read); print_error(msg2, read);
@ -1438,8 +1447,6 @@ test_message (const struct message *message)
read = parse(NULL, 0); read = parse(NULL, 0);
if (message->upgrade && parser->upgrade) goto test;
if (read != 0) { if (read != 0) {
print_error(message->raw, read); print_error(message->raw, read);
exit(1); exit(1);
@ -1495,21 +1502,32 @@ test_message_count_body (const struct message *message)
} }
void void
test_simple (const char *buf, int should_pass) test_simple (const char *buf, enum http_errno err_expected)
{ {
parser_init(HTTP_REQUEST); parser_init(HTTP_REQUEST);
size_t parsed; size_t parsed;
int pass; int pass;
enum http_errno err;
parsed = parse(buf, strlen(buf)); parsed = parse(buf, strlen(buf));
pass = (parsed == strlen(buf)); pass = (parsed == strlen(buf));
err = HTTP_PARSER_ERRNO(parser);
parsed = parse(NULL, 0); parsed = parse(NULL, 0);
pass &= (parsed == 0); pass &= (parsed == 0);
parser_free(); parser_free();
if (pass != should_pass) { /* In strict mode, allow us to pass with an unexpected HPE_STRICT as
fprintf(stderr, "\n*** test_simple expected %s ***\n\n%s", should_pass ? "success" : "error", buf); * long as the caller isn't expecting success.
*/
#if HTTP_PARSER_STRICT
if (err_expected != err && err_expected != HPE_OK && err != HPE_STRICT) {
#else
if (err_expected != err) {
#endif
fprintf(stderr, "\n*** test_simple expected %s, but saw %s ***\n\n%s\n",
http_errno_name(err_expected), http_errno_name(err), buf);
exit(1); exit(1);
} }
} }
@ -1526,10 +1544,14 @@ test_header_overflow_error (int req)
assert(parsed == strlen(buf)); assert(parsed == strlen(buf));
buf = "header-key: header-value\r\n"; buf = "header-key: header-value\r\n";
size_t buflen = strlen(buf);
int i; int i;
for (i = 0; i < 10000; i++) { for (i = 0; i < 10000; i++) {
if (http_parser_execute(&parser, &settings_null, buf, strlen(buf)) != strlen(buf)) { parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
if (parsed != buflen) {
//fprintf(stderr, "error found on iter %d\n", i); //fprintf(stderr, "error found on iter %d\n", i);
assert(HTTP_PARSER_ERRNO(&parser) == HPE_HEADER_OVERFLOW);
return; return;
} }
} }
@ -1574,12 +1596,7 @@ test_no_overflow_long_body (int req, size_t length)
void void
test_multiple3 (const struct message *r1, const struct message *r2, const struct message *r3) test_multiple3 (const struct message *r1, const struct message *r2, const struct message *r3)
{ {
int message_count = 1; int message_count = count_parsed_messages(3, r1, r2, r3);
if (!r1->upgrade) {
message_count++;
if (!r2->upgrade) message_count++;
}
int has_upgrade = (message_count < 3 || r3->upgrade);
char total[ strlen(r1->raw) char total[ strlen(r1->raw)
+ strlen(r2->raw) + strlen(r2->raw)
@ -1598,7 +1615,10 @@ test_multiple3 (const struct message *r1, const struct message *r2, const struct
read = parse(total, strlen(total)); read = parse(total, strlen(total));
if (has_upgrade && parser->upgrade) goto test; if (parser->upgrade) {
upgrade_message_fix(total, read, 3, r1, r2, r3);
goto test;
}
if (read != strlen(total)) { if (read != strlen(total)) {
print_error(total, read); print_error(total, read);
@ -1607,8 +1627,6 @@ test_multiple3 (const struct message *r1, const struct message *r2, const struct
read = parse(NULL, 0); read = parse(NULL, 0);
if (has_upgrade && parser->upgrade) goto test;
if (read != 0) { if (read != 0) {
print_error(total, read); print_error(total, read);
exit(1); exit(1);
@ -1622,12 +1640,8 @@ test:
} }
if (!message_eq(0, r1)) exit(1); if (!message_eq(0, r1)) exit(1);
if (message_count > 1) { if (message_count > 1 && !message_eq(1, r2)) exit(1);
if (!message_eq(1, r2)) exit(1); if (message_count > 2 && !message_eq(2, r3)) exit(1);
if (message_count > 2) {
if (!message_eq(2, r3)) exit(1);
}
}
parser_free(); parser_free();
} }
@ -1656,6 +1670,7 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
int ops = 0 ; int ops = 0 ;
size_t buf1_len, buf2_len, buf3_len; size_t buf1_len, buf2_len, buf3_len;
int message_count = count_parsed_messages(3, r1, r2, r3);
int i,j,type_both; int i,j,type_both;
for (type_both = 0; type_both < 2; type_both ++ ) { for (type_both = 0; type_both < 2; type_both ++ ) {
@ -1684,27 +1699,27 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
read = parse(buf1, buf1_len); read = parse(buf1, buf1_len);
if (r3->upgrade && parser->upgrade) goto test; if (parser->upgrade) goto test;
if (read != buf1_len) { if (read != buf1_len) {
print_error(buf1, read); print_error(buf1, read);
goto error; goto error;
} }
read = parse(buf2, buf2_len); read += parse(buf2, buf2_len);
if (r3->upgrade && parser->upgrade) goto test; if (parser->upgrade) goto test;
if (read != buf2_len) { if (read != buf1_len + buf2_len) {
print_error(buf2, read); print_error(buf2, read);
goto error; goto error;
} }
read = parse(buf3, buf3_len); read += parse(buf3, buf3_len);
if (r3->upgrade && parser->upgrade) goto test; if (parser->upgrade) goto test;
if (read != buf3_len) { if (read != buf1_len + buf2_len + buf3_len) {
print_error(buf3, read); print_error(buf3, read);
goto error; goto error;
} }
@ -1712,9 +1727,13 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
parse(NULL, 0); parse(NULL, 0);
test: test:
if (parser->upgrade) {
upgrade_message_fix(total, read, 3, r1, r2, r3);
}
if (3 != num_messages) { if (message_count != num_messages) {
fprintf(stderr, "\n\nParser didn't see 3 messages only %d\n", num_messages); fprintf(stderr, "\n\nParser didn't see %d messages only %d\n",
message_count, num_messages);
goto error; goto error;
} }
@ -1723,12 +1742,12 @@ test:
goto error; goto error;
} }
if (!message_eq(1, r2)) { if (message_count > 1 && !message_eq(1, r2)) {
fprintf(stderr, "\n\nError matching messages[1] in test_scan.\n"); fprintf(stderr, "\n\nError matching messages[1] in test_scan.\n");
goto error; goto error;
} }
if (!message_eq(2, r3)) { if (message_count > 2 && !message_eq(2, r3)) {
fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n"); fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n");
goto error; goto error;
} }
@ -1868,13 +1887,13 @@ main (void)
/// REQUESTS /// REQUESTS
test_simple("hello world", 0); test_simple("hello world", HPE_INVALID_METHOD);
test_simple("GET / HTP/1.1\r\n\r\n", 0); test_simple("GET / HTP/1.1\r\n\r\n", HPE_INVALID_VERSION);
test_simple("ASDF / HTTP/1.1\r\n\r\n", 0); test_simple("ASDF / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD);
test_simple("PROPPATCHA / HTTP/1.1\r\n\r\n", 0); test_simple("PROPPATCHA / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD);
test_simple("GETA / HTTP/1.1\r\n\r\n", 0); test_simple("GETA / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD);
// Well-formed but incomplete // Well-formed but incomplete
test_simple("GET / HTTP/1.1\r\n" test_simple("GET / HTTP/1.1\r\n"
@ -1882,7 +1901,7 @@ main (void)
"Content-Length: 6\r\n" "Content-Length: 6\r\n"
"\r\n" "\r\n"
"fooba", "fooba",
0); HPE_OK);
static const char *all_methods[] = { static const char *all_methods[] = {
"DELETE", "DELETE",
@ -1900,12 +1919,31 @@ main (void)
"PROPFIND", "PROPFIND",
"PROPPATCH", "PROPPATCH",
"UNLOCK", "UNLOCK",
"REPORT",
"MKACTIVITY",
"CHECKOUT",
"MERGE",
"M-SEARCH",
"NOTIFY",
"SUBSCRIBE",
"UNSUBSCRIBE",
"PATCH",
0 }; 0 };
const char **this_method; const char **this_method;
for (this_method = all_methods; *this_method; this_method++) { for (this_method = all_methods; *this_method; this_method++) {
char buf[200]; char buf[200];
sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method); sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
test_simple(buf, 1); test_simple(buf, HPE_OK);
}
static const char *bad_methods[] = {
"C******",
"M****",
0 };
for (this_method = bad_methods; *this_method; this_method++) {
char buf[200];
sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
test_simple(buf, HPE_UNKNOWN);
} }
const char *dumbfuck2 = const char *dumbfuck2 =
@ -1943,7 +1981,7 @@ main (void)
"\tRA==\r\n" "\tRA==\r\n"
"\t-----END CERTIFICATE-----\r\n" "\t-----END CERTIFICATE-----\r\n"
"\r\n"; "\r\n";
test_simple(dumbfuck2, 0); test_simple(dumbfuck2, HPE_OK);
#if 0 #if 0
// NOTE(Wed Nov 18 11:57:27 CET 2009) this seems okay. we just read body // NOTE(Wed Nov 18 11:57:27 CET 2009) this seems okay. we just read body

12
src/node_http_parser.cc

@ -48,10 +48,7 @@ namespace node {
using namespace v8; using namespace v8;
static Persistent<String> on_message_begin_sym; static Persistent<String> on_message_begin_sym;
static Persistent<String> on_path_sym;
static Persistent<String> on_query_string_sym;
static Persistent<String> on_url_sym; static Persistent<String> on_url_sym;
static Persistent<String> on_fragment_sym;
static Persistent<String> on_header_field_sym; static Persistent<String> on_header_field_sym;
static Persistent<String> on_header_value_sym; static Persistent<String> on_header_value_sym;
static Persistent<String> on_headers_complete_sym; static Persistent<String> on_headers_complete_sym;
@ -184,10 +181,7 @@ class Parser : public ObjectWrap {
DEFINE_HTTP_CB(on_message_begin) DEFINE_HTTP_CB(on_message_begin)
DEFINE_HTTP_CB(on_message_complete) DEFINE_HTTP_CB(on_message_complete)
DEFINE_HTTP_DATA_CB(on_path)
DEFINE_HTTP_DATA_CB(on_url) DEFINE_HTTP_DATA_CB(on_url)
DEFINE_HTTP_DATA_CB(on_fragment)
DEFINE_HTTP_DATA_CB(on_query_string)
DEFINE_HTTP_DATA_CB(on_header_field) DEFINE_HTTP_DATA_CB(on_header_field)
DEFINE_HTTP_DATA_CB(on_header_value) DEFINE_HTTP_DATA_CB(on_header_value)
DEFINE_HTTP_DATA_CB(on_body) DEFINE_HTTP_DATA_CB(on_body)
@ -387,10 +381,7 @@ void InitHttpParser(Handle<Object> target) {
target->Set(String::NewSymbol("HTTPParser"), t->GetFunction()); target->Set(String::NewSymbol("HTTPParser"), t->GetFunction());
on_message_begin_sym = NODE_PSYMBOL("onMessageBegin"); on_message_begin_sym = NODE_PSYMBOL("onMessageBegin");
on_path_sym = NODE_PSYMBOL("onPath");
on_query_string_sym = NODE_PSYMBOL("onQueryString");
on_url_sym = NODE_PSYMBOL("onURL"); on_url_sym = NODE_PSYMBOL("onURL");
on_fragment_sym = NODE_PSYMBOL("onFragment");
on_header_field_sym = NODE_PSYMBOL("onHeaderField"); on_header_field_sym = NODE_PSYMBOL("onHeaderField");
on_header_value_sym = NODE_PSYMBOL("onHeaderValue"); on_header_value_sym = NODE_PSYMBOL("onHeaderValue");
on_headers_complete_sym = NODE_PSYMBOL("onHeadersComplete"); on_headers_complete_sym = NODE_PSYMBOL("onHeadersComplete");
@ -431,10 +422,7 @@ void InitHttpParser(Handle<Object> target) {
upgrade_sym = NODE_PSYMBOL("upgrade"); upgrade_sym = NODE_PSYMBOL("upgrade");
settings.on_message_begin = Parser::on_message_begin; settings.on_message_begin = Parser::on_message_begin;
settings.on_path = Parser::on_path;
settings.on_query_string = Parser::on_query_string;
settings.on_url = Parser::on_url; settings.on_url = Parser::on_url;
settings.on_fragment = Parser::on_fragment;
settings.on_header_field = Parser::on_header_field; settings.on_header_field = Parser::on_header_field;
settings.on_header_value = Parser::on_header_value; settings.on_header_value = Parser::on_header_value;
settings.on_headers_complete = Parser::on_headers_complete; settings.on_headers_complete = Parser::on_headers_complete;

Loading…
Cancel
Save