Browse Source

Upgrade http parser, change node as needed.

The latest version of http-parser is a bit more stringent EOF semantics.
v0.7.4-release
Ryan Dahl 15 years ago
parent
commit
b893859c34
  1. 8
      deps/http_parser/LICENSE
  2. 27
      deps/http_parser/README.md
  3. 5459
      deps/http_parser/http_parser.c
  4. 47
      deps/http_parser/http_parser.h
  5. 182
      deps/http_parser/http_parser.rl
  6. 93
      deps/http_parser/test.c
  7. 7
      lib/http.js
  8. 42
      src/http.cc
  9. 1
      src/http.h
  10. 22
      test/mjsunit/test-http-malformed-request.js
  11. 16
      test/mjsunit/test-http-proxy.js
  12. 14
      test/mjsunit/test-http-server.js

8
deps/http_parser/LICENSE

@ -1,4 +1,5 @@
Copyright 2009, Ryan Lienhart Dahl. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
@ -17,12 +18,9 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
http_parser is based on Zed Shaw's Mongrel. Mongrel's license is as follows.
-- CUT ---- CUT ---- CUT ---- CUT ---- CUT ---- CUT ---- CUT ---- CUT --
---- 8< ---- 8< ---- 8< ---- 8< ---- 8< ---- 8< ---- 8< ---- 8< ----
Mongrel Web Server (Mongrel) is copyrighted free software by Zed A. Shaw
<zedshaw at zedshaw dot com> and contributors. You can redistribute it
and/or modify it under either the terms of the GPL2 or the conditions below:
@ -76,4 +74,4 @@ and/or modify it under either the terms of the GPL2 or the conditions below:
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.
-- CUT ---- CUT ---- CUT ---- CUT ---- CUT ---- CUT ---- CUT ---- CUT --
---- 8< ---- 8< ---- 8< ---- 8< ---- 8< ---- 8< ---- 8< ---- 8< ----

27
deps/http_parser/README.md

