mirror of
https://github.com/zebrajr/node.git
synced 2026-01-15 12:15:26 +00:00
Upgrade http parser, change node as needed.
The latest version of http-parser is a bit more stringent EOF semantics.
This commit is contained in:
16
deps/http_parser/LICENSE
vendored
16
deps/http_parser/LICENSE
vendored
@@ -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
|
||||
@@ -15,16 +16,13 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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.
|
||||
|
||||
|
||||
|
||||
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
|
||||
<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:
|
||||
|
||||
1. You may make and give away verbatim copies of the source form of the
|
||||
@@ -66,9 +64,9 @@ and/or modify it under either the terms of the GPL2 or the conditions below:
|
||||
software (possibly commercial). But some files in the distribution
|
||||
are not written by the author, so that they are not under this terms.
|
||||
|
||||
5. The scripts and library files supplied as input to or produced as
|
||||
5. The scripts and library files supplied as input to or produced as
|
||||
output from the software do not automatically fall under the
|
||||
copyright of the software, but belong to whomever generated them,
|
||||
copyright of the software, but belong to whomever generated them,
|
||||
and may be sold commercially, and may be aggregated with this
|
||||
software.
|
||||
|
||||
@@ -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< ----
|
||||
|
||||
4
deps/http_parser/Makefile
vendored
4
deps/http_parser/Makefile
vendored
@@ -1,8 +1,8 @@
|
||||
#OPT=-O0 -g -Wall -Wextra -Werror
|
||||
OPT=-O2
|
||||
|
||||
test: http_parser.o test.c
|
||||
gcc $(OPT) http_parser.o test.c -o $@
|
||||
test: http_parser.o test.c
|
||||
gcc $(OPT) http_parser.o test.c -o $@
|
||||
|
||||
http_parser.o: http_parser.c http_parser.h Makefile
|
||||
gcc $(OPT) -c http_parser.c
|
||||
|
||||
33
deps/http_parser/README.md
vendored
33
deps/http_parser/README.md
vendored
@@ -5,11 +5,11 @@ This is a parser for HTTP messages written in C. It parses both requests
|
||||
and responses. The parser is designed to be used in performance HTTP
|
||||
applications. It does not make any allocations, it does not buffer data, and
|
||||
it can be interrupted at anytime. It only requires about 128 bytes of data
|
||||
per message stream (in a web server that is per connection).
|
||||
per message stream (in a web server that is per connection).
|
||||
|
||||
Features:
|
||||
|
||||
* No dependencies
|
||||
* No dependencies
|
||||
* Parses both requests and responses.
|
||||
* Handles keep-alive streams.
|
||||
* Decodes chunked encoding.
|
||||
@@ -43,21 +43,35 @@ 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
|
||||
this information is needed later, copy it out of the structure during the
|
||||
`headers_complete` callback.
|
||||
|
||||
|
||||
The parser decodes the transfer-encoding for both requests and responses
|
||||
transparently. That is, a chunked encoding is decoded before being sent to
|
||||
the on_body callback.
|
||||
@@ -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)
|
||||
|
||||
6083
deps/http_parser/http_parser.c
vendored
6083
deps/http_parser/http_parser.c
vendored
File diff suppressed because it is too large
Load Diff
91
deps/http_parser/http_parser.h
vendored
91
deps/http_parser/http_parser.h
vendored
@@ -2,7 +2,7 @@
|
||||
* Based on Zed Shaw's Mongrel, copyright (c) Zed A. Shaw
|
||||
*
|
||||
* 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
|
||||
@@ -10,31 +10,34 @@
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 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.
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef http_parser_h
|
||||
#define http_parser_h
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifdef _MSC_VER
|
||||
# include <stddef.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
|
||||
typedef struct http_parser http_parser;
|
||||
|
||||
/* Callbacks should return non-zero to indicate an error. The parse will
|
||||
* then halt execution.
|
||||
*
|
||||
* then halt execution.
|
||||
*
|
||||
* http_data_cb does not return data chunks. It will be call arbitrarally
|
||||
* many times for each string. E.G. you might get 10 callbacks for "on_path"
|
||||
* each providing just a few characters more data.
|
||||
@@ -58,60 +61,42 @@ 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;
|
||||
|
||||
const char *header_field_mark;
|
||||
size_t header_field_size;
|
||||
const char *header_value_mark;
|
||||
size_t header_value_size;
|
||||
const char *query_string_mark;
|
||||
size_t query_string_size;
|
||||
const char *path_mark;
|
||||
size_t path_size;
|
||||
const char *uri_mark;
|
||||
size_t uri_size;
|
||||
const char *fragment_mark;
|
||||
size_t fragment_size;
|
||||
const char *header_field_mark;
|
||||
size_t header_field_size;
|
||||
const char *header_value_mark;
|
||||
size_t header_value_size;
|
||||
const char *query_string_mark;
|
||||
size_t query_string_size;
|
||||
const char *path_mark;
|
||||
size_t path_size;
|
||||
const char *uri_mark;
|
||||
size_t uri_size;
|
||||
const char *fragment_mark;
|
||||
size_t fragment_size;
|
||||
|
||||
/** 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 */
|
||||
@@ -134,17 +119,23 @@ struct http_parser {
|
||||
};
|
||||
|
||||
/* Initializes an http_parser structure. The second argument specifies if
|
||||
* it will be parsing requests or responses.
|
||||
* it will be parsing requests or responses.
|
||||
*/
|
||||
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
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
236
deps/http_parser/http_parser.rl
vendored
236
deps/http_parser/http_parser.rl
vendored
@@ -2,7 +2,7 @@
|
||||
* Based on Zed Shaw's Mongrel, copyright (c) Zed A. Shaw
|
||||
*
|
||||
* 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
|
||||
@@ -10,22 +10,28 @@
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 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.
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "http_parser.h"
|
||||
#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,34 +56,36 @@ 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->header_field_mark = NULL; \
|
||||
parser->header_value_mark = NULL; \
|
||||
parser->query_string_mark = NULL; \
|
||||
parser->path_mark = NULL; \
|
||||
parser->uri_mark = NULL; \
|
||||
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->keep_alive = -1; \
|
||||
parser->content_length = 0; \
|
||||
parser->body_read = 0;
|
||||
parser->chunk_size = 0; \
|
||||
parser->flags = 0; \
|
||||
parser->header_field_mark = NULL; \
|
||||
parser->header_value_mark = NULL; \
|
||||
parser->query_string_mark = NULL; \
|
||||
parser->path_mark = NULL; \
|
||||
parser->uri_mark = NULL; \
|
||||
parser->fragment_mark = NULL; \
|
||||
parser->status_code = 0; \
|
||||
parser->method = 0; \
|
||||
parser->version = HTTP_VERSION_OTHER; \
|
||||
parser->keep_alive = -1; \
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 use_chunked_encoding { parser->flags |= CHUNKED; }
|
||||
|
||||
action set_keep_alive { parser->keep_alive = TRUE; }
|
||||
action set_not_keep_alive { parser->keep_alive = FALSE; }
|
||||
action set_keep_alive { parser->keep_alive = 1; }
|
||||
action set_not_keep_alive { parser->keep_alive = 0; }
|
||||
|
||||
action version_major {
|
||||
parser->version_major *= 10;
|
||||
parser->version_major += *p - '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,15 +238,15 @@ 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;
|
||||
fhold;
|
||||
if (parser->chunk_size > REMAINING) {
|
||||
fbreak;
|
||||
} else {
|
||||
fgoto chunk_end;
|
||||
fgoto chunk_end;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
parser->chunk_size = parser->content_length;
|
||||
p += 1;
|
||||
/* 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;
|
||||
@@ -314,13 +308,13 @@ do { \
|
||||
escape = ("%" xdigit xdigit);
|
||||
uchar = (unreserved | escape | "\"");
|
||||
pchar = (uchar | ":" | "@" | "&" | "=" | "+");
|
||||
tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\""
|
||||
tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\""
|
||||
| "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t");
|
||||
|
||||
# elements
|
||||
token = (ascii -- (CTL | tspecials));
|
||||
quote = "\"";
|
||||
# qdtext = token -- "\"";
|
||||
# qdtext = token -- "\"";
|
||||
# quoted_pair = "\" ascii;
|
||||
# quoted_string = "\"" (qdtext | quoted_pair )* "\"";
|
||||
|
||||
@@ -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 )*);
|
||||
@@ -364,12 +362,12 @@ do { \
|
||||
hsep = ":" " "*;
|
||||
header = (field_name hsep field_value) :> CRLF;
|
||||
Header = ( ("Content-Length"i hsep digit+ $content_length)
|
||||
| ("Connection"i hsep
|
||||
| ("Connection"i hsep
|
||||
( "Keep-Alive"i %set_keep_alive
|
||||
| "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;
|
||||
chunk_size = (xdigit* [1-9a-fA-F] xdigit*) $add_to_chunk_size;
|
||||
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;
|
||||
|
||||
@@ -415,13 +413,12 @@ do { \
|
||||
%% write data;
|
||||
|
||||
void
|
||||
http_parser_init (http_parser *parser, enum http_parser_type type)
|
||||
http_parser_init (http_parser *parser, enum http_parser_type type)
|
||||
{
|
||||
int cs = 0;
|
||||
%% 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)
|
||||
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;
|
||||
}
|
||||
|
||||
167
deps/http_parser/test.c
vendored
167
deps/http_parser/test.c
vendored
@@ -38,7 +38,7 @@ struct message {
|
||||
static struct message messages[5];
|
||||
static int num_messages;
|
||||
|
||||
/* * R E Q U E S T S * */
|
||||
/* * R E Q U E S T S * */
|
||||
const struct message requests[] =
|
||||
#define CURL_GET 0
|
||||
{ {.name= "curl get"
|
||||
@@ -55,7 +55,7 @@ const struct message requests[] =
|
||||
,.request_path= "/test"
|
||||
,.request_uri= "/test"
|
||||
,.num_headers= 3
|
||||
,.headers=
|
||||
,.headers=
|
||||
{ { "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" }
|
||||
, { "Host", "0.0.0.0=5000" }
|
||||
, { "Accept", "*/*" }
|
||||
@@ -83,7 +83,7 @@ const struct message requests[] =
|
||||
,.request_path= "/favicon.ico"
|
||||
,.request_uri= "/favicon.ico"
|
||||
,.num_headers= 8
|
||||
,.headers=
|
||||
,.headers=
|
||||
{ { "Host", "0.0.0.0=5000" }
|
||||
, { "User-Agent", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0" }
|
||||
, { "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" }
|
||||
@@ -109,7 +109,7 @@ const struct message requests[] =
|
||||
,.request_path= "/dumbfuck"
|
||||
,.request_uri= "/dumbfuck"
|
||||
,.num_headers= 1
|
||||
,.headers=
|
||||
,.headers=
|
||||
{ { "aaaaaaaaaaaaa", "++++++++++" }
|
||||
}
|
||||
,.body= ""
|
||||
@@ -126,7 +126,7 @@ const struct message requests[] =
|
||||
,.fragment= "posts-17408"
|
||||
,.request_path= "/forums/1/topics/2375"
|
||||
/* XXX request uri does not include fragment? */
|
||||
,.request_uri= "/forums/1/topics/2375?page=1"
|
||||
,.request_uri= "/forums/1/topics/2375?page=1"
|
||||
,.num_headers= 0
|
||||
,.body= ""
|
||||
}
|
||||
@@ -159,7 +159,7 @@ const struct message requests[] =
|
||||
,.request_path= "/get_one_header_no_body"
|
||||
,.request_uri= "/get_one_header_no_body"
|
||||
,.num_headers= 1
|
||||
,.headers=
|
||||
,.headers=
|
||||
{ { "Accept" , "*/*" }
|
||||
}
|
||||
,.body= ""
|
||||
@@ -179,7 +179,7 @@ const struct message requests[] =
|
||||
,.request_path= "/get_funky_content_length_body_hello"
|
||||
,.request_uri= "/get_funky_content_length_body_hello"
|
||||
,.num_headers= 1
|
||||
,.headers=
|
||||
,.headers=
|
||||
{ { "conTENT-Length" , "5" }
|
||||
}
|
||||
,.body= "HELLO"
|
||||
@@ -201,10 +201,10 @@ const struct message requests[] =
|
||||
,.request_path= "/post_identity_body_world"
|
||||
,.request_uri= "/post_identity_body_world?q=search"
|
||||
,.num_headers= 3
|
||||
,.headers=
|
||||
,.headers=
|
||||
{ { "Accept", "*/*" }
|
||||
, { "Transfer-Encoding", "identity" }
|
||||
, { "Content-Length", "5" }
|
||||
, { "Content-Length", "5" }
|
||||
}
|
||||
,.body= "World"
|
||||
}
|
||||
@@ -225,7 +225,7 @@ const struct message requests[] =
|
||||
,.request_path= "/post_chunked_all_your_base"
|
||||
,.request_uri= "/post_chunked_all_your_base"
|
||||
,.num_headers= 1
|
||||
,.headers=
|
||||
,.headers=
|
||||
{ { "Transfer-Encoding" , "chunked" }
|
||||
}
|
||||
,.body= "all your base are belong to us"
|
||||
@@ -248,13 +248,13 @@ const struct message requests[] =
|
||||
,.request_path= "/two_chunks_mult_zero_end"
|
||||
,.request_uri= "/two_chunks_mult_zero_end"
|
||||
,.num_headers= 1
|
||||
,.headers=
|
||||
,.headers=
|
||||
{ { "Transfer-Encoding", "chunked" }
|
||||
}
|
||||
,.body= "hello world"
|
||||
}
|
||||
|
||||
#define CHUNKED_W_TRAILING_HEADERS 10
|
||||
#define CHUNKED_W_TRAILING_HEADERS 10
|
||||
, {.name= "chunked with trailing headers. blech."
|
||||
,.type= HTTP_REQUEST
|
||||
,.raw= "POST /chunked_w_trailing_headers HTTP/1.1\r\n"
|
||||
@@ -273,13 +273,13 @@ const struct message requests[] =
|
||||
,.request_path= "/chunked_w_trailing_headers"
|
||||
,.request_uri= "/chunked_w_trailing_headers"
|
||||
,.num_headers= 1
|
||||
,.headers=
|
||||
,.headers=
|
||||
{ { "Transfer-Encoding", "chunked" }
|
||||
}
|
||||
,.body= "hello world"
|
||||
}
|
||||
|
||||
#define CHUNKED_W_BULLSHIT_AFTER_LENGTH 11
|
||||
#define CHUNKED_W_BULLSHIT_AFTER_LENGTH 11
|
||||
, {.name= "with bullshit after the length"
|
||||
,.type= HTTP_REQUEST
|
||||
,.raw= "POST /chunked_w_bullshit_after_length HTTP/1.1\r\n"
|
||||
@@ -296,7 +296,7 @@ const struct message requests[] =
|
||||
,.request_path= "/chunked_w_bullshit_after_length"
|
||||
,.request_uri= "/chunked_w_bullshit_after_length"
|
||||
,.num_headers= 1
|
||||
,.headers=
|
||||
,.headers=
|
||||
{ { "Transfer-Encoding", "chunked" }
|
||||
}
|
||||
,.body= "hello world"
|
||||
@@ -320,8 +320,8 @@ const struct message requests[] =
|
||||
, {.name= NULL } /* sentinel */
|
||||
};
|
||||
|
||||
/* * R E S P O N S E S * */
|
||||
const struct message responses[] =
|
||||
/* * R E S P O N S E S * */
|
||||
const struct message responses[] =
|
||||
{ {.name= "google 301"
|
||||
,.type= HTTP_RESPONSE
|
||||
,.raw= "HTTP/1.1 301 Moved Permanently\r\n"
|
||||
@@ -342,7 +342,7 @@ const struct message responses[] =
|
||||
,.should_keep_alive= TRUE
|
||||
,.status_code= 301
|
||||
,.num_headers= 7
|
||||
,.headers=
|
||||
,.headers=
|
||||
{ { "Location", "http://www.google.com/" }
|
||||
, { "Content-Type", "text/html; charset=UTF-8" }
|
||||
, { "Date", "Sun, 26 Apr 2009 11:11:49 GMT" }
|
||||
@@ -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"
|
||||
@@ -377,7 +416,34 @@ const struct message responses[] =
|
||||
,.num_headers= 0
|
||||
,.headers= {}
|
||||
,.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));
|
||||
}
|
||||
@@ -584,30 +655,32 @@ test_error (const char *buf)
|
||||
void
|
||||
test_multiple3 (const struct message *r1, const struct message *r2, const struct message *r3)
|
||||
{
|
||||
char total[ strlen(r1->raw)
|
||||
+ strlen(r2->raw)
|
||||
+ strlen(r3->raw)
|
||||
char total[ strlen(r1->raw)
|
||||
+ strlen(r2->raw)
|
||||
+ strlen(r3->raw)
|
||||
+ 1
|
||||
];
|
||||
total[0] = '\0';
|
||||
|
||||
strcat(total, r1->raw);
|
||||
strcat(total, r2->raw);
|
||||
strcat(total, r3->raw);
|
||||
strcat(total, r1->raw);
|
||||
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(! http_parser_has_error(&parser) );
|
||||
assert(num_messages == 3);
|
||||
message_eq(0, r1);
|
||||
message_eq(1, r2);
|
||||
message_eq(2, r3);
|
||||
}
|
||||
|
||||
/* SCAN through every possible breaking to make sure the
|
||||
/* SCAN through every possible breaking to make sure the
|
||||
* parser can handle getting the content in any chunks that
|
||||
* might come from the socket
|
||||
*/
|
||||
@@ -619,13 +692,13 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
|
||||
char buf2[80*1024] = "\0";
|
||||
char buf3[80*1024] = "\0";
|
||||
|
||||
strcat(total, r1->raw);
|
||||
strcat(total, r2->raw);
|
||||
strcat(total, r3->raw);
|
||||
strcat(total, r1->raw);
|
||||
strcat(total, r2->raw);
|
||||
strcat(total, r3->raw);
|
||||
|
||||
int total_len = strlen(total);
|
||||
|
||||
int total_ops = (total_len - 1) * (total_len - 2) / 2;
|
||||
int total_ops = (total_len - 1) * (total_len - 2) / 2;
|
||||
int ops = 0 ;
|
||||
|
||||
int i,j;
|
||||
@@ -659,16 +732,16 @@ 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));
|
||||
|
||||
assert(! http_parser_has_error(&parser));
|
||||
http_parser_execute(&parser, NULL, 0);
|
||||
assert(!http_parser_has_error(&parser));
|
||||
|
||||
assert(3 == num_messages);
|
||||
|
||||
@@ -687,14 +760,14 @@ main (void)
|
||||
|
||||
printf("sizeof(http_parser) = %d\n", sizeof(http_parser));
|
||||
|
||||
int request_count;
|
||||
int request_count;
|
||||
for (request_count = 0; requests[request_count].name; request_count++);
|
||||
|
||||
int response_count;
|
||||
int response_count;
|
||||
for (response_count = 0; responses[response_count].name; response_count++);
|
||||
|
||||
|
||||
//// RESPONSES
|
||||
//// RESPONSES
|
||||
|
||||
for (i = 0; i < response_count; i++) {
|
||||
test_message(&responses[i]);
|
||||
@@ -755,7 +828,7 @@ main (void)
|
||||
"Accept: */*\r\n"
|
||||
"\r\n"
|
||||
"HELLO";
|
||||
test_error(bad_get_no_headers_no_body);
|
||||
test_error(bad_get_no_headers_no_body);
|
||||
|
||||
|
||||
/* TODO sending junk and large headers gets rejected */
|
||||
|
||||
@@ -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
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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
s.listen(port);
|
||||
if (++nrequests_completed == nrequests_expected) server.close();
|
||||
|
||||
var c = tcp.createConnection(port);
|
||||
c.addListener("connect", function () {
|
||||
c.send("GET /hello?foo=%99bar HTTP/1.1\r\n\r\n");
|
||||
c.close();
|
||||
puts("nrequests_completed: " + nrequests_completed);
|
||||
});
|
||||
server.listen(port);
|
||||
|
||||
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);
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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,7 +47,8 @@ var c = tcp.createConnection(port);
|
||||
c.setEncoding("utf8");
|
||||
|
||||
c.addListener("connect", function () {
|
||||
c.send( "GET /hello?hello=world&foo=b==ar HTTP/1.1\r\n\r\n" );
|
||||
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");
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user