Browse Source

Upgrade http-parser

v0.7.4-release
Ryan Dahl 15 years ago
parent
commit
c9e21435c0
  1. 2
      deps/http_parser/LICENSE-MIT
  2. 10
      deps/http_parser/README.md
  3. 50
      deps/http_parser/http_parser.c
  4. 10
      deps/http_parser/http_parser.h
  5. 158
      deps/http_parser/test.c
  6. 15
      src/node_http.cc
  7. 10
      src/node_http.h

2
deps/http_parser/LICENSE-MIT

@ -1,4 +1,4 @@
Copyright 2009 Ryan Dahl <ry@tinyclouds.org> Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to of this software and associated documentation files (the "Software"), to

10
deps/http_parser/README.md

@ -29,10 +29,10 @@ Usage
One `http_parser` object is used per TCP connection. Initialize the struct One `http_parser` object is used per TCP connection. Initialize the struct
using `http_parser_init()` and set the callbacks. That might look something using `http_parser_init()` and set the callbacks. That might look something
like this: like this for a request parser:
http_parser *parser = malloc(sizeof(http_parser)); http_parser *parser = malloc(sizeof(http_parser));
http_parser_init(parser); http_parser_init(parser, HTTP_REQUEST);
parser->on_path = my_path_callback; parser->on_path = my_path_callback;
parser->on_header_field = my_header_field_callback; parser->on_header_field = my_header_field_callback;
/* ... */ /* ... */
@ -54,7 +54,7 @@ When data is received on the socket execute the parser and check for errors.
* Note we pass the recved==0 to http_parse_requests to signal * Note we pass the recved==0 to http_parse_requests to signal
* that EOF has been recieved. * that EOF has been recieved.
*/ */
nparsed = http_parse_requests(parser, buf, recved); nparsed = http_parser_execute(parser, buf, recved);
if (nparsed != recved) { if (nparsed != recved) {
/* Handle error. Usually just close the connection. */ /* Handle error. Usually just close the connection. */
@ -63,7 +63,7 @@ When data is received on the socket execute the parser and check for errors.
HTTP needs to know where the end of the stream is. For example, sometimes HTTP needs to know where the end of the stream is. For example, sometimes
servers send responses without Content-Length and expect the client to servers send responses without Content-Length and expect the client to
consume input (for the body) until EOF. To tell http_parser about EOF, give consume input (for the body) until EOF. To tell http_parser about EOF, give
`0` as the third parameter to `http_parse_requests()`. Callbacks and errors `0` as the third parameter to `http_parser_execute()`. Callbacks and errors
can still be encountered during an EOF, so one must still be prepared can still be encountered during an EOF, so one must still be prepared
to receive them. to receive them.
@ -85,7 +85,7 @@ parser, for example, would not want such a feature.
Callbacks Callbacks
--------- ---------
During the `http_parse_requests()` call, the callbacks set in `http_parser` During the `http_parser_execute()` call, the callbacks set in `http_parser`
will be executed. The parser maintains state and never looks behind, so will be executed. The parser maintains state and never looks behind, so
buffering the data is not necessary. If you need to save certain data for buffering the data is not necessary. If you need to save certain data for
later usage, you can do that from the callbacks. later usage, you can do that from the callbacks.

50
deps/http_parser/http_parser.c

@ -1,4 +1,4 @@
/* Copyright 2009 Ryan Dahl <ry@tinyclouds.org> /* Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
* *
* Some parts of this source file were taken from NGINX * Some parts of this source file were taken from NGINX
* (src/http/ngx_http_parser.c) copyright (C) 2002-2009 Igor Sysoev. * (src/http/ngx_http_parser.c) copyright (C) 2002-2009 Igor Sysoev.
@ -97,6 +97,7 @@ static inline int message_complete_callback (http_parser *parser)
return parser->on_message_complete(parser); return parser->on_message_complete(parser);
} }
#define PROXY_CONNECTION "proxy-connection"
#define CONNECTION "connection" #define CONNECTION "connection"
#define CONTENT_LENGTH "content-length" #define CONTENT_LENGTH "content-length"
#define TRANSFER_ENCODING "transfer-encoding" #define TRANSFER_ENCODING "transfer-encoding"
@ -218,6 +219,7 @@ enum header_states
, h_CON , h_CON
, h_matching_connection , h_matching_connection
, h_matching_proxy_connection
, h_matching_content_length , h_matching_content_length
, h_matching_transfer_encoding , h_matching_transfer_encoding
@ -245,6 +247,8 @@ enum flags
#define LF '\n' #define LF '\n'
#define LOWER(c) (unsigned char)(c | 0x20) #define LOWER(c) (unsigned char)(c | 0x20)
#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
#if HTTP_PARSER_STRICT #if HTTP_PARSER_STRICT
# define STRICT_CHECK(cond) if (cond) goto error # define STRICT_CHECK(cond) if (cond) goto error
# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead) # define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
@ -253,8 +257,9 @@ enum flags
# define NEW_MESSAGE() start_state # define NEW_MESSAGE() start_state
#endif #endif
static inline size_t http_parser_execute (http_parser *parser,
size_t parse (http_parser *parser, const char *data, size_t len, int start_state) const char *data,
size_t len)
{ {
char c, ch; char c, ch;
const char *p, *pe; const char *p, *pe;
@ -950,6 +955,10 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
header_state = h_C; header_state = h_C;
break; break;
case 'p':
header_state = h_matching_proxy_connection;
break;
case 't': case 't':
header_state = h_matching_transfer_encoding; header_state = h_matching_transfer_encoding;
break; break;
@ -1007,6 +1016,18 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
} }
break; break;
/* proxy-connection */
case h_matching_proxy_connection:
index++;
if (index > sizeof(PROXY_CONNECTION)-1
|| c != PROXY_CONNECTION[index]) {
header_state = h_general;
} else if (index == sizeof(PROXY_CONNECTION)-2) {
header_state = h_connection;
}
break;
/* content-length */ /* content-length */
case h_matching_content_length: case h_matching_content_length:
@ -1256,7 +1277,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
/* Content-Length header given and non-zero */ /* Content-Length header given and non-zero */
state = s_body_identity; state = s_body_identity;
} else { } else {
if (start_state == s_start_req || http_should_keep_alive(parser)) { if (parser->type == HTTP_REQUEST || http_should_keep_alive(parser)) {
/* Assume content-length 0 - read the next */ /* Assume content-length 0 - read the next */
CALLBACK2(message_complete); CALLBACK2(message_complete);
state = NEW_MESSAGE(); state = NEW_MESSAGE();
@ -1408,22 +1429,6 @@ error:
} }
size_t
http_parse_requests (http_parser *parser, const char *data, size_t len)
{
if (!parser->state) parser->state = s_start_req;
return parse(parser, data, len, s_start_req);
}
size_t
http_parse_responses (http_parser *parser, const char *data, size_t len)
{
if (!parser->state) parser->state = s_start_res;
return parse(parser, data, len, s_start_res);
}
int int
http_should_keep_alive (http_parser *parser) http_should_keep_alive (http_parser *parser)
{ {
@ -1446,9 +1451,10 @@ http_should_keep_alive (http_parser *parser)
void void
http_parser_init (http_parser *parser) http_parser_init (http_parser *parser, enum http_parser_type t)
{ {
parser->state = 0; parser->type = t;
parser->state = (t == HTTP_REQUEST ? s_start_req : s_start_res);
parser->on_message_begin = NULL; parser->on_message_begin = NULL;
parser->on_path = NULL; parser->on_path = NULL;
parser->on_query_string = NULL; parser->on_query_string = NULL;

10
deps/http_parser/http_parser.h

@ -1,4 +1,4 @@
/* Copyright 2009 Ryan Dahl <ry@tinyclouds.org> /* Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to * of this software and associated documentation files (the "Software"), to
@ -74,8 +74,11 @@ enum http_method
, HTTP_UNLOCK = 0x4000 , HTTP_UNLOCK = 0x4000
}; };
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE };
struct http_parser { struct http_parser {
/** PRIVATE **/ /** PRIVATE **/
enum http_parser_type type;
unsigned short state; unsigned short state;
unsigned short header_state; unsigned short header_state;
size_t index; size_t index;
@ -125,9 +128,8 @@ struct http_parser {
http_cb on_message_complete; http_cb on_message_complete;
}; };
void http_parser_init(http_parser *parser); void http_parser_init(http_parser *parser, enum http_parser_type type);
size_t http_parse_requests(http_parser *parser, const char *data, size_t len); size_t http_parser_execute(http_parser *parser, const char *data, size_t len);
size_t http_parse_responses(http_parser *parser, const char *data, size_t len);
/* Call this in the on_headers_complete or on_message_complete callback to /* Call this in the on_headers_complete or on_message_complete callback to
* determine if this will be the last message on the connection. * determine if this will be the last message on the connection.
* If you are the server, respond with the "Connection: close" header * If you are the server, respond with the "Connection: close" header

158
deps/http_parser/test.c

@ -1,4 +1,4 @@
/* Copyright 2009 Ryan Dahl <ry@tinyclouds.org> /* Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to * of this software and associated documentation files (the "Software"), to
@ -34,14 +34,12 @@
#define MAX_HEADERS 10 #define MAX_HEADERS 10
#define MAX_ELEMENT_SIZE 500 #define MAX_ELEMENT_SIZE 500
enum message_type { REQUEST, RESPONSE };
static http_parser *parser; static http_parser *parser;
struct message { struct message {
const char *name; // for debugging purposes const char *name; // for debugging purposes
const char *raw; const char *raw;
enum message_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_path[MAX_ELEMENT_SIZE];
@ -60,17 +58,16 @@ struct message {
int message_begin_cb_called; int message_begin_cb_called;
int headers_complete_cb_called; int headers_complete_cb_called;
int message_complete_cb_called; int message_complete_cb_called;
int message_complete_on_eof; int eof_indicates_message_end;
}; };
static int currently_parsing_eof; static int currently_parsing_eof;
inline size_t parse (enum message_type t, const char *buf, size_t len) inline size_t parse (const char *buf, size_t len)
{ {
size_t nparsed; size_t nparsed;
currently_parsing_eof = (len == 0); currently_parsing_eof = (len == 0);
nparsed = (t == REQUEST ? http_parse_requests(parser, buf, len) nparsed = http_parser_execute(parser, buf, len);
: http_parse_responses(parser, buf, len));
return nparsed; return nparsed;
} }
@ -81,14 +78,14 @@ static int num_messages;
const struct message requests[] = const struct message requests[] =
#define CURL_GET 0 #define CURL_GET 0
{ {.name= "curl get" { {.name= "curl get"
,.type= REQUEST ,.type= HTTP_REQUEST
,.raw= "GET /test HTTP/1.1\r\n" ,.raw= "GET /test HTTP/1.1\r\n"
"User-Agent: curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1\r\n" "User-Agent: curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1\r\n"
"Host: 0.0.0.0=5000\r\n" "Host: 0.0.0.0=5000\r\n"
"Accept: */*\r\n" "Accept: */*\r\n"
"\r\n" "\r\n"
,.should_keep_alive= TRUE ,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE ,.eof_indicates_message_end= FALSE
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_GET ,.method= HTTP_GET
@ -107,7 +104,7 @@ const struct message requests[] =
#define FIREFOX_GET 1 #define FIREFOX_GET 1
, {.name= "firefox get" , {.name= "firefox get"
,.type= REQUEST ,.type= HTTP_REQUEST
,.raw= "GET /favicon.ico HTTP/1.1\r\n" ,.raw= "GET /favicon.ico HTTP/1.1\r\n"
"Host: 0.0.0.0=5000\r\n" "Host: 0.0.0.0=5000\r\n"
"User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0\r\n" "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0\r\n"
@ -119,7 +116,7 @@ const struct message requests[] =
"Connection: keep-alive\r\n" "Connection: keep-alive\r\n"
"\r\n" "\r\n"
,.should_keep_alive= TRUE ,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE ,.eof_indicates_message_end= FALSE
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_GET ,.method= HTTP_GET
@ -143,12 +140,12 @@ const struct message requests[] =
#define DUMBFUCK 2 #define DUMBFUCK 2
, {.name= "dumbfuck" , {.name= "dumbfuck"
,.type= REQUEST ,.type= HTTP_REQUEST
,.raw= "GET /dumbfuck HTTP/1.1\r\n" ,.raw= "GET /dumbfuck HTTP/1.1\r\n"
"aaaaaaaaaaaaa:++++++++++\r\n" "aaaaaaaaaaaaa:++++++++++\r\n"
"\r\n" "\r\n"
,.should_keep_alive= TRUE ,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE ,.eof_indicates_message_end= FALSE
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_GET ,.method= HTTP_GET
@ -165,11 +162,11 @@ const struct message requests[] =
#define FRAGMENT_IN_URI 3 #define FRAGMENT_IN_URI 3
, {.name= "fragment in url" , {.name= "fragment in url"
,.type= REQUEST ,.type= HTTP_REQUEST
,.raw= "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n" ,.raw= "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n"
"\r\n" "\r\n"
,.should_keep_alive= TRUE ,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE ,.eof_indicates_message_end= FALSE
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_GET ,.method= HTTP_GET
@ -184,11 +181,11 @@ const struct message requests[] =
#define GET_NO_HEADERS_NO_BODY 4 #define GET_NO_HEADERS_NO_BODY 4
, {.name= "get no headers no body" , {.name= "get no headers no body"
,.type= REQUEST ,.type= HTTP_REQUEST
,.raw= "GET /get_no_headers_no_body/world HTTP/1.1\r\n" ,.raw= "GET /get_no_headers_no_body/world HTTP/1.1\r\n"
"\r\n" "\r\n"
,.should_keep_alive= TRUE ,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE /* would need Connection: close */ ,.eof_indicates_message_end= FALSE /* would need Connection: close */
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_GET ,.method= HTTP_GET
@ -202,12 +199,12 @@ const struct message requests[] =
#define GET_ONE_HEADER_NO_BODY 5 #define GET_ONE_HEADER_NO_BODY 5
, {.name= "get one header no body" , {.name= "get one header no body"
,.type= REQUEST ,.type= HTTP_REQUEST
,.raw= "GET /get_one_header_no_body HTTP/1.1\r\n" ,.raw= "GET /get_one_header_no_body HTTP/1.1\r\n"
"Accept: */*\r\n" "Accept: */*\r\n"
"\r\n" "\r\n"
,.should_keep_alive= TRUE ,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE /* would need Connection: close */ ,.eof_indicates_message_end= FALSE /* would need Connection: close */
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_GET ,.method= HTTP_GET
@ -224,13 +221,13 @@ const struct message requests[] =
#define GET_FUNKY_CONTENT_LENGTH 6 #define GET_FUNKY_CONTENT_LENGTH 6
, {.name= "get funky content length body hello" , {.name= "get funky content length body hello"
,.type= REQUEST ,.type= HTTP_REQUEST
,.raw= "GET /get_funky_content_length_body_hello HTTP/1.0\r\n" ,.raw= "GET /get_funky_content_length_body_hello HTTP/1.0\r\n"
"conTENT-Length: 5\r\n" "conTENT-Length: 5\r\n"
"\r\n" "\r\n"
"HELLO" "HELLO"
,.should_keep_alive= FALSE ,.should_keep_alive= FALSE
,.message_complete_on_eof= FALSE ,.eof_indicates_message_end= FALSE
,.http_major= 1 ,.http_major= 1
,.http_minor= 0 ,.http_minor= 0
,.method= HTTP_GET ,.method= HTTP_GET
@ -247,7 +244,7 @@ const struct message requests[] =
#define POST_IDENTITY_BODY_WORLD 7 #define POST_IDENTITY_BODY_WORLD 7
, {.name= "post identity body world" , {.name= "post identity body world"
,.type= REQUEST ,.type= HTTP_REQUEST
,.raw= "POST /post_identity_body_world?q=search#hey HTTP/1.1\r\n" ,.raw= "POST /post_identity_body_world?q=search#hey HTTP/1.1\r\n"
"Accept: */*\r\n" "Accept: */*\r\n"
"Transfer-Encoding: identity\r\n" "Transfer-Encoding: identity\r\n"
@ -255,7 +252,7 @@ const struct message requests[] =
"\r\n" "\r\n"
"World" "World"
,.should_keep_alive= TRUE ,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE ,.eof_indicates_message_end= FALSE
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_POST ,.method= HTTP_POST
@ -274,7 +271,7 @@ const struct message requests[] =
#define POST_CHUNKED_ALL_YOUR_BASE 8 #define POST_CHUNKED_ALL_YOUR_BASE 8
, {.name= "post - chunked body: all your base are belong to us" , {.name= "post - chunked body: all your base are belong to us"
,.type= REQUEST ,.type= HTTP_REQUEST
,.raw= "POST /post_chunked_all_your_base HTTP/1.1\r\n" ,.raw= "POST /post_chunked_all_your_base HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n" "Transfer-Encoding: chunked\r\n"
"\r\n" "\r\n"
@ -282,7 +279,7 @@ const struct message requests[] =
"0\r\n" "0\r\n"
"\r\n" "\r\n"
,.should_keep_alive= TRUE ,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE ,.eof_indicates_message_end= FALSE
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_POST ,.method= HTTP_POST
@ -299,7 +296,7 @@ const struct message requests[] =
#define TWO_CHUNKS_MULT_ZERO_END 9 #define TWO_CHUNKS_MULT_ZERO_END 9
, {.name= "two chunks ; triple zero ending" , {.name= "two chunks ; triple zero ending"
,.type= REQUEST ,.type= HTTP_REQUEST
,.raw= "POST /two_chunks_mult_zero_end HTTP/1.1\r\n" ,.raw= "POST /two_chunks_mult_zero_end HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n" "Transfer-Encoding: chunked\r\n"
"\r\n" "\r\n"
@ -308,7 +305,7 @@ const struct message requests[] =
"000\r\n" "000\r\n"
"\r\n" "\r\n"
,.should_keep_alive= TRUE ,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE ,.eof_indicates_message_end= FALSE
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_POST ,.method= HTTP_POST
@ -325,7 +322,7 @@ const struct message requests[] =
#define CHUNKED_W_TRAILING_HEADERS 10 #define CHUNKED_W_TRAILING_HEADERS 10
, {.name= "chunked with trailing headers. blech." , {.name= "chunked with trailing headers. blech."
,.type= REQUEST ,.type= HTTP_REQUEST
,.raw= "POST /chunked_w_trailing_headers HTTP/1.1\r\n" ,.raw= "POST /chunked_w_trailing_headers HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n" "Transfer-Encoding: chunked\r\n"
"\r\n" "\r\n"
@ -336,7 +333,7 @@ const struct message requests[] =
"Content-Type: text/plain\r\n" "Content-Type: text/plain\r\n"
"\r\n" "\r\n"
,.should_keep_alive= TRUE ,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE ,.eof_indicates_message_end= FALSE
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_POST ,.method= HTTP_POST
@ -355,7 +352,7 @@ const struct message requests[] =
#define CHUNKED_W_BULLSHIT_AFTER_LENGTH 11 #define CHUNKED_W_BULLSHIT_AFTER_LENGTH 11
, {.name= "with bullshit after the length" , {.name= "with bullshit after the length"
,.type= REQUEST ,.type= HTTP_REQUEST
,.raw= "POST /chunked_w_bullshit_after_length HTTP/1.1\r\n" ,.raw= "POST /chunked_w_bullshit_after_length HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n" "Transfer-Encoding: chunked\r\n"
"\r\n" "\r\n"
@ -364,7 +361,7 @@ const struct message requests[] =
"0\r\n" "0\r\n"
"\r\n" "\r\n"
,.should_keep_alive= TRUE ,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE ,.eof_indicates_message_end= FALSE
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_POST ,.method= HTTP_POST
@ -381,10 +378,10 @@ const struct message requests[] =
#define WITH_QUOTES 12 #define WITH_QUOTES 12
, {.name= "with quotes" , {.name= "with quotes"
,.type= REQUEST ,.type= HTTP_REQUEST
,.raw= "GET /with_\"stupid\"_quotes?foo=\"bar\" HTTP/1.1\r\n\r\n" ,.raw= "GET /with_\"stupid\"_quotes?foo=\"bar\" HTTP/1.1\r\n\r\n"
,.should_keep_alive= TRUE ,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE ,.eof_indicates_message_end= FALSE
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.method= HTTP_GET ,.method= HTTP_GET
@ -400,17 +397,17 @@ const struct message requests[] =
#define APACHEBENCH_GET 13 #define APACHEBENCH_GET 13
/* The server receiving this request SHOULD NOT wait for EOF /* The server receiving this request SHOULD NOT wait for EOF
* to know that content-length == 0. * to know that content-length == 0.
* How to represent this in a unit test? message_complete_on_eof * How to represent this in a unit test? eof_indicates_message_end
* Compare with NO_CONTENT_LENGTH_RESPONSE. * Compare with NO_CONTENT_LENGTH_RESPONSE.
*/ */
, {.name = "apachebench get" , {.name = "apachebench get"
,.type= REQUEST ,.type= HTTP_REQUEST
,.raw= "GET /test HTTP/1.0\r\n" ,.raw= "GET /test HTTP/1.0\r\n"
"Host: 0.0.0.0:5000\r\n" "Host: 0.0.0.0:5000\r\n"
"User-Agent: ApacheBench/2.3\r\n" "User-Agent: ApacheBench/2.3\r\n"
"Accept: */*\r\n\r\n" "Accept: */*\r\n\r\n"
,.should_keep_alive= FALSE ,.should_keep_alive= FALSE
,.message_complete_on_eof= FALSE ,.eof_indicates_message_end= FALSE
,.http_major= 1 ,.http_major= 1
,.http_minor= 0 ,.http_minor= 0
,.method= HTTP_GET ,.method= HTTP_GET
@ -433,7 +430,7 @@ const struct message requests[] =
const struct message responses[] = const struct message responses[] =
#define GOOGLE_301 0 #define GOOGLE_301 0
{ {.name= "google 301" { {.name= "google 301"
,.type= RESPONSE ,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 301 Moved Permanently\r\n" ,.raw= "HTTP/1.1 301 Moved Permanently\r\n"
"Location: http://www.google.com/\r\n" "Location: http://www.google.com/\r\n"
"Content-Type: text/html; charset=UTF-8\r\n" "Content-Type: text/html; charset=UTF-8\r\n"
@ -450,7 +447,7 @@ const struct message responses[] =
"<A HREF=\"http://www.google.com/\">here</A>.\r\n" "<A HREF=\"http://www.google.com/\">here</A>.\r\n"
"</BODY></HTML>\r\n" "</BODY></HTML>\r\n"
,.should_keep_alive= TRUE ,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE ,.eof_indicates_message_end= FALSE
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.status_code= 301 ,.status_code= 301
@ -479,7 +476,7 @@ const struct message responses[] =
* Compare with APACHEBENCH_GET * Compare with APACHEBENCH_GET
*/ */
, {.name= "no content-length response" , {.name= "no content-length response"
,.type= RESPONSE ,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 200 OK\r\n" ,.raw= "HTTP/1.1 200 OK\r\n"
"Date: Tue, 04 Aug 2009 07:59:32 GMT\r\n" "Date: Tue, 04 Aug 2009 07:59:32 GMT\r\n"
"Server: Apache\r\n" "Server: Apache\r\n"
@ -497,7 +494,7 @@ const struct message responses[] =
" </SOAP-ENV:Body>\n" " </SOAP-ENV:Body>\n"
"</SOAP-ENV:Envelope>" "</SOAP-ENV:Envelope>"
,.should_keep_alive= FALSE ,.should_keep_alive= FALSE
,.message_complete_on_eof= TRUE ,.eof_indicates_message_end= TRUE
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.status_code= 200 ,.status_code= 200
@ -522,10 +519,10 @@ const struct message responses[] =
#define NO_HEADERS_NO_BODY_404 2 #define NO_HEADERS_NO_BODY_404 2
, {.name= "404 no headers no body" , {.name= "404 no headers no body"
,.type= RESPONSE ,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 404 Not Found\r\n\r\n" ,.raw= "HTTP/1.1 404 Not Found\r\n\r\n"
,.should_keep_alive= TRUE ,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE ,.eof_indicates_message_end= FALSE
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.status_code= 404 ,.status_code= 404
@ -536,10 +533,10 @@ const struct message responses[] =
#define NO_REASON_PHRASE 3 #define NO_REASON_PHRASE 3
, {.name= "301 no response phrase" , {.name= "301 no response phrase"
,.type= RESPONSE ,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 301\r\n\r\n" ,.raw= "HTTP/1.1 301\r\n\r\n"
,.should_keep_alive = TRUE ,.should_keep_alive = TRUE
,.message_complete_on_eof= FALSE ,.eof_indicates_message_end= FALSE
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.status_code= 301 ,.status_code= 301
@ -550,7 +547,7 @@ const struct message responses[] =
#define TRAILING_SPACE_ON_CHUNKED_BODY 4 #define TRAILING_SPACE_ON_CHUNKED_BODY 4
, {.name="200 trailing space on chunked body" , {.name="200 trailing space on chunked body"
,.type= RESPONSE ,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 200 OK\r\n" ,.raw= "HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n" "Content-Type: text/plain\r\n"
"Transfer-Encoding: chunked\r\n" "Transfer-Encoding: chunked\r\n"
@ -564,7 +561,7 @@ const struct message responses[] =
"0 \r\n" "0 \r\n"
"\r\n" "\r\n"
,.should_keep_alive= TRUE ,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE ,.eof_indicates_message_end= FALSE
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.status_code= 200 ,.status_code= 200
@ -581,14 +578,14 @@ const struct message responses[] =
#define NO_CARRIAGE_RET 5 #define NO_CARRIAGE_RET 5
, {.name="no carriage ret" , {.name="no carriage ret"
,.type= RESPONSE ,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 200 OK\n" ,.raw= "HTTP/1.1 200 OK\n"
"Content-Type: text/html; charset=utf-8\n" "Content-Type: text/html; charset=utf-8\n"
"Connection: close\n" "Connection: close\n"
"\n" "\n"
"these headers are from http://news.ycombinator.com/" "these headers are from http://news.ycombinator.com/"
,.should_keep_alive= FALSE ,.should_keep_alive= FALSE
,.message_complete_on_eof= TRUE ,.eof_indicates_message_end= TRUE
,.http_major= 1 ,.http_major= 1
,.http_minor= 1 ,.http_minor= 1
,.status_code= 200 ,.status_code= 200
@ -600,6 +597,31 @@ const struct message responses[] =
,.body= "these headers are from http://news.ycombinator.com/" ,.body= "these headers are from http://news.ycombinator.com/"
} }
#define PROXY_CONNECTION 6
, {.name="proxy connection"
,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=UTF-8\r\n"
"Content-Length: 11\r\n"
"Proxy-Connection: close\r\n"
"Date: Thu, 31 Dec 2009 20:55:48 +0000\r\n"
"\r\n"
"hello world"
,.should_keep_alive= FALSE
,.eof_indicates_message_end= FALSE
,.http_major= 1
,.http_minor= 1
,.status_code= 200
,.num_headers= 4
,.headers=
{ {"Content-Type", "text/html; charset=UTF-8" }
, {"Content-Length", "11" }
, {"Proxy-Connection", "close" }
, {"Date", "Thu, 31 Dec 2009 20:55:48 +0000"}
}
,.body= "hello world"
}
, {.name= NULL } /* sentinel */ , {.name= NULL } /* sentinel */
}; };
@ -708,14 +730,14 @@ message_complete_cb (http_parser *p)
} }
messages[num_messages].message_complete_cb_called = TRUE; messages[num_messages].message_complete_cb_called = TRUE;
messages[num_messages].message_complete_on_eof = currently_parsing_eof; messages[num_messages].eof_indicates_message_end = currently_parsing_eof;
num_messages++; num_messages++;
return 0; return 0;
} }
void void
parser_init () parser_init (enum http_parser_type type)
{ {
num_messages = 0; num_messages = 0;
@ -723,7 +745,7 @@ parser_init ()
parser = malloc(sizeof(http_parser)); parser = malloc(sizeof(http_parser));
http_parser_init(parser); http_parser_init(parser, type);
memset(&messages, 0, sizeof messages); memset(&messages, 0, sizeof messages);
@ -791,14 +813,14 @@ message_eq (int index, const struct message *expected)
MESSAGE_CHECK_NUM_EQ(expected, m, http_major); MESSAGE_CHECK_NUM_EQ(expected, m, http_major);
MESSAGE_CHECK_NUM_EQ(expected, m, http_minor); MESSAGE_CHECK_NUM_EQ(expected, m, http_minor);
if (expected->type == REQUEST) { if (expected->type == HTTP_REQUEST) {
MESSAGE_CHECK_NUM_EQ(expected, m, method); MESSAGE_CHECK_NUM_EQ(expected, m, method);
} else { } else {
MESSAGE_CHECK_NUM_EQ(expected, m, status_code); MESSAGE_CHECK_NUM_EQ(expected, m, status_code);
} }
MESSAGE_CHECK_NUM_EQ(expected, m, should_keep_alive); MESSAGE_CHECK_NUM_EQ(expected, m, should_keep_alive);
MESSAGE_CHECK_NUM_EQ(expected, m, message_complete_on_eof); MESSAGE_CHECK_NUM_EQ(expected, m, eof_indicates_message_end);
assert(m->message_begin_cb_called); assert(m->message_begin_cb_called);
assert(m->headers_complete_cb_called); assert(m->headers_complete_cb_called);
@ -869,17 +891,17 @@ print_error (const char *raw, size_t error_location)
void void
test_message (const struct message *message) test_message (const struct message *message)
{ {
parser_init(); parser_init(message->type);
size_t read; size_t read;
read = parse(message->type, message->raw, strlen(message->raw)); read = parse(message->raw, strlen(message->raw));
if (read != strlen(message->raw)) { if (read != strlen(message->raw)) {
print_error(message->raw, read); print_error(message->raw, read);
exit(1); exit(1);
} }
read = parse(message->type, NULL, 0); read = parse(NULL, 0);
if (read != 0) { if (read != 0) {
print_error(message->raw, read); print_error(message->raw, read);
exit(1); exit(1);
@ -898,13 +920,13 @@ test_message (const struct message *message)
void void
test_error (const char *buf) test_error (const char *buf)
{ {
parser_init(); parser_init(HTTP_REQUEST);
size_t parsed; size_t parsed;
parsed = parse(REQUEST, buf, strlen(buf)); parsed = parse(buf, strlen(buf));
if (parsed != strlen(buf)) goto out; if (parsed != strlen(buf)) goto out;
parsed = parse(REQUEST, NULL, 0); parsed = parse(NULL, 0);
if (parsed != 0) goto out; if (parsed != 0) goto out;
fprintf(stderr, "\n*** Error expected but none found ***\n\n%s", buf); fprintf(stderr, "\n*** Error expected but none found ***\n\n%s", buf);
@ -929,17 +951,17 @@ test_multiple3 (const struct message *r1, const struct message *r2, const struct
strcat(total, r2->raw); strcat(total, r2->raw);
strcat(total, r3->raw); strcat(total, r3->raw);
parser_init(); parser_init(r1->type);
size_t read; size_t read;
read = parse(r1->type, total, strlen(total)); read = parse(total, strlen(total));
if (read != strlen(total)) { if (read != strlen(total)) {
print_error(total, read); print_error(total, read);
exit(1); exit(1);
} }
read = parse(REQUEST, NULL, 0); read = parse(NULL, 0);
if (read != 0) { if (read != 0) {
print_error(total, read); print_error(total, read);
exit(1); exit(1);
@ -992,7 +1014,7 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
} }
ops += 1; ops += 1;
parser_init(); parser_init(r1->type);
buf1_len = i; buf1_len = i;
strncpy(buf1, total, buf1_len); strncpy(buf1, total, buf1_len);
@ -1006,25 +1028,25 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
strncpy(buf3, total+j, buf3_len); strncpy(buf3, total+j, buf3_len);
buf3[buf3_len] = 0; buf3[buf3_len] = 0;
read = parse(r1->type, buf1, buf1_len); read = parse(buf1, buf1_len);
if (read != buf1_len) { if (read != buf1_len) {
print_error(buf1, read); print_error(buf1, read);
goto error; goto error;
} }
read = parse(r1->type, buf2, buf2_len); read = parse(buf2, buf2_len);
if (read != buf2_len) { if (read != buf2_len) {
print_error(buf2, read); print_error(buf2, read);
goto error; goto error;
} }
read = parse(r1->type, buf3, buf3_len); read = parse(buf3, buf3_len);
if (read != buf3_len) { if (read != buf3_len) {
print_error(buf3, read); print_error(buf3, read);
goto error; goto error;
} }
parse(r1->type, NULL, 0); parse(NULL, 0);
if (3 != num_messages) { if (3 != num_messages) {
fprintf(stderr, "\n\nParser didn't see 3 messages only %d\n", num_messages); fprintf(stderr, "\n\nParser didn't see 3 messages only %d\n", num_messages);

15
src/node_http.cc

@ -127,13 +127,9 @@ HTTPConnection::OnReceive (const void *buf, size_t len)
HandleScope scope; HandleScope scope;
assert(refs_); assert(refs_);
size_t nparsed; size_t nparsed;
if (type_ == HTTP_REQUEST) { nparsed = http_parser_execute(&parser_, static_cast<const char*>(buf), len);
nparsed = http_parse_requests(&parser_, static_cast<const char*>(buf), len);
} else {
nparsed = http_parse_responses(&parser_, static_cast<const char*>(buf), len);
}
if (nparsed != len) { if (nparsed != len) {
ForceClose(); ForceClose();
@ -145,11 +141,8 @@ HTTPConnection::OnEOF ()
{ {
HandleScope scope; HandleScope scope;
assert(refs_); assert(refs_);
if (type_ == HTTP_REQUEST) { size_t nparsed;
http_parse_requests(&parser_, NULL, 0); nparsed = http_parser_execute(&parser_, NULL, 0);
} else {
http_parse_responses(&parser_, NULL, 0);
}
Emit(eof_symbol, 0, NULL); Emit(eof_symbol, 0, NULL);
} }

10
src/node_http.h

@ -7,8 +7,6 @@
namespace node { namespace node {
enum http_connection_type { HTTP_RESPONSE, HTTP_REQUEST };
class HTTPConnection : public Connection { class HTTPConnection : public Connection {
public: public:
static void Initialize (v8::Handle<v8::Object> target); static void Initialize (v8::Handle<v8::Object> target);
@ -21,7 +19,7 @@ protected:
static v8::Handle<v8::Value> NewServer (const v8::Arguments& args); static v8::Handle<v8::Value> NewServer (const v8::Arguments& args);
static v8::Handle<v8::Value> ResetParser(const v8::Arguments& args); static v8::Handle<v8::Value> ResetParser(const v8::Arguments& args);
HTTPConnection (enum http_connection_type t) HTTPConnection (enum http_parser_type t)
: Connection() : Connection()
{ {
type_ = t; type_ = t;
@ -29,7 +27,7 @@ protected:
} }
void ResetParser() { void ResetParser() {
http_parser_init (&parser_); http_parser_init (&parser_, type_);
parser_.on_message_begin = on_message_begin; parser_.on_message_begin = on_message_begin;
parser_.on_url = on_url; parser_.on_url = on_url;
parser_.on_path = on_path; parser_.on_path = on_path;
@ -57,10 +55,8 @@ protected:
static int on_body (http_parser *parser, const char *buf, size_t len); static int on_body (http_parser *parser, const char *buf, size_t len);
static int on_message_complete (http_parser *parser); static int on_message_complete (http_parser *parser);
enum http_parser_type type_;
http_parser parser_; http_parser parser_;
enum http_connection_type type_; // should probably use subclass
// but going to refactor this all soon
// so won't worry about it.
friend class HTTPServer; friend class HTTPServer;
}; };

Loading…
Cancel
Save