@ -43,15 +43,29 @@ When data is received on the socket execute the parser and check for errors.
char buf[len];
ssize_t recved;
recved = read(fd, buf, len);
if (recved != 0) // handle error
recved = recv(fd, buf, len, 0);
if (recved < 0) {
/* Handle error. */
}
/* Start up / continue the parser.
* Note we pass the recved==0 to http_parser_execute to signal
* that EOF has been recieved.
*/
http_parser_execute(parser, buf, recved);
if (http_parser_has_error(parser)) {
// handle error. usually just close the connection
/* Handle error. Usually just close the connection. */
}
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
consume input (for the body) until EOF. To tell http_parser about EOF, give
`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
to receive them.
Scalar valued message information such as `status_code`, `method`, and the
HTTP version are stored in the parser structure. This data is only
temporarlly stored in `http_parser` and gets reset on each new message. If
@ -129,3 +143,10 @@ Releases
* [0.1](http://s3.amazonaws.com/four.livejournal/20090427/http_parser-0.1.tar.gz)
The source repo is at [github](http://github.com/ry/http-parser).
Bindings
--------
* [Ruby](http://github.com/yakischloba/http-parser-ffi)
* [Lua](http://github.com/phoenixsol/lua-http-parser)

5459
deps/http_parser/http_parser.c

File diff suppressed because it is too large

47
deps/http_parser/http_parser.h

@ -28,6 +28,9 @@
extern "C" {
#endif
#ifdef _MSC_VER
# include <stddef.h>
#endif
#include <sys/types.h>
typedef struct http_parser http_parser;
@ -58,36 +61,20 @@ typedef int (*http_cb) (http_parser*);
#define HTTP_TRACE 0x1000
#define HTTP_UNLOCK 0x2000
/* Transfer Encodings */
#define HTTP_IDENTITY 0x01
#define HTTP_CHUNKED 0x02
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE };
#define HTTP_VERSION_OTHER 0x00
#define HTTP_VERSION_11 0x01
#define HTTP_VERSION_10 0x02
#define HTTP_VERSION_09 0x04
struct http_parser {
/** PRIVATE **/
int cs;
enum http_parser_type type;
size_t chunk_size;
/**
XXX
do this so no other code has to change, but make the field only 1 byte wide
instead of 2 (on x86/x86_64).
doing this not only shrinks the sizeof this struct by a byte but it ALSO
makes wrapping this in FFI way easier.
*/
union {
struct {
unsigned eating:1;
unsigned error:1;
};
struct {
unsigned char _flags;
};
};
char flags;
size_t body_read;
@ -107,11 +94,9 @@ struct http_parser {
/** READ-ONLY **/
unsigned short status_code; /* responses only */
unsigned short method; /* requests only */
short transfer_encoding;
unsigned short version_major;
unsigned short version_minor;
short version;
short keep_alive;
size_t content_length;
ssize_t content_length;
/** PUBLIC **/
void *data; /* A pointer to get hook to the "connection" or "socket" object */
@ -138,11 +123,17 @@ struct http_parser {
*/
void http_parser_init (http_parser *parser, enum http_parser_type);
size_t http_parser_execute (http_parser *parser, const char *data, size_t len);
void http_parser_execute (http_parser *parser, const char *data, size_t len);
int http_parser_has_error (http_parser *parser);
int http_parser_should_keep_alive (http_parser *parser);
static inline int
http_parser_should_keep_alive (http_parser *parser)
{
if (parser->keep_alive == -1) return (parser->version == HTTP_VERSION_11);
return parser->keep_alive;
}
#ifdef __cplusplus
}

182
deps/http_parser/http_parser.rl

@ -26,6 +26,12 @@
#include <limits.h>
#include <assert.h>
/* parser->flags */
#define EATING 0x01
#define ERROR 0x02
#define CHUNKED 0x04
#define EAT_FOREVER 0x10
static int unhex[] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
@ -35,12 +41,14 @@ static int unhex[] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
};
#define TRUE 1
#define FALSE 0
#define MIN(a,b) (a < b ? a : b)
#define NULL (void*)(0)
#define MAX_FIELD_SIZE 80*1024
#undef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#undef NULL
#define NULL ((void*)(0))
#define MAX_FIELD_SIZE (80*1024)
#define REMAINING (unsigned long)(pe - p)
#define CALLBACK(FOR) \
@ -48,20 +56,24 @@ do { \
if (parser->FOR##_mark) { \
parser->FOR##_size += p - parser->FOR##_mark; \
if (parser->FOR##_size > MAX_FIELD_SIZE) { \
parser->error = TRUE; \
return 0; \
parser->flags |= ERROR; \
return; \
} \
if (parser->on_##FOR) { \
callback_return_value = parser->on_##FOR(parser, \
parser->FOR##_mark, \
p - parser->FOR##_mark); \
} \
if (callback_return_value != 0) { \
parser->flags |= ERROR; \
return; \
} \
} \
} while(0)
#define RESET_PARSER(parser) \
parser->chunk_size = 0; \
parser->eating = 0; \
parser->flags = 0; \
parser->header_field_mark = NULL; \
parser->header_value_mark = NULL; \
parser->query_string_mark = NULL; \
@ -70,12 +82,10 @@ do { \
parser->fragment_mark = NULL; \
parser->status_code = 0; \
parser->method = 0; \
parser->transfer_encoding = HTTP_IDENTITY; \
parser->version_major = 0; \
parser->version_minor = 0; \
parser->version = HTTP_VERSION_OTHER; \
parser->keep_alive = -1; \
parser->content_length = 0; \
parser->body_read = 0;
parser->content_length = -1; \
parser->body_read = 0
#define END_REQUEST \
do { \
@ -97,12 +107,12 @@ do { \
parser->body_read += tmp; \
parser->chunk_size -= tmp; \
if (0 == parser->chunk_size) { \
parser->eating = FALSE; \
if (parser->transfer_encoding == HTTP_IDENTITY) { \
parser->flags &= ~EATING; \
if (!(parser->flags & CHUNKED)) { \
END_REQUEST; \
} \
} else { \
parser->eating = TRUE; \
parser->flags |= EATING; \
} \
} \
} while (0)
@ -142,60 +152,36 @@ do { \
action header_field {
CALLBACK(header_field);
if (callback_return_value != 0) {
parser->error = TRUE;
return 0;
}
parser->header_field_mark = NULL;
parser->header_field_size = 0;
}
action header_value {
CALLBACK(header_value);
if (callback_return_value != 0) {
parser->error = TRUE;
return 0;
}
parser->header_value_mark = NULL;
parser->header_value_size = 0;
}
action request_uri {
CALLBACK(uri);
if (callback_return_value != 0) {
parser->error = TRUE;
return 0;
}
parser->uri_mark = NULL;
parser->uri_size = 0;
}
action fragment {
CALLBACK(fragment);
if (callback_return_value != 0) {
parser->error = TRUE;
return 0;
}
parser->fragment_mark = NULL;
parser->fragment_size = 0;
}
action query_string {
CALLBACK(query_string);
if (callback_return_value != 0) {
parser->error = TRUE;
return 0;
}
parser->query_string_mark = NULL;
parser->query_string_size = 0;
}
action request_path {
CALLBACK(path);
if (callback_return_value != 0) {
parser->error = TRUE;
return 0;
}
parser->path_mark = NULL;
parser->path_size = 0;
}
@ -204,8 +190,8 @@ do { \
if(parser->on_headers_complete) {
callback_return_value = parser->on_headers_complete(parser);
if (callback_return_value != 0) {
parser->error = TRUE;
return 0;
parser->flags |= ERROR;
return;
}
}
}
@ -214,16 +200,17 @@ do { \
if(parser->on_message_begin) {
callback_return_value = parser->on_message_begin(parser);
if (callback_return_value != 0) {
parser->error = TRUE;
return 0;
parser->flags |= ERROR;
return;
}
}
}
action content_length {
if (parser->content_length == -1) parser->content_length = 0;
if (parser->content_length > INT_MAX) {
parser->error = TRUE;
return 0;
parser->flags |= ERROR;
return;
}
parser->content_length *= 10;
parser->content_length += *p - '0';
@ -234,21 +221,14 @@ do { \
parser->status_code += *p - '0';
}
action use_identity_encoding { parser->transfer_encoding = HTTP_IDENTITY; }
action use_chunked_encoding { parser->transfer_encoding = HTTP_CHUNKED; }
action set_keep_alive { parser->keep_alive = TRUE; }
action set_not_keep_alive { parser->keep_alive = FALSE; }
action use_chunked_encoding { parser->flags |= CHUNKED; }
action version_major {
parser->version_major *= 10;
parser->version_major += *p - '0';
}
action set_keep_alive { parser->keep_alive = 1; }
action set_not_keep_alive { parser->keep_alive = 0; }
action version_minor {
parser->version_minor *= 10;
parser->version_minor += *p - '0';
}
action version_11 { parser->version = HTTP_VERSION_11; }
action version_10 { parser->version = HTTP_VERSION_10; }
action version_09 { parser->version = HTTP_VERSION_09; }
action add_to_chunk_size {
parser->chunk_size *= 16;
@ -258,8 +238,8 @@ do { \
action skip_chunk_data {
SKIP_BODY(MIN(parser->chunk_size, REMAINING));
if (callback_return_value != 0) {
parser->error = TRUE;
return 0;
parser->flags |= ERROR;
return;
}
fhold;
@ -280,18 +260,32 @@ do { \
}
action body_logic {
if (parser->transfer_encoding == HTTP_CHUNKED) {
if (parser->flags & CHUNKED) {
fnext ChunkedBody;
} else {
/* this is pretty stupid. i'd prefer to combine this with skip_chunk_data */
/* this is pretty stupid. i'd prefer to combine this with
* skip_chunk_data */
if (parser->content_length < 0) {
/* If we didn't get a content length; if not keep-alive
* just read body until EOF */
if (!http_parser_should_keep_alive(parser)) {
parser->flags |= EAT_FOREVER;
parser->chunk_size = REMAINING;
} else {
/* Otherwise, if keep-alive, then assume the message
* has no body. */
parser->chunk_size = parser->content_length = 0;
}
} else {
parser->chunk_size = parser->content_length;
}
p += 1;
SKIP_BODY(MIN(REMAINING, parser->content_length));
SKIP_BODY(MIN(REMAINING, parser->chunk_size));
if (callback_return_value != 0) {
parser->error = TRUE;
return 0;
parser->flags |= ERROR;
return;
}
fhold;
@ -342,7 +336,11 @@ do { \
| "UNLOCK" %{ parser->method = HTTP_UNLOCK; }
); # Not allowing extension methods
HTTP_Version = "HTTP/" digit $version_major "." digit $version_minor;
HTTP_Version = "HTTP/" ( "1.1" %version_11
| "1.0" %version_10
| "0.9" %version_09
| (digit "." digit)
);
scheme = ( alpha | digit | "+" | "-" | "." )* ;
absolute_uri = (scheme ":" (uchar | reserved )*);
@ -369,7 +367,7 @@ do { \
| "close"i %set_not_keep_alive
)
)
| ("Transfer-Encoding"i %use_chunked_encoding hsep "identity" %use_identity_encoding)
| ("Transfer-Encoding"i hsep "chunked"i %use_chunked_encoding)
| (Field_Name hsep Field_Value)
) :> CRLF;
@ -387,11 +385,11 @@ do { \
chunk_ext_val = token*;
chunk_ext_name = token*;
chunk_extension = ( ";" " "* chunk_ext_name ("=" chunk_ext_val)? )*;
last_chunk = "0"+ chunk_extension CRLF;
last_chunk = "0"+ ( chunk_extension | " "+) CRLF;
chunk_size = (xdigit* [1-9a-fA-F] xdigit* ) $add_to_chunk_size;
chunk_end = CRLF;
chunk_body = any >skip_chunk_data;
chunk_begin = chunk_size chunk_extension CRLF;
chunk_begin = chunk_size ( chunk_extension | " "+ ) CRLF;
chunk = chunk_begin chunk_body chunk_end;
ChunkedBody := chunk* last_chunk trailing_headers CRLF @end_chunked_body;
@ -421,7 +419,6 @@ http_parser_init (http_parser *parser, enum http_parser_type type)
%% write init;
parser->cs = cs;
parser->type = type;
parser->error = 0;
parser->on_message_begin = NULL;
parser->on_path = NULL;
@ -438,23 +435,39 @@ http_parser_init (http_parser *parser, enum http_parser_type type)
}
/** exec **/
size_t
void
http_parser_execute (http_parser *parser, const char *buffer, size_t len)
{
size_t tmp; // REMOVE ME this is extremely hacky
int callback_return_value = 0;
const char *p, *pe;
const char *p, *pe, *eof;
int cs = parser->cs;
p = buffer;
pe = buffer+len;
eof = len ? NULL : pe;
if (0 < parser->chunk_size && parser->eating) {
if (parser->flags & EAT_FOREVER) {
if (len == 0) {
if (parser->on_message_complete) {
callback_return_value = parser->on_message_complete(parser);
if (callback_return_value != 0) parser->flags |= ERROR;
}
} else {
if (parser->on_body) {
callback_return_value = parser->on_body(parser, p, len);
if (callback_return_value != 0) parser->flags |= ERROR;
}
}
return;
}
if (0 < parser->chunk_size && (parser->flags & EATING)) {
/* eat body */
SKIP_BODY(MIN(len, parser->chunk_size));
if (callback_return_value != 0) {
parser->error = TRUE;
return 0;
parser->flags |= ERROR;
return;
}
}
@ -477,26 +490,11 @@ http_parser_execute (http_parser *parser, const char *buffer, size_t len)
CALLBACK(uri);
assert(p <= pe && "buffer overflow after parsing execute");
return(p - buffer);
}
int
http_parser_has_error (http_parser *parser)
{
if (parser->error) return TRUE;
if (parser->flags & ERROR) return 1;
return parser->cs == http_parser_error;
}
int
http_parser_should_keep_alive (http_parser *parser)
{
if (parser->keep_alive == -1)
if (parser->version_major == 1)
return (parser->version_minor != 0);
else if (parser->version_major == 0)
return FALSE;
else
return TRUE;
else
return parser->keep_alive;
}

93
deps/http_parser/test.c

@ -359,6 +359,45 @@ const struct message responses[] =
"</BODY></HTML>\r\n"
}
, {.name= "no content-length response"
,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 200 OK\r\n"
"Date: Tue, 04 Aug 2009 07:59:32 GMT\r\n"
"Server: Apache\r\n"
"X-Powered-By: Servlet/2.5 JSP/2.1\r\n"
"Content-Type: text/xml; charset=utf-8\r\n"
"Connection: close\r\n"
"\r\n"
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
" <SOAP-ENV:Body>\n"
" <SOAP-ENV:Fault>\n"
" <faultcode>SOAP-ENV:Client</faultcode>\n"
" <faultstring>Client Error</faultstring>\n"
" </SOAP-ENV:Fault>\n"
" </SOAP-ENV:Body>\n"
"</SOAP-ENV:Envelope>"
,.should_keep_alive= FALSE
,.status_code= 200
,.num_headers= 5
,.headers=
{ { "Date", "Tue, 04 Aug 2009 07:59:32 GMT" }
, { "Server", "Apache" }
, { "X-Powered-By", "Servlet/2.5 JSP/2.1" }
, { "Content-Type", "text/xml; charset=utf-8" }
, { "Connection", "close" }
}
,.body= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
" <SOAP-ENV:Body>\n"
" <SOAP-ENV:Fault>\n"
" <faultcode>SOAP-ENV:Client</faultcode>\n"
" <faultstring>Client Error</faultstring>\n"
" </SOAP-ENV:Fault>\n"
" </SOAP-ENV:Body>\n"
"</SOAP-ENV:Envelope>"
}
, {.name= "404 no headers no body"
,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 404 Not Found\r\n\r\n"
@ -379,6 +418,33 @@ const struct message responses[] =
,.body= ""
}
, {.name="200 trailing space on chunked body"
,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"25 \r\n"
"This is the data in the first chunk\r\n"
"\r\n"
"1C\r\n"
"and this is the second one\r\n"
"\r\n"
"0 \r\n"
"\r\n"
,.should_keep_alive= TRUE
,.status_code= 200
,.num_headers= 2
,.headers=
{ {"Content-Type", "text/plain" }
, {"Transfer-Encoding", "chunked" }
}
,.body =
"This is the data in the first chunk\r\n"
"and this is the second one\r\n"
}
, {.name= NULL } /* sentinel */
};
@ -543,12 +609,14 @@ parse_messages (int message_count, const struct message *input_messages[])
}
// Parse the stream
size_t traversed = 0;
parser_init(HTTP_REQUEST);
traversed = http_parser_execute(&parser, total, length);
http_parser_execute(&parser, total, length);
assert(!http_parser_has_error(&parser));
http_parser_execute(&parser, NULL, 0);
assert(!http_parser_has_error(&parser));
assert(num_messages == message_count);
for (i = 0; i < message_count; i++) {
@ -560,11 +628,14 @@ parse_messages (int message_count, const struct message *input_messages[])
void
test_message (const struct message *message)
{
size_t traversed = 0;
parser_init(message->type);
traversed = http_parser_execute(&parser, message->raw, strlen(message->raw));
http_parser_execute(&parser, message->raw, strlen(message->raw));
assert(!http_parser_has_error(&parser));
http_parser_execute(&parser, NULL, 0);
assert(!http_parser_has_error(&parser));
assert(num_messages == 1);
message_eq(0, message);
@ -573,10 +644,10 @@ test_message (const struct message *message)
void
test_error (const char *buf)
{
size_t traversed = 0;
parser_init(HTTP_REQUEST);
traversed = http_parser_execute(&parser, buf, strlen(buf));
http_parser_execute(&parser, buf, strlen(buf));
http_parser_execute(&parser, NULL, 0);
assert(http_parser_has_error(&parser));
}
@ -595,12 +666,14 @@ test_multiple3 (const struct message *r1, const struct message *r2, const struct
strcat(total, r2->raw);
strcat(total, r3->raw);
size_t traversed = 0;
parser_init(HTTP_REQUEST);
traversed = http_parser_execute(&parser, total, strlen(total));
http_parser_execute(&parser, total, strlen(total));
assert(!http_parser_has_error(&parser) );
http_parser_execute(&parser, NULL, 0);
assert(!http_parser_has_error(&parser) );
assert(num_messages == 3);
message_eq(0, r1);
message_eq(1, r2);
@ -659,15 +732,15 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
*/
http_parser_execute(&parser, buf1, buf1_len);
assert(!http_parser_has_error(&parser));
http_parser_execute(&parser, buf2, buf2_len);
assert(!http_parser_has_error(&parser));
http_parser_execute(&parser, buf3, buf3_len);
assert(!http_parser_has_error(&parser));
http_parser_execute(&parser, NULL, 0);
assert(!http_parser_has_error(&parser));
assert(3 == num_messages);

7
lib/http.js

@ -333,7 +333,7 @@ function ClientRequest (method, uri, headers) {
}
this.closeOnFinish = true;
this.sendHeaderLines(method + " " + uri + " HTTP/1.1\r\n", headers);
this.sendHeaderLines(method + " " + uri + " HTTP/1.0\r\n", headers);
}
node.inherits(ClientRequest, OutgoingMessage);
@ -476,7 +476,6 @@ function connectionListener (connection) {
}
});
createIncomingMessageStream(connection, function (incoming, should_keep_alive) {
var req = incoming;
@ -507,7 +506,9 @@ exports.createClient = function (port, host) {
return;
}
//sys.debug("client flush readyState = " + client.readyState);
if (req == requests[0]) flushMessageQueue(client, [req]);
if (req == requests[0]) {
if(flushMessageQueue(client, [req])) client.close();
}
});
requests.push(req);
};

42
src/http.cc

@ -66,6 +66,21 @@ HTTPConnection::OnReceive (const void *buf, size_t len)
if (http_parser_has_error(&parser_)) ForceClose();
}
void
HTTPConnection::OnEOF ()
{
HandleScope scope;
assert(attached_);
http_parser_execute(&parser_, NULL, 0);
if (http_parser_has_error(&parser_)) {
ForceClose();
} else {
Emit("eof", 0, NULL);
}
}
int
HTTPConnection::on_message_begin (http_parser *parser)
{
@ -211,14 +226,25 @@ HTTPConnection::on_headers_complete (http_parser *parser)
Integer::New(connection->parser_.status_code));
// VERSION
char version[10];
snprintf( version
, 10
, "%d.%d"
, connection->parser_.version_major
, connection->parser_.version_minor
);
message_info->Set(HTTP_VERSION_SYMBOL, String::New(version));
Local<String> version;
switch (connection->parser_.version) {
case HTTP_VERSION_OTHER:
version = String::NewSymbol("Other");
break;
case HTTP_VERSION_09:
version = String::NewSymbol("0.9");
break;
case HTTP_VERSION_10:
version = String::NewSymbol("1.0");
break;
case HTTP_VERSION_11:
version = String::NewSymbol("1.1");
break;
}
message_info->Set(HTTP_VERSION_SYMBOL, version);
message_info->Set(SHOULD_KEEP_ALIVE_SYMBOL,
http_parser_should_keep_alive(&connection->parser_) ? True() : False());

1
src/http.h

@ -36,6 +36,7 @@ protected:
}
void OnReceive (const void *buf, size_t len);
void OnEOF ();
static int on_message_begin (http_parser *parser);
static int on_uri (http_parser *parser, const char *at, size_t length);

22
test/mjsunit/test-http-malformed-request.js

@ -7,26 +7,30 @@ http = require("/http.js");
port = 9999;
nrequests_completed = 0;
nrequests_expected = 1;
nrequests_expected = 2;
var s = http.createServer(function (req, res) {
var server = http.createServer(function (req, res) {
puts("req: " + JSON.stringify(req.uri));
res.sendHeader(200, {"Content-Type": "text/plain"});
res.sendBody("Hello World");
res.finish();
if (++nrequests_completed == nrequests_expected) s.close();
if (++nrequests_completed == nrequests_expected) server.close();
puts("nrequests_completed: " + nrequests_completed);
});
s.listen(port);
server.listen(port);
var c = tcp.createConnection(port);
c.addListener("connect", function () {
c.send("GET /hello?foo=%99bar HTTP/1.1\r\n\r\n");
c.close();
tcp.createConnection(port).addListener("connect", function () {
this.send("GET /hello?foo=%99bar HTTP/1.1\r\nConnection: close\r\n\r\n");
this.close();
});
// TODO add more!
tcp.createConnection(port).addListener("connect", function () {
this.send("GET /with_\"stupid\"_quotes?in_the=\"uri\" HTTP/1.1\r\nConnection: close\r\n\r\n");
this.close();
});
process.addListener("exit", function () {
assertEquals(nrequests_expected, nrequests_completed);

16
test/mjsunit/test-http-proxy.js

@ -5,12 +5,12 @@ var PROXY_PORT = 8869;
var BACKEND_PORT = 8870;
var backend = http.createServer(function (req, res) {
// debug("backend");
debug("backend");
res.sendHeader(200, {"content-type": "text/plain"});
res.sendBody("hello world\n");
res.finish();
});
// debug("listen backend")
debug("listen backend")
backend.listen(BACKEND_PORT);
var proxy_client = http.createClient(BACKEND_PORT);
@ -24,30 +24,30 @@ var proxy = http.createServer(function (req, res) {
});
proxy_res.addListener("complete", function() {
res.finish();
// debug("proxy res");
debug("proxy res");
});
});
});
// debug("listen proxy")
debug("listen proxy")
proxy.listen(PROXY_PORT);
var body = "";
var client = http.createClient(PROXY_PORT);
var req = client.get("/test");
// debug("client req")
debug("client req")
req.finish(function (res) {
// debug("got res");
debug("got res: " + JSON.stringify(res.headers));
assertEquals(200, res.statusCode);
res.setBodyEncoding("utf8");
res.addListener("body", function (chunk) { body += chunk; });
res.addListener("complete", function () {
proxy.close();
backend.close();
// debug("closed both");
debug("closed both");
});
});
process.addListener("exit", function () {
assertEquals(body, "hello world\n");
assertEquals("hello world\n", body);
});

14
test/mjsunit/test-http-server.js

@ -13,6 +13,12 @@ http.createServer(function (req, res) {
res.id = request_number;
req.id = request_number++;
puts("server got request " + req.id);
req.addListener("complete", function () {
puts("request complete " + req.id);
});
if (req.id == 0) {
assertEquals("GET", req.method);
assertEquals("/hello", req.uri.path);
@ -24,10 +30,11 @@ http.createServer(function (req, res) {
assertEquals("POST", req.method);
assertEquals("/quit", req.uri.path);
this.close();
//puts("server closed");
puts("server closed");
}
setTimeout(function () {
puts("send response " + req.id);
res.sendHeader(200, {"Content-Type": "text/plain"});
res.sendBody(req.uri.path);
res.finish();
@ -40,6 +47,7 @@ var c = tcp.createConnection(port);
c.setEncoding("utf8");
c.addListener("connect", function () {
puts("client connected. sending first request");
c.send("GET /hello?hello=world&foo=b==ar HTTP/1.1\r\n\r\n" );
requests_sent += 1;
});
@ -48,7 +56,9 @@ c.addListener("receive", function (chunk) {
server_response += chunk;
if (requests_sent == 1) {
puts("send request 2");
c.send("POST /quit HTTP/1.1\r\n\r\n");
puts("close client");
c.close();
assertEquals(c.readyState, "readOnly");
requests_sent += 1;
@ -56,10 +66,12 @@ c.addListener("receive", function (chunk) {
});
c.addListener("eof", function () {
puts("client got eof");
client_got_eof = true;
});
c.addListener("close", function () {
puts("client closed");
assertEquals(c.readyState, "closed");
});

Loading…
Cancel
Save