http_parser.rb 0.5.3 → 0.6.0.beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitmodules +3 -3
- data/Gemfile +1 -1
- data/Gemfile.lock +9 -2
- data/README.md +50 -45
- data/bench/standalone.rb +23 -0
- data/bench/thin.rb +1 -0
- data/ext/ruby_http_parser/org/ruby_http_parser/RubyHttpParser.java +66 -58
- data/ext/ruby_http_parser/ruby_http_parser.c +10 -41
- data/ext/ruby_http_parser/vendor/http-parser-java/AUTHORS +32 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/LICENSE-MIT +5 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/README.md +133 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/TODO +6 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.c +1029 -615
- data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.gyp +79 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.h +177 -43
- data/ext/ruby_http_parser/vendor/http-parser-java/src/Http-parser.java.iml +22 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/FieldData.java +41 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPHeadersCompleteCallback.java +13 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPMethod.java +4 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParserUrl.java +76 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserSettings.java +2 -2
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/Util.java +6 -6
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPHeadersCompleteCallback.java +12 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java +715 -637
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java +1 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Message.java +71 -21
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/ParseUrl.java +51 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Requests.java +1 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Responses.java +1 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Test.java +2 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestHeaderOverflowError.java +1 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestLoaderNG.java +6 -17
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestNoOverflowLongBody.java +1 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/UnitTest.java +1 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Upgrade.java +1 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Url.java +127 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Util.java +80 -9
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/WrongContentLength.java +2 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/test.c +1141 -210
- data/ext/ruby_http_parser/vendor/http-parser-java/tests.dumped +230 -71
- data/ext/ruby_http_parser/vendor/http-parser/AUTHORS +32 -0
- data/ext/ruby_http_parser/vendor/http-parser/LICENSE-MIT +5 -1
- data/ext/ruby_http_parser/vendor/http-parser/README.md +9 -2
- data/ext/ruby_http_parser/vendor/http-parser/http_parser.c +1029 -615
- data/ext/ruby_http_parser/vendor/http-parser/http_parser.gyp +79 -0
- data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +145 -16
- data/ext/ruby_http_parser/vendor/http-parser/test.c +1065 -141
- data/http_parser.rb.gemspec +3 -1
- data/spec/parser_spec.rb +41 -17
- data/spec/support/requests.json +236 -24
- data/spec/support/responses.json +182 -36
- data/tasks/compile.rake +2 -2
- data/tasks/fixtures.rake +7 -1
- metadata +57 -19
- data/ext/ruby_http_parser/vendor/http-parser-java/compile +0 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/test_permutations +0 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/test_unit +0 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/test_utf8 +0 -1
@@ -0,0 +1,79 @@
|
|
1
|
+
# This file is used with the GYP meta build system.
|
2
|
+
# http://code.google.com/p/gyp/
|
3
|
+
# To build try this:
|
4
|
+
# svn co http://gyp.googlecode.com/svn/trunk gyp
|
5
|
+
# ./gyp/gyp -f make --depth=`pwd` http_parser.gyp
|
6
|
+
# ./out/Debug/test
|
7
|
+
{
|
8
|
+
'target_defaults': {
|
9
|
+
'default_configuration': 'Debug',
|
10
|
+
'configurations': {
|
11
|
+
# TODO: hoist these out and put them somewhere common, because
|
12
|
+
# RuntimeLibrary MUST MATCH across the entire project
|
13
|
+
'Debug': {
|
14
|
+
'defines': [ 'DEBUG', '_DEBUG' ],
|
15
|
+
'msvs_settings': {
|
16
|
+
'VCCLCompilerTool': {
|
17
|
+
'RuntimeLibrary': 1, # static debug
|
18
|
+
},
|
19
|
+
},
|
20
|
+
},
|
21
|
+
'Release': {
|
22
|
+
'defines': [ 'NDEBUG' ],
|
23
|
+
'msvs_settings': {
|
24
|
+
'VCCLCompilerTool': {
|
25
|
+
'RuntimeLibrary': 0, # static release
|
26
|
+
},
|
27
|
+
},
|
28
|
+
}
|
29
|
+
},
|
30
|
+
'msvs_settings': {
|
31
|
+
'VCCLCompilerTool': {
|
32
|
+
},
|
33
|
+
'VCLibrarianTool': {
|
34
|
+
},
|
35
|
+
'VCLinkerTool': {
|
36
|
+
'GenerateDebugInformation': 'true',
|
37
|
+
},
|
38
|
+
},
|
39
|
+
'conditions': [
|
40
|
+
['OS == "win"', {
|
41
|
+
'defines': [
|
42
|
+
'WIN32'
|
43
|
+
],
|
44
|
+
}]
|
45
|
+
],
|
46
|
+
},
|
47
|
+
|
48
|
+
'targets': [
|
49
|
+
{
|
50
|
+
'target_name': 'http_parser',
|
51
|
+
'type': 'static_library',
|
52
|
+
'include_dirs': [ '.' ],
|
53
|
+
'direct_dependent_settings': {
|
54
|
+
'include_dirs': [ '.' ],
|
55
|
+
},
|
56
|
+
'defines': [ 'HTTP_PARSER_STRICT=0' ],
|
57
|
+
'sources': [ './http_parser.c', ],
|
58
|
+
'conditions': [
|
59
|
+
['OS=="win"', {
|
60
|
+
'msvs_settings': {
|
61
|
+
'VCCLCompilerTool': {
|
62
|
+
# Compile as C++. http_parser.c is actually C99, but C++ is
|
63
|
+
# close enough in this case.
|
64
|
+
'CompileAs': 2,
|
65
|
+
},
|
66
|
+
},
|
67
|
+
}]
|
68
|
+
],
|
69
|
+
},
|
70
|
+
|
71
|
+
{
|
72
|
+
'target_name': 'test',
|
73
|
+
'type': 'executable',
|
74
|
+
'dependencies': [ 'http_parser' ],
|
75
|
+
'sources': [ 'test.c' ]
|
76
|
+
}
|
77
|
+
]
|
78
|
+
}
|
79
|
+
|
@@ -28,7 +28,7 @@ extern "C" {
|
|
28
28
|
#define HTTP_PARSER_VERSION_MINOR 0
|
29
29
|
|
30
30
|
#include <sys/types.h>
|
31
|
-
#if defined(_WIN32) && !defined(__MINGW32__)
|
31
|
+
#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600)
|
32
32
|
typedef __int8 int8_t;
|
33
33
|
typedef unsigned __int8 uint8_t;
|
34
34
|
typedef __int16 int16_t;
|
@@ -49,8 +49,13 @@ typedef int ssize_t;
|
|
49
49
|
*/
|
50
50
|
#ifndef HTTP_PARSER_STRICT
|
51
51
|
# define HTTP_PARSER_STRICT 1
|
52
|
-
#
|
53
|
-
|
52
|
+
#endif
|
53
|
+
|
54
|
+
/* Compile with -DHTTP_PARSER_DEBUG=1 to add extra debugging information to
|
55
|
+
* the error reporting facility.
|
56
|
+
*/
|
57
|
+
#ifndef HTTP_PARSER_DEBUG
|
58
|
+
# define HTTP_PARSER_DEBUG 0
|
54
59
|
#endif
|
55
60
|
|
56
61
|
|
@@ -60,6 +65,7 @@ typedef int ssize_t;
|
|
60
65
|
|
61
66
|
typedef struct http_parser http_parser;
|
62
67
|
typedef struct http_parser_settings http_parser_settings;
|
68
|
+
typedef struct http_parser_result http_parser_result;
|
63
69
|
|
64
70
|
|
65
71
|
/* Callbacks should return non-zero to indicate an error. The parser will
|
@@ -108,35 +114,118 @@ enum http_method
|
|
108
114
|
, HTTP_NOTIFY
|
109
115
|
, HTTP_SUBSCRIBE
|
110
116
|
, HTTP_UNSUBSCRIBE
|
117
|
+
/* RFC-5789 */
|
118
|
+
, HTTP_PATCH
|
119
|
+
, HTTP_PURGE
|
111
120
|
};
|
112
121
|
|
113
122
|
|
114
123
|
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
|
115
124
|
|
116
125
|
|
126
|
+
/* Flag values for http_parser.flags field */
|
127
|
+
enum flags
|
128
|
+
{ F_CHUNKED = 1 << 0
|
129
|
+
, F_CONNECTION_KEEP_ALIVE = 1 << 1
|
130
|
+
, F_CONNECTION_CLOSE = 1 << 2
|
131
|
+
, F_TRAILING = 1 << 3
|
132
|
+
, F_UPGRADE = 1 << 4
|
133
|
+
, F_SKIPBODY = 1 << 5
|
134
|
+
};
|
135
|
+
|
136
|
+
|
137
|
+
/* Map for errno-related constants
|
138
|
+
*
|
139
|
+
* The provided argument should be a macro that takes 2 arguments.
|
140
|
+
*/
|
141
|
+
#define HTTP_ERRNO_MAP(XX) \
|
142
|
+
/* No error */ \
|
143
|
+
XX(OK, "success") \
|
144
|
+
\
|
145
|
+
/* Callback-related errors */ \
|
146
|
+
XX(CB_message_begin, "the on_message_begin callback failed") \
|
147
|
+
XX(CB_url, "the on_url callback failed") \
|
148
|
+
XX(CB_header_field, "the on_header_field callback failed") \
|
149
|
+
XX(CB_header_value, "the on_header_value callback failed") \
|
150
|
+
XX(CB_headers_complete, "the on_headers_complete callback failed") \
|
151
|
+
XX(CB_body, "the on_body callback failed") \
|
152
|
+
XX(CB_message_complete, "the on_message_complete callback failed") \
|
153
|
+
\
|
154
|
+
/* Parsing-related errors */ \
|
155
|
+
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
|
156
|
+
XX(HEADER_OVERFLOW, \
|
157
|
+
"too many header bytes seen; overflow detected") \
|
158
|
+
XX(CLOSED_CONNECTION, \
|
159
|
+
"data received after completed connection: close message") \
|
160
|
+
XX(INVALID_VERSION, "invalid HTTP version") \
|
161
|
+
XX(INVALID_STATUS, "invalid HTTP status code") \
|
162
|
+
XX(INVALID_METHOD, "invalid HTTP method") \
|
163
|
+
XX(INVALID_URL, "invalid URL") \
|
164
|
+
XX(INVALID_HOST, "invalid host") \
|
165
|
+
XX(INVALID_PORT, "invalid port") \
|
166
|
+
XX(INVALID_PATH, "invalid path") \
|
167
|
+
XX(INVALID_QUERY_STRING, "invalid query string") \
|
168
|
+
XX(INVALID_FRAGMENT, "invalid fragment") \
|
169
|
+
XX(LF_EXPECTED, "LF character expected") \
|
170
|
+
XX(INVALID_HEADER_TOKEN, "invalid character in header") \
|
171
|
+
XX(INVALID_CONTENT_LENGTH, \
|
172
|
+
"invalid character in content-length header") \
|
173
|
+
XX(INVALID_CHUNK_SIZE, \
|
174
|
+
"invalid character in chunk size header") \
|
175
|
+
XX(INVALID_CONSTANT, "invalid constant string") \
|
176
|
+
XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
|
177
|
+
XX(STRICT, "strict mode assertion failed") \
|
178
|
+
XX(PAUSED, "parser is paused") \
|
179
|
+
XX(UNKNOWN, "an unknown error occurred")
|
180
|
+
|
181
|
+
|
182
|
+
/* Define HPE_* values for each errno value above */
|
183
|
+
#define HTTP_ERRNO_GEN(n, s) HPE_##n,
|
184
|
+
enum http_errno {
|
185
|
+
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
|
186
|
+
};
|
187
|
+
#undef HTTP_ERRNO_GEN
|
188
|
+
|
189
|
+
|
190
|
+
/* Get an http_errno value from an http_parser */
|
191
|
+
#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
|
192
|
+
|
193
|
+
/* Get the line number that generated the current error */
|
194
|
+
#if HTTP_PARSER_DEBUG
|
195
|
+
#define HTTP_PARSER_ERRNO_LINE(p) ((p)->error_lineno)
|
196
|
+
#else
|
197
|
+
#define HTTP_PARSER_ERRNO_LINE(p) 0
|
198
|
+
#endif
|
199
|
+
|
200
|
+
|
117
201
|
struct http_parser {
|
118
202
|
/** PRIVATE **/
|
119
|
-
unsigned char type : 2;
|
120
|
-
unsigned char flags : 6;
|
121
|
-
unsigned char state;
|
122
|
-
unsigned char header_state;
|
123
|
-
unsigned char index;
|
203
|
+
unsigned char type : 2; /* enum http_parser_type */
|
204
|
+
unsigned char flags : 6; /* F_* values from 'flags' enum; semi-public */
|
205
|
+
unsigned char state; /* enum state from http_parser.c */
|
206
|
+
unsigned char header_state; /* enum header_state from http_parser.c */
|
207
|
+
unsigned char index; /* index into current matcher */
|
124
208
|
|
125
|
-
uint32_t nread;
|
126
|
-
|
209
|
+
uint32_t nread; /* # bytes read in various scenarios */
|
210
|
+
uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
|
127
211
|
|
128
212
|
/** READ-ONLY **/
|
129
213
|
unsigned short http_major;
|
130
214
|
unsigned short http_minor;
|
131
215
|
unsigned short status_code; /* responses only */
|
132
|
-
unsigned char method;
|
216
|
+
unsigned char method; /* requests only */
|
217
|
+
unsigned char http_errno : 7;
|
133
218
|
|
134
219
|
/* 1 = Upgrade header was present and the parser has exited because of that.
|
135
220
|
* 0 = No upgrade header present.
|
136
221
|
* Should be checked when http_parser_execute() returns in addition to
|
137
222
|
* error checking.
|
138
223
|
*/
|
139
|
-
char upgrade;
|
224
|
+
unsigned char upgrade : 1;
|
225
|
+
|
226
|
+
#if HTTP_PARSER_DEBUG
|
227
|
+
uint32_t error_lineno;
|
228
|
+
#endif
|
140
229
|
|
141
230
|
/** PUBLIC **/
|
142
231
|
void *data; /* A pointer to get hook to the "connection" or "socket" object */
|
@@ -145,10 +234,7 @@ struct http_parser {
|
|
145
234
|
|
146
235
|
struct http_parser_settings {
|
147
236
|
http_cb on_message_begin;
|
148
|
-
http_data_cb on_path;
|
149
|
-
http_data_cb on_query_string;
|
150
237
|
http_data_cb on_url;
|
151
|
-
http_data_cb on_fragment;
|
152
238
|
http_data_cb on_header_field;
|
153
239
|
http_data_cb on_header_value;
|
154
240
|
http_cb on_headers_complete;
|
@@ -157,6 +243,35 @@ struct http_parser_settings {
|
|
157
243
|
};
|
158
244
|
|
159
245
|
|
246
|
+
enum http_parser_url_fields
|
247
|
+
{ UF_SCHEMA = 0
|
248
|
+
, UF_HOST = 1
|
249
|
+
, UF_PORT = 2
|
250
|
+
, UF_PATH = 3
|
251
|
+
, UF_QUERY = 4
|
252
|
+
, UF_FRAGMENT = 5
|
253
|
+
, UF_MAX = 6
|
254
|
+
};
|
255
|
+
|
256
|
+
|
257
|
+
/* Result structure for http_parser_parse_url().
|
258
|
+
*
|
259
|
+
* Callers should index into field_data[] with UF_* values iff field_set
|
260
|
+
* has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
|
261
|
+
* because we probably have padding left over), we convert any port to
|
262
|
+
* a uint16_t.
|
263
|
+
*/
|
264
|
+
struct http_parser_url {
|
265
|
+
uint16_t field_set; /* Bitmask of (1 << UF_*) values */
|
266
|
+
uint16_t port; /* Converted UF_PORT string */
|
267
|
+
|
268
|
+
struct {
|
269
|
+
uint16_t off; /* Offset into buffer in which field starts */
|
270
|
+
uint16_t len; /* Length of run in buffer */
|
271
|
+
} field_data[UF_MAX];
|
272
|
+
};
|
273
|
+
|
274
|
+
|
160
275
|
void http_parser_init(http_parser *parser, enum http_parser_type type);
|
161
276
|
|
162
277
|
|
@@ -175,7 +290,21 @@ size_t http_parser_execute(http_parser *parser,
|
|
175
290
|
int http_should_keep_alive(http_parser *parser);
|
176
291
|
|
177
292
|
/* Returns a string version of the HTTP method. */
|
178
|
-
const char *http_method_str(enum http_method);
|
293
|
+
const char *http_method_str(enum http_method m);
|
294
|
+
|
295
|
+
/* Return a string name of the given error */
|
296
|
+
const char *http_errno_name(enum http_errno err);
|
297
|
+
|
298
|
+
/* Return a string description of the given error */
|
299
|
+
const char *http_errno_description(enum http_errno err);
|
300
|
+
|
301
|
+
/* Parse a URL; return nonzero on failure */
|
302
|
+
int http_parser_parse_url(const char *buf, size_t buflen,
|
303
|
+
int is_connect,
|
304
|
+
struct http_parser_url *u);
|
305
|
+
|
306
|
+
/* Pause or un-pause the parser; a nonzero value pauses */
|
307
|
+
void http_parser_pause(http_parser *parser, int paused);
|
179
308
|
|
180
309
|
#ifdef __cplusplus
|
181
310
|
}
|
@@ -50,12 +50,13 @@ struct message {
|
|
50
50
|
char query_string[MAX_ELEMENT_SIZE];
|
51
51
|
char body[MAX_ELEMENT_SIZE];
|
52
52
|
size_t body_size;
|
53
|
+
uint16_t port;
|
53
54
|
int num_headers;
|
54
55
|
enum { NONE=0, FIELD, VALUE } last_header_element;
|
55
56
|
char headers [MAX_HEADERS][2][MAX_ELEMENT_SIZE];
|
56
57
|
int should_keep_alive;
|
57
58
|
|
58
|
-
|
59
|
+
const char *upgrade; // upgraded body
|
59
60
|
|
60
61
|
unsigned short http_major;
|
61
62
|
unsigned short http_minor;
|
@@ -70,6 +71,7 @@ static int currently_parsing_eof;
|
|
70
71
|
|
71
72
|
static struct message messages[5];
|
72
73
|
static int num_messages;
|
74
|
+
static http_parser_settings *current_pause_parser;
|
73
75
|
|
74
76
|
/* * R E Q U E S T S * */
|
75
77
|
const struct message requests[] =
|
@@ -473,6 +475,7 @@ const struct message requests[] =
|
|
473
475
|
"Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n"
|
474
476
|
"Origin: http://example.com\r\n"
|
475
477
|
"\r\n"
|
478
|
+
"Hot diggity dogg"
|
476
479
|
,.should_keep_alive= TRUE
|
477
480
|
,.message_complete_on_eof= FALSE
|
478
481
|
,.http_major= 1
|
@@ -483,7 +486,7 @@ const struct message requests[] =
|
|
483
486
|
,.request_path= "/demo"
|
484
487
|
,.request_url= "/demo"
|
485
488
|
,.num_headers= 7
|
486
|
-
,.upgrade=
|
489
|
+
,.upgrade="Hot diggity dogg"
|
487
490
|
,.headers= { { "Host", "example.com" }
|
488
491
|
, { "Connection", "Upgrade" }
|
489
492
|
, { "Sec-WebSocket-Key2", "12998 5 Y3 1 .P00" }
|
@@ -498,10 +501,12 @@ const struct message requests[] =
|
|
498
501
|
#define CONNECT_REQUEST 17
|
499
502
|
, {.name = "connect request"
|
500
503
|
,.type= HTTP_REQUEST
|
501
|
-
,.raw= "CONNECT home0.netscape.com:443 HTTP/1.0\r\n"
|
504
|
+
,.raw= "CONNECT 0-home0.netscape.com:443 HTTP/1.0\r\n"
|
502
505
|
"User-agent: Mozilla/1.1N\r\n"
|
503
506
|
"Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
|
504
507
|
"\r\n"
|
508
|
+
"some data\r\n"
|
509
|
+
"and yet even more data"
|
505
510
|
,.should_keep_alive= FALSE
|
506
511
|
,.message_complete_on_eof= FALSE
|
507
512
|
,.http_major= 1
|
@@ -510,9 +515,9 @@ const struct message requests[] =
|
|
510
515
|
,.query_string= ""
|
511
516
|
,.fragment= ""
|
512
517
|
,.request_path= ""
|
513
|
-
,.request_url= "home0.netscape.com:443"
|
518
|
+
,.request_url= "0-home0.netscape.com:443"
|
514
519
|
,.num_headers= 2
|
515
|
-
,.upgrade=
|
520
|
+
,.upgrade="some data\r\nand yet even more data"
|
516
521
|
,.headers= { { "User-agent", "Mozilla/1.1N" }
|
517
522
|
, { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
|
518
523
|
}
|
@@ -582,28 +587,36 @@ const struct message requests[] =
|
|
582
587
|
,.body= ""
|
583
588
|
}
|
584
589
|
|
585
|
-
#define
|
586
|
-
, {.name= "
|
590
|
+
#define LINE_FOLDING_IN_HEADER 20
|
591
|
+
, {.name= "line folding in header value"
|
587
592
|
,.type= HTTP_REQUEST
|
588
|
-
,.raw= "GET
|
589
|
-
"
|
593
|
+
,.raw= "GET / HTTP/1.1\r\n"
|
594
|
+
"Line1: abc\r\n"
|
595
|
+
"\tdef\r\n"
|
596
|
+
" ghi\r\n"
|
597
|
+
"\t\tjkl\r\n"
|
598
|
+
" mno \r\n"
|
599
|
+
"\t \tqrs\r\n"
|
600
|
+
"Line2: \t line2\t\r\n"
|
590
601
|
"\r\n"
|
591
602
|
,.should_keep_alive= TRUE
|
592
603
|
,.message_complete_on_eof= FALSE
|
593
604
|
,.http_major= 1
|
594
605
|
,.http_minor= 1
|
595
606
|
,.method= HTTP_GET
|
596
|
-
,.query_string= "
|
597
|
-
,.fragment= "
|
598
|
-
,.request_path= "
|
599
|
-
,.request_url= "
|
600
|
-
,.num_headers=
|
601
|
-
,.headers= { {"
|
607
|
+
,.query_string= ""
|
608
|
+
,.fragment= ""
|
609
|
+
,.request_path= "/"
|
610
|
+
,.request_url= "/"
|
611
|
+
,.num_headers= 2
|
612
|
+
,.headers= { { "Line1", "abcdefghijklmno qrs" }
|
613
|
+
, { "Line2", "line2\t" }
|
602
614
|
}
|
603
615
|
,.body= ""
|
604
616
|
}
|
605
617
|
|
606
|
-
|
618
|
+
|
619
|
+
#define QUERY_TERMINATED_HOST 21
|
607
620
|
, {.name= "host terminated by a query string"
|
608
621
|
,.type= HTTP_REQUEST
|
609
622
|
,.raw= "GET http://hypnotoad.org?hail=all HTTP/1.1\r\n"
|
@@ -622,7 +635,7 @@ const struct message requests[] =
|
|
622
635
|
,.body= ""
|
623
636
|
}
|
624
637
|
|
625
|
-
#define QUERY_TERMINATED_HOSTPORT
|
638
|
+
#define QUERY_TERMINATED_HOSTPORT 22
|
626
639
|
, {.name= "host:port terminated by a query string"
|
627
640
|
,.type= HTTP_REQUEST
|
628
641
|
,.raw= "GET http://hypnotoad.org:1234?hail=all HTTP/1.1\r\n"
|
@@ -636,12 +649,13 @@ const struct message requests[] =
|
|
636
649
|
,.fragment= ""
|
637
650
|
,.request_path= ""
|
638
651
|
,.request_url= "http://hypnotoad.org:1234?hail=all"
|
652
|
+
,.port= 1234
|
639
653
|
,.num_headers= 0
|
640
654
|
,.headers= { }
|
641
655
|
,.body= ""
|
642
656
|
}
|
643
657
|
|
644
|
-
#define SPACE_TERMINATED_HOSTPORT
|
658
|
+
#define SPACE_TERMINATED_HOSTPORT 23
|
645
659
|
, {.name= "host:port terminated by a space"
|
646
660
|
,.type= HTTP_REQUEST
|
647
661
|
,.raw= "GET http://hypnotoad.org:1234 HTTP/1.1\r\n"
|
@@ -655,11 +669,187 @@ const struct message requests[] =
|
|
655
669
|
,.fragment= ""
|
656
670
|
,.request_path= ""
|
657
671
|
,.request_url= "http://hypnotoad.org:1234"
|
672
|
+
,.port= 1234
|
658
673
|
,.num_headers= 0
|
659
674
|
,.headers= { }
|
660
675
|
,.body= ""
|
661
676
|
}
|
662
677
|
|
678
|
+
#define PATCH_REQ 24
|
679
|
+
, {.name = "PATCH request"
|
680
|
+
,.type= HTTP_REQUEST
|
681
|
+
,.raw= "PATCH /file.txt HTTP/1.1\r\n"
|
682
|
+
"Host: www.example.com\r\n"
|
683
|
+
"Content-Type: application/example\r\n"
|
684
|
+
"If-Match: \"e0023aa4e\"\r\n"
|
685
|
+
"Content-Length: 10\r\n"
|
686
|
+
"\r\n"
|
687
|
+
"cccccccccc"
|
688
|
+
,.should_keep_alive= TRUE
|
689
|
+
,.message_complete_on_eof= FALSE
|
690
|
+
,.http_major= 1
|
691
|
+
,.http_minor= 1
|
692
|
+
,.method= HTTP_PATCH
|
693
|
+
,.query_string= ""
|
694
|
+
,.fragment= ""
|
695
|
+
,.request_path= "/file.txt"
|
696
|
+
,.request_url= "/file.txt"
|
697
|
+
,.num_headers= 4
|
698
|
+
,.headers= { { "Host", "www.example.com" }
|
699
|
+
, { "Content-Type", "application/example" }
|
700
|
+
, { "If-Match", "\"e0023aa4e\"" }
|
701
|
+
, { "Content-Length", "10" }
|
702
|
+
}
|
703
|
+
,.body= "cccccccccc"
|
704
|
+
}
|
705
|
+
|
706
|
+
#define CONNECT_CAPS_REQUEST 25
|
707
|
+
, {.name = "connect caps request"
|
708
|
+
,.type= HTTP_REQUEST
|
709
|
+
,.raw= "CONNECT HOME0.NETSCAPE.COM:443 HTTP/1.0\r\n"
|
710
|
+
"User-agent: Mozilla/1.1N\r\n"
|
711
|
+
"Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
|
712
|
+
"\r\n"
|
713
|
+
,.should_keep_alive= FALSE
|
714
|
+
,.message_complete_on_eof= FALSE
|
715
|
+
,.http_major= 1
|
716
|
+
,.http_minor= 0
|
717
|
+
,.method= HTTP_CONNECT
|
718
|
+
,.query_string= ""
|
719
|
+
,.fragment= ""
|
720
|
+
,.request_path= ""
|
721
|
+
,.request_url= "HOME0.NETSCAPE.COM:443"
|
722
|
+
,.num_headers= 2
|
723
|
+
,.upgrade=""
|
724
|
+
,.headers= { { "User-agent", "Mozilla/1.1N" }
|
725
|
+
, { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
|
726
|
+
}
|
727
|
+
,.body= ""
|
728
|
+
}
|
729
|
+
|
730
|
+
#if !HTTP_PARSER_STRICT
|
731
|
+
#define UTF8_PATH_REQ 26
|
732
|
+
, {.name= "utf-8 path request"
|
733
|
+
,.type= HTTP_REQUEST
|
734
|
+
,.raw= "GET /δ¶/δt/pope?q=1#narf HTTP/1.1\r\n"
|
735
|
+
"Host: github.com\r\n"
|
736
|
+
"\r\n"
|
737
|
+
,.should_keep_alive= TRUE
|
738
|
+
,.message_complete_on_eof= FALSE
|
739
|
+
,.http_major= 1
|
740
|
+
,.http_minor= 1
|
741
|
+
,.method= HTTP_GET
|
742
|
+
,.query_string= "q=1"
|
743
|
+
,.fragment= "narf"
|
744
|
+
,.request_path= "/δ¶/δt/pope"
|
745
|
+
,.request_url= "/δ¶/δt/pope?q=1#narf"
|
746
|
+
,.num_headers= 1
|
747
|
+
,.headers= { {"Host", "github.com" }
|
748
|
+
}
|
749
|
+
,.body= ""
|
750
|
+
}
|
751
|
+
|
752
|
+
#define HOSTNAME_UNDERSCORE 27
|
753
|
+
, {.name = "hostname underscore"
|
754
|
+
,.type= HTTP_REQUEST
|
755
|
+
,.raw= "CONNECT home_0.netscape.com:443 HTTP/1.0\r\n"
|
756
|
+
"User-agent: Mozilla/1.1N\r\n"
|
757
|
+
"Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
|
758
|
+
"\r\n"
|
759
|
+
,.should_keep_alive= FALSE
|
760
|
+
,.message_complete_on_eof= FALSE
|
761
|
+
,.http_major= 1
|
762
|
+
,.http_minor= 0
|
763
|
+
,.method= HTTP_CONNECT
|
764
|
+
,.query_string= ""
|
765
|
+
,.fragment= ""
|
766
|
+
,.request_path= ""
|
767
|
+
,.request_url= "home_0.netscape.com:443"
|
768
|
+
,.num_headers= 2
|
769
|
+
,.upgrade=""
|
770
|
+
,.headers= { { "User-agent", "Mozilla/1.1N" }
|
771
|
+
, { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
|
772
|
+
}
|
773
|
+
,.body= ""
|
774
|
+
}
|
775
|
+
#endif /* !HTTP_PARSER_STRICT */
|
776
|
+
|
777
|
+
/* see https://github.com/ry/http-parser/issues/47 */
|
778
|
+
#define EAT_TRAILING_CRLF_NO_CONNECTION_CLOSE 28
|
779
|
+
, {.name = "eat CRLF between requests, no \"Connection: close\" header"
|
780
|
+
,.raw= "POST / HTTP/1.1\r\n"
|
781
|
+
"Host: www.example.com\r\n"
|
782
|
+
"Content-Type: application/x-www-form-urlencoded\r\n"
|
783
|
+
"Content-Length: 4\r\n"
|
784
|
+
"\r\n"
|
785
|
+
"q=42\r\n" /* note the trailing CRLF */
|
786
|
+
,.should_keep_alive= TRUE
|
787
|
+
,.message_complete_on_eof= FALSE
|
788
|
+
,.http_major= 1
|
789
|
+
,.http_minor= 1
|
790
|
+
,.method= HTTP_POST
|
791
|
+
,.query_string= ""
|
792
|
+
,.fragment= ""
|
793
|
+
,.request_path= "/"
|
794
|
+
,.request_url= "/"
|
795
|
+
,.num_headers= 3
|
796
|
+
,.upgrade= 0
|
797
|
+
,.headers= { { "Host", "www.example.com" }
|
798
|
+
, { "Content-Type", "application/x-www-form-urlencoded" }
|
799
|
+
, { "Content-Length", "4" }
|
800
|
+
}
|
801
|
+
,.body= "q=42"
|
802
|
+
}
|
803
|
+
|
804
|
+
/* see https://github.com/ry/http-parser/issues/47 */
|
805
|
+
#define EAT_TRAILING_CRLF_WITH_CONNECTION_CLOSE 29
|
806
|
+
, {.name = "eat CRLF between requests even if \"Connection: close\" is set"
|
807
|
+
,.raw= "POST / HTTP/1.1\r\n"
|
808
|
+
"Host: www.example.com\r\n"
|
809
|
+
"Content-Type: application/x-www-form-urlencoded\r\n"
|
810
|
+
"Content-Length: 4\r\n"
|
811
|
+
"Connection: close\r\n"
|
812
|
+
"\r\n"
|
813
|
+
"q=42\r\n" /* note the trailing CRLF */
|
814
|
+
,.should_keep_alive= FALSE
|
815
|
+
,.message_complete_on_eof= FALSE /* input buffer isn't empty when on_message_complete is called */
|
816
|
+
,.http_major= 1
|
817
|
+
,.http_minor= 1
|
818
|
+
,.method= HTTP_POST
|
819
|
+
,.query_string= ""
|
820
|
+
,.fragment= ""
|
821
|
+
,.request_path= "/"
|
822
|
+
,.request_url= "/"
|
823
|
+
,.num_headers= 4
|
824
|
+
,.upgrade= 0
|
825
|
+
,.headers= { { "Host", "www.example.com" }
|
826
|
+
, { "Content-Type", "application/x-www-form-urlencoded" }
|
827
|
+
, { "Content-Length", "4" }
|
828
|
+
, { "Connection", "close" }
|
829
|
+
}
|
830
|
+
,.body= "q=42"
|
831
|
+
}
|
832
|
+
|
833
|
+
#define PURGE_REQ 30
|
834
|
+
, {.name = "PURGE request"
|
835
|
+
,.type= HTTP_REQUEST
|
836
|
+
,.raw= "PURGE /file.txt HTTP/1.1\r\n"
|
837
|
+
"Host: www.example.com\r\n"
|
838
|
+
"\r\n"
|
839
|
+
,.should_keep_alive= TRUE
|
840
|
+
,.message_complete_on_eof= FALSE
|
841
|
+
,.http_major= 1
|
842
|
+
,.http_minor= 1
|
843
|
+
,.method= HTTP_PURGE
|
844
|
+
,.query_string= ""
|
845
|
+
,.fragment= ""
|
846
|
+
,.request_path= "/file.txt"
|
847
|
+
,.request_url= "/file.txt"
|
848
|
+
,.num_headers= 1
|
849
|
+
,.headers= { { "Host", "www.example.com" } }
|
850
|
+
,.body= ""
|
851
|
+
}
|
852
|
+
|
663
853
|
, {.name= NULL } /* sentinel */
|
664
854
|
};
|
665
855
|
|
@@ -760,8 +950,8 @@ const struct message responses[] =
|
|
760
950
|
, {.name= "404 no headers no body"
|
761
951
|
,.type= HTTP_RESPONSE
|
762
952
|
,.raw= "HTTP/1.1 404 Not Found\r\n\r\n"
|
763
|
-
,.should_keep_alive=
|
764
|
-
,.message_complete_on_eof=
|
953
|
+
,.should_keep_alive= FALSE
|
954
|
+
,.message_complete_on_eof= TRUE
|
765
955
|
,.http_major= 1
|
766
956
|
,.http_minor= 1
|
767
957
|
,.status_code= 404
|
@@ -775,8 +965,8 @@ const struct message responses[] =
|
|
775
965
|
, {.name= "301 no response phrase"
|
776
966
|
,.type= HTTP_RESPONSE
|
777
967
|
,.raw= "HTTP/1.1 301\r\n\r\n"
|
778
|
-
,.should_keep_alive =
|
779
|
-
,.message_complete_on_eof=
|
968
|
+
,.should_keep_alive = FALSE
|
969
|
+
,.message_complete_on_eof= TRUE
|
780
970
|
,.http_major= 1
|
781
971
|
,.http_minor= 1
|
782
972
|
,.status_code= 301
|
@@ -925,40 +1115,7 @@ const struct message responses[] =
|
|
925
1115
|
,.body= ""
|
926
1116
|
}
|
927
1117
|
|
928
|
-
#define
|
929
|
-
/* Should handle spaces in header fields */
|
930
|
-
, {.name= "field space"
|
931
|
-
,.type= HTTP_RESPONSE
|
932
|
-
,.raw= "HTTP/1.1 200 OK\r\n"
|
933
|
-
"Server: Microsoft-IIS/6.0\r\n"
|
934
|
-
"X-Powered-By: ASP.NET\r\n"
|
935
|
-
"en-US Content-Type: text/xml\r\n" /* this is the problem */
|
936
|
-
"Content-Type: text/xml\r\n"
|
937
|
-
"Content-Length: 16\r\n"
|
938
|
-
"Date: Fri, 23 Jul 2010 18:45:38 GMT\r\n"
|
939
|
-
"Connection: keep-alive\r\n"
|
940
|
-
"\r\n"
|
941
|
-
"<xml>hello</xml>" /* fake body */
|
942
|
-
,.should_keep_alive= TRUE
|
943
|
-
,.message_complete_on_eof= FALSE
|
944
|
-
,.http_major= 1
|
945
|
-
,.http_minor= 1
|
946
|
-
,.status_code= 200
|
947
|
-
,.num_headers= 7
|
948
|
-
,.headers=
|
949
|
-
{ { "Server", "Microsoft-IIS/6.0" }
|
950
|
-
, { "X-Powered-By", "ASP.NET" }
|
951
|
-
, { "en-US Content-Type", "text/xml" }
|
952
|
-
, { "Content-Type", "text/xml" }
|
953
|
-
, { "Content-Length", "16" }
|
954
|
-
, { "Date", "Fri, 23 Jul 2010 18:45:38 GMT" }
|
955
|
-
, { "Connection", "keep-alive" }
|
956
|
-
}
|
957
|
-
,.body= "<xml>hello</xml>"
|
958
|
-
}
|
959
|
-
|
960
|
-
|
961
|
-
#define RES_FIELD_UNDERSCORE 10
|
1118
|
+
#define RES_FIELD_UNDERSCORE 9
|
962
1119
|
/* Should handle spaces in header fields */
|
963
1120
|
, {.name= "field underscore"
|
964
1121
|
,.type= HTTP_RESPONSE
|
@@ -998,7 +1155,7 @@ const struct message responses[] =
|
|
998
1155
|
,.body= ""
|
999
1156
|
}
|
1000
1157
|
|
1001
|
-
#define NON_ASCII_IN_STATUS_LINE
|
1158
|
+
#define NON_ASCII_IN_STATUS_LINE 10
|
1002
1159
|
/* Should handle non-ASCII in status line */
|
1003
1160
|
, {.name= "non-ASCII in status line"
|
1004
1161
|
,.type= HTTP_RESPONSE
|
@@ -1018,21 +1175,196 @@ const struct message responses[] =
|
|
1018
1175
|
, { "Content-Length", "0" }
|
1019
1176
|
, { "Connection", "close" }
|
1020
1177
|
}
|
1021
|
-
,.body= ""
|
1178
|
+
,.body= ""
|
1179
|
+
}
|
1180
|
+
|
1181
|
+
#define HTTP_VERSION_0_9 11
|
1182
|
+
/* Should handle HTTP/0.9 */
|
1183
|
+
, {.name= "http version 0.9"
|
1184
|
+
,.type= HTTP_RESPONSE
|
1185
|
+
,.raw= "HTTP/0.9 200 OK\r\n"
|
1186
|
+
"\r\n"
|
1187
|
+
,.should_keep_alive= FALSE
|
1188
|
+
,.message_complete_on_eof= TRUE
|
1189
|
+
,.http_major= 0
|
1190
|
+
,.http_minor= 9
|
1191
|
+
,.status_code= 200
|
1192
|
+
,.num_headers= 0
|
1193
|
+
,.headers=
|
1194
|
+
{}
|
1195
|
+
,.body= ""
|
1196
|
+
}
|
1197
|
+
|
1198
|
+
#define NO_CONTENT_LENGTH_NO_TRANSFER_ENCODING_RESPONSE 12
|
1199
|
+
/* The client should wait for the server's EOF. That is, when neither
|
1200
|
+
* content-length nor transfer-encoding is specified, the end of body
|
1201
|
+
* is specified by the EOF.
|
1202
|
+
*/
|
1203
|
+
, {.name= "neither content-length nor transfer-encoding response"
|
1204
|
+
,.type= HTTP_RESPONSE
|
1205
|
+
,.raw= "HTTP/1.1 200 OK\r\n"
|
1206
|
+
"Content-Type: text/plain\r\n"
|
1207
|
+
"\r\n"
|
1208
|
+
"hello world"
|
1209
|
+
,.should_keep_alive= FALSE
|
1210
|
+
,.message_complete_on_eof= TRUE
|
1211
|
+
,.http_major= 1
|
1212
|
+
,.http_minor= 1
|
1213
|
+
,.status_code= 200
|
1214
|
+
,.num_headers= 1
|
1215
|
+
,.headers=
|
1216
|
+
{ { "Content-Type", "text/plain" }
|
1217
|
+
}
|
1218
|
+
,.body= "hello world"
|
1219
|
+
}
|
1220
|
+
|
1221
|
+
#define NO_BODY_HTTP10_KA_200 13
|
1222
|
+
, {.name= "HTTP/1.0 with keep-alive and EOF-terminated 200 status"
|
1223
|
+
,.type= HTTP_RESPONSE
|
1224
|
+
,.raw= "HTTP/1.0 200 OK\r\n"
|
1225
|
+
"Connection: keep-alive\r\n"
|
1226
|
+
"\r\n"
|
1227
|
+
,.should_keep_alive= FALSE
|
1228
|
+
,.message_complete_on_eof= TRUE
|
1229
|
+
,.http_major= 1
|
1230
|
+
,.http_minor= 0
|
1231
|
+
,.status_code= 200
|
1232
|
+
,.num_headers= 1
|
1233
|
+
,.headers=
|
1234
|
+
{ { "Connection", "keep-alive" }
|
1235
|
+
}
|
1236
|
+
,.body_size= 0
|
1237
|
+
,.body= ""
|
1238
|
+
}
|
1239
|
+
|
1240
|
+
#define NO_BODY_HTTP10_KA_204 14
|
1241
|
+
, {.name= "HTTP/1.0 with keep-alive and a 204 status"
|
1242
|
+
,.type= HTTP_RESPONSE
|
1243
|
+
,.raw= "HTTP/1.0 204 No content\r\n"
|
1244
|
+
"Connection: keep-alive\r\n"
|
1245
|
+
"\r\n"
|
1246
|
+
,.should_keep_alive= TRUE
|
1247
|
+
,.message_complete_on_eof= FALSE
|
1248
|
+
,.http_major= 1
|
1249
|
+
,.http_minor= 0
|
1250
|
+
,.status_code= 204
|
1251
|
+
,.num_headers= 1
|
1252
|
+
,.headers=
|
1253
|
+
{ { "Connection", "keep-alive" }
|
1254
|
+
}
|
1255
|
+
,.body_size= 0
|
1256
|
+
,.body= ""
|
1257
|
+
}
|
1258
|
+
|
1259
|
+
#define NO_BODY_HTTP11_KA_200 15
|
1260
|
+
, {.name= "HTTP/1.1 with an EOF-terminated 200 status"
|
1261
|
+
,.type= HTTP_RESPONSE
|
1262
|
+
,.raw= "HTTP/1.1 200 OK\r\n"
|
1263
|
+
"\r\n"
|
1264
|
+
,.should_keep_alive= FALSE
|
1265
|
+
,.message_complete_on_eof= TRUE
|
1266
|
+
,.http_major= 1
|
1267
|
+
,.http_minor= 1
|
1268
|
+
,.status_code= 200
|
1269
|
+
,.num_headers= 0
|
1270
|
+
,.headers={}
|
1271
|
+
,.body_size= 0
|
1272
|
+
,.body= ""
|
1273
|
+
}
|
1274
|
+
|
1275
|
+
#define NO_BODY_HTTP11_KA_204 16
|
1276
|
+
, {.name= "HTTP/1.1 with a 204 status"
|
1277
|
+
,.type= HTTP_RESPONSE
|
1278
|
+
,.raw= "HTTP/1.1 204 No content\r\n"
|
1279
|
+
"\r\n"
|
1280
|
+
,.should_keep_alive= TRUE
|
1281
|
+
,.message_complete_on_eof= FALSE
|
1282
|
+
,.http_major= 1
|
1283
|
+
,.http_minor= 1
|
1284
|
+
,.status_code= 204
|
1285
|
+
,.num_headers= 0
|
1286
|
+
,.headers={}
|
1287
|
+
,.body_size= 0
|
1288
|
+
,.body= ""
|
1289
|
+
}
|
1290
|
+
|
1291
|
+
#define NO_BODY_HTTP11_NOKA_204 17
|
1292
|
+
, {.name= "HTTP/1.1 with a 204 status and keep-alive disabled"
|
1293
|
+
,.type= HTTP_RESPONSE
|
1294
|
+
,.raw= "HTTP/1.1 204 No content\r\n"
|
1295
|
+
"Connection: close\r\n"
|
1296
|
+
"\r\n"
|
1297
|
+
,.should_keep_alive= FALSE
|
1298
|
+
,.message_complete_on_eof= FALSE
|
1299
|
+
,.http_major= 1
|
1300
|
+
,.http_minor= 1
|
1301
|
+
,.status_code= 204
|
1302
|
+
,.num_headers= 1
|
1303
|
+
,.headers=
|
1304
|
+
{ { "Connection", "close" }
|
1305
|
+
}
|
1306
|
+
,.body_size= 0
|
1307
|
+
,.body= ""
|
1308
|
+
}
|
1309
|
+
|
1310
|
+
#define NO_BODY_HTTP11_KA_CHUNKED_200 18
|
1311
|
+
, {.name= "HTTP/1.1 with chunked endocing and a 200 response"
|
1312
|
+
,.type= HTTP_RESPONSE
|
1313
|
+
,.raw= "HTTP/1.1 200 OK\r\n"
|
1314
|
+
"Transfer-Encoding: chunked\r\n"
|
1315
|
+
"\r\n"
|
1316
|
+
"0\r\n"
|
1317
|
+
"\r\n"
|
1318
|
+
,.should_keep_alive= TRUE
|
1319
|
+
,.message_complete_on_eof= FALSE
|
1320
|
+
,.http_major= 1
|
1321
|
+
,.http_minor= 1
|
1322
|
+
,.status_code= 200
|
1323
|
+
,.num_headers= 1
|
1324
|
+
,.headers=
|
1325
|
+
{ { "Transfer-Encoding", "chunked" }
|
1326
|
+
}
|
1327
|
+
,.body_size= 0
|
1328
|
+
,.body= ""
|
1329
|
+
}
|
1330
|
+
|
1331
|
+
#if !HTTP_PARSER_STRICT
|
1332
|
+
#define SPACE_IN_FIELD_RES 19
|
1333
|
+
/* Should handle spaces in header fields */
|
1334
|
+
, {.name= "field space"
|
1335
|
+
,.type= HTTP_RESPONSE
|
1336
|
+
,.raw= "HTTP/1.1 200 OK\r\n"
|
1337
|
+
"Server: Microsoft-IIS/6.0\r\n"
|
1338
|
+
"X-Powered-By: ASP.NET\r\n"
|
1339
|
+
"en-US Content-Type: text/xml\r\n" /* this is the problem */
|
1340
|
+
"Content-Type: text/xml\r\n"
|
1341
|
+
"Content-Length: 16\r\n"
|
1342
|
+
"Date: Fri, 23 Jul 2010 18:45:38 GMT\r\n"
|
1343
|
+
"Connection: keep-alive\r\n"
|
1344
|
+
"\r\n"
|
1345
|
+
"<xml>hello</xml>" /* fake body */
|
1346
|
+
,.should_keep_alive= TRUE
|
1347
|
+
,.message_complete_on_eof= FALSE
|
1348
|
+
,.http_major= 1
|
1349
|
+
,.http_minor= 1
|
1350
|
+
,.status_code= 200
|
1351
|
+
,.num_headers= 7
|
1352
|
+
,.headers=
|
1353
|
+
{ { "Server", "Microsoft-IIS/6.0" }
|
1354
|
+
, { "X-Powered-By", "ASP.NET" }
|
1355
|
+
, { "en-US Content-Type", "text/xml" }
|
1356
|
+
, { "Content-Type", "text/xml" }
|
1357
|
+
, { "Content-Length", "16" }
|
1358
|
+
, { "Date", "Fri, 23 Jul 2010 18:45:38 GMT" }
|
1359
|
+
, { "Connection", "keep-alive" }
|
1360
|
+
}
|
1361
|
+
,.body= "<xml>hello</xml>"
|
1022
1362
|
}
|
1023
|
-
|
1363
|
+
#endif /* !HTTP_PARSER_STRICT */
|
1024
1364
|
|
1025
1365
|
, {.name= NULL } /* sentinel */
|
1026
1366
|
};
|
1027
1367
|
|
1028
|
-
int
|
1029
|
-
request_path_cb (http_parser *p, const char *buf, size_t len)
|
1030
|
-
{
|
1031
|
-
assert(p == parser);
|
1032
|
-
strncat(messages[num_messages].request_path, buf, len);
|
1033
|
-
return 0;
|
1034
|
-
}
|
1035
|
-
|
1036
1368
|
int
|
1037
1369
|
request_url_cb (http_parser *p, const char *buf, size_t len)
|
1038
1370
|
{
|
@@ -1041,22 +1373,6 @@ request_url_cb (http_parser *p, const char *buf, size_t len)
|
|
1041
1373
|
return 0;
|
1042
1374
|
}
|
1043
1375
|
|
1044
|
-
int
|
1045
|
-
query_string_cb (http_parser *p, const char *buf, size_t len)
|
1046
|
-
{
|
1047
|
-
assert(p == parser);
|
1048
|
-
strncat(messages[num_messages].query_string, buf, len);
|
1049
|
-
return 0;
|
1050
|
-
}
|
1051
|
-
|
1052
|
-
int
|
1053
|
-
fragment_cb (http_parser *p, const char *buf, size_t len)
|
1054
|
-
{
|
1055
|
-
assert(p == parser);
|
1056
|
-
strncat(messages[num_messages].fragment, buf, len);
|
1057
|
-
return 0;
|
1058
|
-
}
|
1059
|
-
|
1060
1376
|
int
|
1061
1377
|
header_field_cb (http_parser *p, const char *buf, size_t len)
|
1062
1378
|
{
|
@@ -1146,14 +1462,151 @@ message_complete_cb (http_parser *p)
|
|
1146
1462
|
return 0;
|
1147
1463
|
}
|
1148
1464
|
|
1465
|
+
/* These dontcall_* callbacks exist so that we can verify that when we're
|
1466
|
+
* paused, no additional callbacks are invoked */
|
1467
|
+
int
|
1468
|
+
dontcall_message_begin_cb (http_parser *p)
|
1469
|
+
{
|
1470
|
+
if (p) { } // gcc
|
1471
|
+
fprintf(stderr, "\n\n*** on_message_begin() called on paused parser ***\n\n");
|
1472
|
+
exit(1);
|
1473
|
+
}
|
1474
|
+
|
1475
|
+
int
|
1476
|
+
dontcall_header_field_cb (http_parser *p, const char *buf, size_t len)
|
1477
|
+
{
|
1478
|
+
if (p || buf || len) { } // gcc
|
1479
|
+
fprintf(stderr, "\n\n*** on_header_field() called on paused parser ***\n\n");
|
1480
|
+
exit(1);
|
1481
|
+
}
|
1482
|
+
|
1483
|
+
int
|
1484
|
+
dontcall_header_value_cb (http_parser *p, const char *buf, size_t len)
|
1485
|
+
{
|
1486
|
+
if (p || buf || len) { } // gcc
|
1487
|
+
fprintf(stderr, "\n\n*** on_header_value() called on paused parser ***\n\n");
|
1488
|
+
exit(1);
|
1489
|
+
}
|
1490
|
+
|
1491
|
+
int
|
1492
|
+
dontcall_request_url_cb (http_parser *p, const char *buf, size_t len)
|
1493
|
+
{
|
1494
|
+
if (p || buf || len) { } // gcc
|
1495
|
+
fprintf(stderr, "\n\n*** on_request_url() called on paused parser ***\n\n");
|
1496
|
+
exit(1);
|
1497
|
+
}
|
1498
|
+
|
1499
|
+
int
|
1500
|
+
dontcall_body_cb (http_parser *p, const char *buf, size_t len)
|
1501
|
+
{
|
1502
|
+
if (p || buf || len) { } // gcc
|
1503
|
+
fprintf(stderr, "\n\n*** on_body_cb() called on paused parser ***\n\n");
|
1504
|
+
exit(1);
|
1505
|
+
}
|
1506
|
+
|
1507
|
+
int
|
1508
|
+
dontcall_headers_complete_cb (http_parser *p)
|
1509
|
+
{
|
1510
|
+
if (p) { } // gcc
|
1511
|
+
fprintf(stderr, "\n\n*** on_headers_complete() called on paused "
|
1512
|
+
"parser ***\n\n");
|
1513
|
+
exit(1);
|
1514
|
+
}
|
1515
|
+
|
1516
|
+
int
|
1517
|
+
dontcall_message_complete_cb (http_parser *p)
|
1518
|
+
{
|
1519
|
+
if (p) { } // gcc
|
1520
|
+
fprintf(stderr, "\n\n*** on_message_complete() called on paused "
|
1521
|
+
"parser ***\n\n");
|
1522
|
+
exit(1);
|
1523
|
+
}
|
1524
|
+
|
1525
|
+
static http_parser_settings settings_dontcall =
|
1526
|
+
{.on_message_begin = dontcall_message_begin_cb
|
1527
|
+
,.on_header_field = dontcall_header_field_cb
|
1528
|
+
,.on_header_value = dontcall_header_value_cb
|
1529
|
+
,.on_url = dontcall_request_url_cb
|
1530
|
+
,.on_body = dontcall_body_cb
|
1531
|
+
,.on_headers_complete = dontcall_headers_complete_cb
|
1532
|
+
,.on_message_complete = dontcall_message_complete_cb
|
1533
|
+
};
|
1534
|
+
|
1535
|
+
/* These pause_* callbacks always pause the parser and just invoke the regular
|
1536
|
+
* callback that tracks content. Before returning, we overwrite the parser
|
1537
|
+
* settings to point to the _dontcall variety so that we can verify that
|
1538
|
+
* the pause actually did, you know, pause. */
|
1539
|
+
int
|
1540
|
+
pause_message_begin_cb (http_parser *p)
|
1541
|
+
{
|
1542
|
+
http_parser_pause(p, 1);
|
1543
|
+
*current_pause_parser = settings_dontcall;
|
1544
|
+
return message_begin_cb(p);
|
1545
|
+
}
|
1546
|
+
|
1547
|
+
int
|
1548
|
+
pause_header_field_cb (http_parser *p, const char *buf, size_t len)
|
1549
|
+
{
|
1550
|
+
http_parser_pause(p, 1);
|
1551
|
+
*current_pause_parser = settings_dontcall;
|
1552
|
+
return header_field_cb(p, buf, len);
|
1553
|
+
}
|
1554
|
+
|
1555
|
+
int
|
1556
|
+
pause_header_value_cb (http_parser *p, const char *buf, size_t len)
|
1557
|
+
{
|
1558
|
+
http_parser_pause(p, 1);
|
1559
|
+
*current_pause_parser = settings_dontcall;
|
1560
|
+
return header_value_cb(p, buf, len);
|
1561
|
+
}
|
1562
|
+
|
1563
|
+
int
|
1564
|
+
pause_request_url_cb (http_parser *p, const char *buf, size_t len)
|
1565
|
+
{
|
1566
|
+
http_parser_pause(p, 1);
|
1567
|
+
*current_pause_parser = settings_dontcall;
|
1568
|
+
return request_url_cb(p, buf, len);
|
1569
|
+
}
|
1570
|
+
|
1571
|
+
int
|
1572
|
+
pause_body_cb (http_parser *p, const char *buf, size_t len)
|
1573
|
+
{
|
1574
|
+
http_parser_pause(p, 1);
|
1575
|
+
*current_pause_parser = settings_dontcall;
|
1576
|
+
return body_cb(p, buf, len);
|
1577
|
+
}
|
1578
|
+
|
1579
|
+
int
|
1580
|
+
pause_headers_complete_cb (http_parser *p)
|
1581
|
+
{
|
1582
|
+
http_parser_pause(p, 1);
|
1583
|
+
*current_pause_parser = settings_dontcall;
|
1584
|
+
return headers_complete_cb(p);
|
1585
|
+
}
|
1586
|
+
|
1587
|
+
int
|
1588
|
+
pause_message_complete_cb (http_parser *p)
|
1589
|
+
{
|
1590
|
+
http_parser_pause(p, 1);
|
1591
|
+
*current_pause_parser = settings_dontcall;
|
1592
|
+
return message_complete_cb(p);
|
1593
|
+
}
|
1594
|
+
|
1595
|
+
static http_parser_settings settings_pause =
|
1596
|
+
{.on_message_begin = pause_message_begin_cb
|
1597
|
+
,.on_header_field = pause_header_field_cb
|
1598
|
+
,.on_header_value = pause_header_value_cb
|
1599
|
+
,.on_url = pause_request_url_cb
|
1600
|
+
,.on_body = pause_body_cb
|
1601
|
+
,.on_headers_complete = pause_headers_complete_cb
|
1602
|
+
,.on_message_complete = pause_message_complete_cb
|
1603
|
+
};
|
1604
|
+
|
1149
1605
|
static http_parser_settings settings =
|
1150
1606
|
{.on_message_begin = message_begin_cb
|
1151
1607
|
,.on_header_field = header_field_cb
|
1152
1608
|
,.on_header_value = header_value_cb
|
1153
|
-
,.on_path = request_path_cb
|
1154
1609
|
,.on_url = request_url_cb
|
1155
|
-
,.on_fragment = fragment_cb
|
1156
|
-
,.on_query_string = query_string_cb
|
1157
1610
|
,.on_body = body_cb
|
1158
1611
|
,.on_headers_complete = headers_complete_cb
|
1159
1612
|
,.on_message_complete = message_complete_cb
|
@@ -1163,10 +1616,7 @@ static http_parser_settings settings_count_body =
|
|
1163
1616
|
{.on_message_begin = message_begin_cb
|
1164
1617
|
,.on_header_field = header_field_cb
|
1165
1618
|
,.on_header_value = header_value_cb
|
1166
|
-
,.on_path = request_path_cb
|
1167
1619
|
,.on_url = request_url_cb
|
1168
|
-
,.on_fragment = fragment_cb
|
1169
|
-
,.on_query_string = query_string_cb
|
1170
1620
|
,.on_body = count_body_cb
|
1171
1621
|
,.on_headers_complete = headers_complete_cb
|
1172
1622
|
,.on_message_complete = message_complete_cb
|
@@ -1176,10 +1626,7 @@ static http_parser_settings settings_null =
|
|
1176
1626
|
{.on_message_begin = 0
|
1177
1627
|
,.on_header_field = 0
|
1178
1628
|
,.on_header_value = 0
|
1179
|
-
,.on_path = 0
|
1180
1629
|
,.on_url = 0
|
1181
|
-
,.on_fragment = 0
|
1182
|
-
,.on_query_string = 0
|
1183
1630
|
,.on_body = 0
|
1184
1631
|
,.on_headers_complete = 0
|
1185
1632
|
,.on_message_complete = 0
|
@@ -1224,12 +1671,29 @@ size_t parse_count_body (const char *buf, size_t len)
|
|
1224
1671
|
return nparsed;
|
1225
1672
|
}
|
1226
1673
|
|
1674
|
+
size_t parse_pause (const char *buf, size_t len)
|
1675
|
+
{
|
1676
|
+
size_t nparsed;
|
1677
|
+
http_parser_settings s = settings_pause;
|
1678
|
+
|
1679
|
+
currently_parsing_eof = (len == 0);
|
1680
|
+
current_pause_parser = &s;
|
1681
|
+
nparsed = http_parser_execute(parser, current_pause_parser, buf, len);
|
1682
|
+
return nparsed;
|
1683
|
+
}
|
1684
|
+
|
1227
1685
|
static inline int
|
1228
1686
|
check_str_eq (const struct message *m,
|
1229
1687
|
const char *prop,
|
1230
1688
|
const char *expected,
|
1231
1689
|
const char *found) {
|
1232
|
-
if (
|
1690
|
+
if ((expected == NULL) != (found == NULL)) {
|
1691
|
+
printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
|
1692
|
+
printf("expected %s\n", (expected == NULL) ? "NULL" : expected);
|
1693
|
+
printf(" found %s\n", (found == NULL) ? "NULL" : found);
|
1694
|
+
return 0;
|
1695
|
+
}
|
1696
|
+
if (expected != NULL && 0 != strcmp(expected, found)) {
|
1233
1697
|
printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
|
1234
1698
|
printf("expected '%s'\n", expected);
|
1235
1699
|
printf(" found '%s'\n", found);
|
@@ -1258,6 +1722,20 @@ check_num_eq (const struct message *m,
|
|
1258
1722
|
#define MESSAGE_CHECK_NUM_EQ(expected, found, prop) \
|
1259
1723
|
if (!check_num_eq(expected, #prop, expected->prop, found->prop)) return 0
|
1260
1724
|
|
1725
|
+
#define MESSAGE_CHECK_URL_EQ(u, expected, found, prop, fn) \
|
1726
|
+
do { \
|
1727
|
+
char ubuf[256]; \
|
1728
|
+
\
|
1729
|
+
if ((u)->field_set & (1 << (fn))) { \
|
1730
|
+
memcpy(ubuf, (found)->request_url + (u)->field_data[(fn)].off, \
|
1731
|
+
(u)->field_data[(fn)].len); \
|
1732
|
+
ubuf[(u)->field_data[(fn)].len] = '\0'; \
|
1733
|
+
} else { \
|
1734
|
+
ubuf[0] = '\0'; \
|
1735
|
+
} \
|
1736
|
+
\
|
1737
|
+
check_str_eq(expected, #prop, expected->prop, ubuf); \
|
1738
|
+
} while(0)
|
1261
1739
|
|
1262
1740
|
int
|
1263
1741
|
message_eq (int index, const struct message *expected)
|
@@ -1282,10 +1760,29 @@ message_eq (int index, const struct message *expected)
|
|
1282
1760
|
assert(m->message_complete_cb_called);
|
1283
1761
|
|
1284
1762
|
|
1285
|
-
MESSAGE_CHECK_STR_EQ(expected, m, request_path);
|
1286
|
-
MESSAGE_CHECK_STR_EQ(expected, m, query_string);
|
1287
|
-
MESSAGE_CHECK_STR_EQ(expected, m, fragment);
|
1288
1763
|
MESSAGE_CHECK_STR_EQ(expected, m, request_url);
|
1764
|
+
|
1765
|
+
/* Check URL components; we can't do this w/ CONNECT since it doesn't
|
1766
|
+
* send us a well-formed URL.
|
1767
|
+
*/
|
1768
|
+
if (*m->request_url && m->method != HTTP_CONNECT) {
|
1769
|
+
struct http_parser_url u;
|
1770
|
+
|
1771
|
+
if (http_parser_parse_url(m->request_url, strlen(m->request_url), 0, &u)) {
|
1772
|
+
fprintf(stderr, "\n\n*** failed to parse URL %s ***\n\n",
|
1773
|
+
m->request_url);
|
1774
|
+
exit(1);
|
1775
|
+
}
|
1776
|
+
|
1777
|
+
m->port = (u.field_set & (1 << UF_PORT)) ?
|
1778
|
+
u.port : 0;
|
1779
|
+
|
1780
|
+
MESSAGE_CHECK_URL_EQ(&u, expected, m, query_string, UF_QUERY);
|
1781
|
+
MESSAGE_CHECK_URL_EQ(&u, expected, m, fragment, UF_FRAGMENT);
|
1782
|
+
MESSAGE_CHECK_URL_EQ(&u, expected, m, request_path, UF_PATH);
|
1783
|
+
MESSAGE_CHECK_NUM_EQ(expected, m, port);
|
1784
|
+
}
|
1785
|
+
|
1289
1786
|
if (expected->body_size) {
|
1290
1787
|
MESSAGE_CHECK_NUM_EQ(expected, m, body_size);
|
1291
1788
|
} else {
|
@@ -1302,13 +1799,81 @@ message_eq (int index, const struct message *expected)
|
|
1302
1799
|
if (!r) return 0;
|
1303
1800
|
}
|
1304
1801
|
|
1802
|
+
MESSAGE_CHECK_STR_EQ(expected, m, upgrade);
|
1803
|
+
|
1305
1804
|
return 1;
|
1306
1805
|
}
|
1307
1806
|
|
1807
|
+
/* Given a sequence of varargs messages, return the number of them that the
|
1808
|
+
* parser should successfully parse, taking into account that upgraded
|
1809
|
+
* messages prevent all subsequent messages from being parsed.
|
1810
|
+
*/
|
1811
|
+
size_t
|
1812
|
+
count_parsed_messages(const size_t nmsgs, ...) {
|
1813
|
+
size_t i;
|
1814
|
+
va_list ap;
|
1815
|
+
|
1816
|
+
va_start(ap, nmsgs);
|
1817
|
+
|
1818
|
+
for (i = 0; i < nmsgs; i++) {
|
1819
|
+
struct message *m = va_arg(ap, struct message *);
|
1820
|
+
|
1821
|
+
if (m->upgrade) {
|
1822
|
+
va_end(ap);
|
1823
|
+
return i + 1;
|
1824
|
+
}
|
1825
|
+
}
|
1826
|
+
|
1827
|
+
va_end(ap);
|
1828
|
+
return nmsgs;
|
1829
|
+
}
|
1830
|
+
|
1831
|
+
/* Given a sequence of bytes and the number of these that we were able to
|
1832
|
+
* parse, verify that upgrade bodies are correct.
|
1833
|
+
*/
|
1834
|
+
void
|
1835
|
+
upgrade_message_fix(char *body, const size_t nread, const size_t nmsgs, ...) {
|
1836
|
+
va_list ap;
|
1837
|
+
size_t i;
|
1838
|
+
size_t off = 0;
|
1839
|
+
|
1840
|
+
va_start(ap, nmsgs);
|
1841
|
+
|
1842
|
+
for (i = 0; i < nmsgs; i++) {
|
1843
|
+
struct message *m = va_arg(ap, struct message *);
|
1844
|
+
|
1845
|
+
off += strlen(m->raw);
|
1846
|
+
|
1847
|
+
if (m->upgrade) {
|
1848
|
+
off -= strlen(m->upgrade);
|
1849
|
+
|
1850
|
+
/* Check the portion of the response after its specified upgrade */
|
1851
|
+
if (!check_str_eq(m, "upgrade", body + off, body + nread)) {
|
1852
|
+
exit(1);
|
1853
|
+
}
|
1854
|
+
|
1855
|
+
/* Fix up the response so that message_eq() will verify the beginning
|
1856
|
+
* of the upgrade */
|
1857
|
+
*(body + nread + strlen(m->upgrade)) = '\0';
|
1858
|
+
messages[num_messages -1 ].upgrade = body + nread;
|
1859
|
+
|
1860
|
+
va_end(ap);
|
1861
|
+
return;
|
1862
|
+
}
|
1863
|
+
}
|
1864
|
+
|
1865
|
+
va_end(ap);
|
1866
|
+
printf("\n\n*** Error: expected a message with upgrade ***\n");
|
1867
|
+
|
1868
|
+
exit(1);
|
1869
|
+
}
|
1870
|
+
|
1308
1871
|
static void
|
1309
1872
|
print_error (const char *raw, size_t error_location)
|
1310
1873
|
{
|
1311
|
-
fprintf(stderr, "\n***
|
1874
|
+
fprintf(stderr, "\n*** %s:%d -- %s ***\n\n",
|
1875
|
+
"http_parser.c", HTTP_PARSER_ERRNO_LINE(parser),
|
1876
|
+
http_errno_description(HTTP_PARSER_ERRNO(parser)));
|
1312
1877
|
|
1313
1878
|
int this_line = 0, char_len = 0;
|
1314
1879
|
size_t i, j, len = strlen(raw), error_location_line = 0;
|
@@ -1346,6 +1911,218 @@ print_error (const char *raw, size_t error_location)
|
|
1346
1911
|
fprintf(stderr, "^\n\nerror location: %u\n", (unsigned int)error_location);
|
1347
1912
|
}
|
1348
1913
|
|
1914
|
+
void
|
1915
|
+
test_preserve_data (void)
|
1916
|
+
{
|
1917
|
+
char my_data[] = "application-specific data";
|
1918
|
+
http_parser parser;
|
1919
|
+
parser.data = my_data;
|
1920
|
+
http_parser_init(&parser, HTTP_REQUEST);
|
1921
|
+
if (parser.data != my_data) {
|
1922
|
+
printf("\n*** parser.data not preserved accross http_parser_init ***\n\n");
|
1923
|
+
exit(1);
|
1924
|
+
}
|
1925
|
+
}
|
1926
|
+
|
1927
|
+
struct url_test {
|
1928
|
+
const char *name;
|
1929
|
+
const char *url;
|
1930
|
+
int is_connect;
|
1931
|
+
struct http_parser_url u;
|
1932
|
+
int rv;
|
1933
|
+
};
|
1934
|
+
|
1935
|
+
const struct url_test url_tests[] =
|
1936
|
+
{ {.name="proxy request"
|
1937
|
+
,.url="http://hostname/"
|
1938
|
+
,.is_connect=0
|
1939
|
+
,.u=
|
1940
|
+
{.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
|
1941
|
+
,.port=0
|
1942
|
+
,.field_data=
|
1943
|
+
{{ 0, 4 } /* UF_SCHEMA */
|
1944
|
+
,{ 7, 8 } /* UF_HOST */
|
1945
|
+
,{ 0, 0 } /* UF_PORT */
|
1946
|
+
,{ 15, 1 } /* UF_PATH */
|
1947
|
+
,{ 0, 0 } /* UF_QUERY */
|
1948
|
+
,{ 0, 0 } /* UF_FRAGMENT */
|
1949
|
+
}
|
1950
|
+
}
|
1951
|
+
,.rv=0
|
1952
|
+
}
|
1953
|
+
|
1954
|
+
, {.name="CONNECT request"
|
1955
|
+
,.url="hostname:443"
|
1956
|
+
,.is_connect=1
|
1957
|
+
,.u=
|
1958
|
+
{.field_set=(1 << UF_HOST) | (1 << UF_PORT)
|
1959
|
+
,.port=443
|
1960
|
+
,.field_data=
|
1961
|
+
{{ 0, 0 } /* UF_SCHEMA */
|
1962
|
+
,{ 0, 8 } /* UF_HOST */
|
1963
|
+
,{ 9, 3 } /* UF_PORT */
|
1964
|
+
,{ 0, 0 } /* UF_PATH */
|
1965
|
+
,{ 0, 0 } /* UF_QUERY */
|
1966
|
+
,{ 0, 0 } /* UF_FRAGMENT */
|
1967
|
+
}
|
1968
|
+
}
|
1969
|
+
,.rv=0
|
1970
|
+
}
|
1971
|
+
|
1972
|
+
, {.name="proxy ipv6 request"
|
1973
|
+
,.url="http://[1:2::3:4]/"
|
1974
|
+
,.is_connect=0
|
1975
|
+
,.u=
|
1976
|
+
{.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
|
1977
|
+
,.port=0
|
1978
|
+
,.field_data=
|
1979
|
+
{{ 0, 4 } /* UF_SCHEMA */
|
1980
|
+
,{ 8, 8 } /* UF_HOST */
|
1981
|
+
,{ 0, 0 } /* UF_PORT */
|
1982
|
+
,{ 17, 1 } /* UF_PATH */
|
1983
|
+
,{ 0, 0 } /* UF_QUERY */
|
1984
|
+
,{ 0, 0 } /* UF_FRAGMENT */
|
1985
|
+
}
|
1986
|
+
}
|
1987
|
+
,.rv=0
|
1988
|
+
}
|
1989
|
+
|
1990
|
+
, {.name="CONNECT ipv6 address"
|
1991
|
+
,.url="[1:2::3:4]:443"
|
1992
|
+
,.is_connect=1
|
1993
|
+
,.u=
|
1994
|
+
{.field_set=(1 << UF_HOST) | (1 << UF_PORT)
|
1995
|
+
,.port=443
|
1996
|
+
,.field_data=
|
1997
|
+
{{ 0, 0 } /* UF_SCHEMA */
|
1998
|
+
,{ 1, 8 } /* UF_HOST */
|
1999
|
+
,{ 11, 3 } /* UF_PORT */
|
2000
|
+
,{ 0, 0 } /* UF_PATH */
|
2001
|
+
,{ 0, 0 } /* UF_QUERY */
|
2002
|
+
,{ 0, 0 } /* UF_FRAGMENT */
|
2003
|
+
}
|
2004
|
+
}
|
2005
|
+
,.rv=0
|
2006
|
+
}
|
2007
|
+
|
2008
|
+
, {.name="extra ? in query string"
|
2009
|
+
,.url="http://a.tbcdn.cn/p/fp/2010c/??fp-header-min.css,fp-base-min.css,fp-channel-min.css,fp-product-min.css,fp-mall-min.css,fp-category-min.css,fp-sub-min.css,fp-gdp4p-min.css,fp-css3-min.css,fp-misc-min.css?t=20101022.css"
|
2010
|
+
,.is_connect=0
|
2011
|
+
,.u=
|
2012
|
+
{.field_set=(1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY)
|
2013
|
+
,.port=0
|
2014
|
+
,.field_data=
|
2015
|
+
{{ 0, 4 } /* UF_SCHEMA */
|
2016
|
+
,{ 7, 10 } /* UF_HOST */
|
2017
|
+
,{ 0, 0 } /* UF_PORT */
|
2018
|
+
,{ 17, 12 } /* UF_PATH */
|
2019
|
+
,{ 30,187 } /* UF_QUERY */
|
2020
|
+
,{ 0, 0 } /* UF_FRAGMENT */
|
2021
|
+
}
|
2022
|
+
}
|
2023
|
+
,.rv=0
|
2024
|
+
}
|
2025
|
+
|
2026
|
+
, {.name="proxy empty host"
|
2027
|
+
,.url="http://:443/"
|
2028
|
+
,.is_connect=0
|
2029
|
+
,.rv=1
|
2030
|
+
}
|
2031
|
+
|
2032
|
+
, {.name="proxy empty port"
|
2033
|
+
,.url="http://hostname:/"
|
2034
|
+
,.is_connect=0
|
2035
|
+
,.rv=1
|
2036
|
+
}
|
2037
|
+
|
2038
|
+
, {.name="CONNECT empty host"
|
2039
|
+
,.url=":443"
|
2040
|
+
,.is_connect=1
|
2041
|
+
,.rv=1
|
2042
|
+
}
|
2043
|
+
|
2044
|
+
, {.name="CONNECT empty port"
|
2045
|
+
,.url="hostname:"
|
2046
|
+
,.is_connect=1
|
2047
|
+
,.rv=1
|
2048
|
+
}
|
2049
|
+
|
2050
|
+
, {.name="CONNECT with extra bits"
|
2051
|
+
,.url="hostname:443/"
|
2052
|
+
,.is_connect=1
|
2053
|
+
,.rv=1
|
2054
|
+
}
|
2055
|
+
};
|
2056
|
+
|
2057
|
+
void
|
2058
|
+
dump_url (const char *url, const struct http_parser_url *u)
|
2059
|
+
{
|
2060
|
+
char part[512];
|
2061
|
+
unsigned int i;
|
2062
|
+
|
2063
|
+
printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port);
|
2064
|
+
for (i = 0; i < UF_MAX; i++) {
|
2065
|
+
if ((u->field_set & (1 << i)) == 0) {
|
2066
|
+
printf("\tfield_data[%u]: unset\n", i);
|
2067
|
+
continue;
|
2068
|
+
}
|
2069
|
+
|
2070
|
+
memcpy(part, url + u->field_data[i].off, u->field_data[i].len);
|
2071
|
+
part[u->field_data[i].len] = '\0';
|
2072
|
+
|
2073
|
+
printf("\tfield_data[%u]: off: %u len: %u part: \"%s\"\n",
|
2074
|
+
i,
|
2075
|
+
u->field_data[i].off,
|
2076
|
+
u->field_data[i].len,
|
2077
|
+
part);
|
2078
|
+
}
|
2079
|
+
}
|
2080
|
+
|
2081
|
+
void
|
2082
|
+
test_parse_url (void)
|
2083
|
+
{
|
2084
|
+
struct http_parser_url u;
|
2085
|
+
const struct url_test *test;
|
2086
|
+
unsigned int i;
|
2087
|
+
int rv;
|
2088
|
+
|
2089
|
+
for (i = 0; i < (sizeof(url_tests) / sizeof(url_tests[0])); i++) {
|
2090
|
+
test = &url_tests[i];
|
2091
|
+
memset(&u, 0, sizeof(u));
|
2092
|
+
|
2093
|
+
rv = http_parser_parse_url(test->url,
|
2094
|
+
strlen(test->url),
|
2095
|
+
test->is_connect,
|
2096
|
+
&u);
|
2097
|
+
|
2098
|
+
if (test->rv == 0) {
|
2099
|
+
if (rv != 0) {
|
2100
|
+
printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
|
2101
|
+
"unexpected rv %d ***\n\n", test->url, test->name, rv);
|
2102
|
+
exit(1);
|
2103
|
+
}
|
2104
|
+
|
2105
|
+
if (memcmp(&u, &test->u, sizeof(u)) != 0) {
|
2106
|
+
printf("\n*** http_parser_parse_url(\"%s\") \"%s\" failed ***\n",
|
2107
|
+
test->url, test->name);
|
2108
|
+
|
2109
|
+
printf("target http_parser_url:\n");
|
2110
|
+
dump_url(test->url, &test->u);
|
2111
|
+
printf("result http_parser_url:\n");
|
2112
|
+
dump_url(test->url, &u);
|
2113
|
+
|
2114
|
+
exit(1);
|
2115
|
+
}
|
2116
|
+
} else {
|
2117
|
+
/* test->rv != 0 */
|
2118
|
+
if (rv == 0) {
|
2119
|
+
printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
|
2120
|
+
"unexpected rv %d ***\n\n", test->url, test->name, rv);
|
2121
|
+
exit(1);
|
2122
|
+
}
|
2123
|
+
}
|
2124
|
+
}
|
2125
|
+
}
|
1349
2126
|
|
1350
2127
|
void
|
1351
2128
|
test_message (const struct message *message)
|
@@ -1363,7 +2140,10 @@ test_message (const struct message *message)
|
|
1363
2140
|
if (msg1len) {
|
1364
2141
|
read = parse(msg1, msg1len);
|
1365
2142
|
|
1366
|
-
if (message->upgrade && parser->upgrade)
|
2143
|
+
if (message->upgrade && parser->upgrade) {
|
2144
|
+
messages[num_messages - 1].upgrade = msg1 + read;
|
2145
|
+
goto test;
|
2146
|
+
}
|
1367
2147
|
|
1368
2148
|
if (read != msg1len) {
|
1369
2149
|
print_error(msg1, read);
|
@@ -1374,7 +2154,10 @@ test_message (const struct message *message)
|
|
1374
2154
|
|
1375
2155
|
read = parse(msg2, msg2len);
|
1376
2156
|
|
1377
|
-
if (message->upgrade && parser->upgrade)
|
2157
|
+
if (message->upgrade && parser->upgrade) {
|
2158
|
+
messages[num_messages - 1].upgrade = msg2 + read;
|
2159
|
+
goto test;
|
2160
|
+
}
|
1378
2161
|
|
1379
2162
|
if (read != msg2len) {
|
1380
2163
|
print_error(msg2, read);
|
@@ -1383,8 +2166,6 @@ test_message (const struct message *message)
|
|
1383
2166
|
|
1384
2167
|
read = parse(NULL, 0);
|
1385
2168
|
|
1386
|
-
if (message->upgrade && parser->upgrade) goto test;
|
1387
|
-
|
1388
2169
|
if (read != 0) {
|
1389
2170
|
print_error(message->raw, read);
|
1390
2171
|
exit(1);
|
@@ -1440,21 +2221,32 @@ test_message_count_body (const struct message *message)
|
|
1440
2221
|
}
|
1441
2222
|
|
1442
2223
|
void
|
1443
|
-
test_simple (const char *buf,
|
2224
|
+
test_simple (const char *buf, enum http_errno err_expected)
|
1444
2225
|
{
|
1445
2226
|
parser_init(HTTP_REQUEST);
|
1446
2227
|
|
1447
2228
|
size_t parsed;
|
1448
2229
|
int pass;
|
2230
|
+
enum http_errno err;
|
2231
|
+
|
1449
2232
|
parsed = parse(buf, strlen(buf));
|
1450
2233
|
pass = (parsed == strlen(buf));
|
2234
|
+
err = HTTP_PARSER_ERRNO(parser);
|
1451
2235
|
parsed = parse(NULL, 0);
|
1452
2236
|
pass &= (parsed == 0);
|
1453
2237
|
|
1454
2238
|
parser_free();
|
1455
2239
|
|
1456
|
-
|
1457
|
-
|
2240
|
+
/* In strict mode, allow us to pass with an unexpected HPE_STRICT as
|
2241
|
+
* long as the caller isn't expecting success.
|
2242
|
+
*/
|
2243
|
+
#if HTTP_PARSER_STRICT
|
2244
|
+
if (err_expected != err && err_expected != HPE_OK && err != HPE_STRICT) {
|
2245
|
+
#else
|
2246
|
+
if (err_expected != err) {
|
2247
|
+
#endif
|
2248
|
+
fprintf(stderr, "\n*** test_simple expected %s, but saw %s ***\n\n%s\n",
|
2249
|
+
http_errno_name(err_expected), http_errno_name(err), buf);
|
1458
2250
|
exit(1);
|
1459
2251
|
}
|
1460
2252
|
}
|
@@ -1471,10 +2263,14 @@ test_header_overflow_error (int req)
|
|
1471
2263
|
assert(parsed == strlen(buf));
|
1472
2264
|
|
1473
2265
|
buf = "header-key: header-value\r\n";
|
2266
|
+
size_t buflen = strlen(buf);
|
2267
|
+
|
1474
2268
|
int i;
|
1475
2269
|
for (i = 0; i < 10000; i++) {
|
1476
|
-
|
2270
|
+
parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
|
2271
|
+
if (parsed != buflen) {
|
1477
2272
|
//fprintf(stderr, "error found on iter %d\n", i);
|
2273
|
+
assert(HTTP_PARSER_ERRNO(&parser) == HPE_HEADER_OVERFLOW);
|
1478
2274
|
return;
|
1479
2275
|
}
|
1480
2276
|
}
|
@@ -1483,6 +2279,53 @@ test_header_overflow_error (int req)
|
|
1483
2279
|
exit(1);
|
1484
2280
|
}
|
1485
2281
|
|
2282
|
+
static void
|
2283
|
+
test_content_length_overflow (const char *buf, size_t buflen, int expect_ok)
|
2284
|
+
{
|
2285
|
+
http_parser parser;
|
2286
|
+
http_parser_init(&parser, HTTP_RESPONSE);
|
2287
|
+
http_parser_execute(&parser, &settings_null, buf, buflen);
|
2288
|
+
|
2289
|
+
if (expect_ok)
|
2290
|
+
assert(HTTP_PARSER_ERRNO(&parser) == HPE_OK);
|
2291
|
+
else
|
2292
|
+
assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_CONTENT_LENGTH);
|
2293
|
+
}
|
2294
|
+
|
2295
|
+
void
|
2296
|
+
test_header_content_length_overflow_error (void)
|
2297
|
+
{
|
2298
|
+
#define X(size) \
|
2299
|
+
"HTTP/1.1 200 OK\r\n" \
|
2300
|
+
"Content-Length: " #size "\r\n" \
|
2301
|
+
"\r\n"
|
2302
|
+
const char a[] = X(18446744073709551614); /* 2^64-2 */
|
2303
|
+
const char b[] = X(18446744073709551615); /* 2^64-1 */
|
2304
|
+
const char c[] = X(18446744073709551616); /* 2^64 */
|
2305
|
+
#undef X
|
2306
|
+
test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok */
|
2307
|
+
test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */
|
2308
|
+
test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */
|
2309
|
+
}
|
2310
|
+
|
2311
|
+
void
|
2312
|
+
test_chunk_content_length_overflow_error (void)
|
2313
|
+
{
|
2314
|
+
#define X(size) \
|
2315
|
+
"HTTP/1.1 200 OK\r\n" \
|
2316
|
+
"Transfer-Encoding: chunked\r\n" \
|
2317
|
+
"\r\n" \
|
2318
|
+
#size "\r\n" \
|
2319
|
+
"..."
|
2320
|
+
const char a[] = X(FFFFFFFFFFFFFFFE); /* 2^64-2 */
|
2321
|
+
const char b[] = X(FFFFFFFFFFFFFFFF); /* 2^64-1 */
|
2322
|
+
const char c[] = X(10000000000000000); /* 2^64 */
|
2323
|
+
#undef X
|
2324
|
+
test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok */
|
2325
|
+
test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */
|
2326
|
+
test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */
|
2327
|
+
}
|
2328
|
+
|
1486
2329
|
void
|
1487
2330
|
test_no_overflow_long_body (int req, size_t length)
|
1488
2331
|
{
|
@@ -1519,12 +2362,7 @@ test_no_overflow_long_body (int req, size_t length)
|
|
1519
2362
|
void
|
1520
2363
|
test_multiple3 (const struct message *r1, const struct message *r2, const struct message *r3)
|
1521
2364
|
{
|
1522
|
-
int message_count =
|
1523
|
-
if (!r1->upgrade) {
|
1524
|
-
message_count++;
|
1525
|
-
if (!r2->upgrade) message_count++;
|
1526
|
-
}
|
1527
|
-
int has_upgrade = (message_count < 3 || r3->upgrade);
|
2365
|
+
int message_count = count_parsed_messages(3, r1, r2, r3);
|
1528
2366
|
|
1529
2367
|
char total[ strlen(r1->raw)
|
1530
2368
|
+ strlen(r2->raw)
|
@@ -1543,7 +2381,10 @@ test_multiple3 (const struct message *r1, const struct message *r2, const struct
|
|
1543
2381
|
|
1544
2382
|
read = parse(total, strlen(total));
|
1545
2383
|
|
1546
|
-
if (
|
2384
|
+
if (parser->upgrade) {
|
2385
|
+
upgrade_message_fix(total, read, 3, r1, r2, r3);
|
2386
|
+
goto test;
|
2387
|
+
}
|
1547
2388
|
|
1548
2389
|
if (read != strlen(total)) {
|
1549
2390
|
print_error(total, read);
|
@@ -1552,8 +2393,6 @@ test_multiple3 (const struct message *r1, const struct message *r2, const struct
|
|
1552
2393
|
|
1553
2394
|
read = parse(NULL, 0);
|
1554
2395
|
|
1555
|
-
if (has_upgrade && parser->upgrade) goto test;
|
1556
|
-
|
1557
2396
|
if (read != 0) {
|
1558
2397
|
print_error(total, read);
|
1559
2398
|
exit(1);
|
@@ -1567,12 +2406,8 @@ test:
|
|
1567
2406
|
}
|
1568
2407
|
|
1569
2408
|
if (!message_eq(0, r1)) exit(1);
|
1570
|
-
if (message_count > 1)
|
1571
|
-
|
1572
|
-
if (message_count > 2) {
|
1573
|
-
if (!message_eq(2, r3)) exit(1);
|
1574
|
-
}
|
1575
|
-
}
|
2409
|
+
if (message_count > 1 && !message_eq(1, r2)) exit(1);
|
2410
|
+
if (message_count > 2 && !message_eq(2, r3)) exit(1);
|
1576
2411
|
|
1577
2412
|
parser_free();
|
1578
2413
|
}
|
@@ -1601,6 +2436,7 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
|
|
1601
2436
|
int ops = 0 ;
|
1602
2437
|
|
1603
2438
|
size_t buf1_len, buf2_len, buf3_len;
|
2439
|
+
int message_count = count_parsed_messages(3, r1, r2, r3);
|
1604
2440
|
|
1605
2441
|
int i,j,type_both;
|
1606
2442
|
for (type_both = 0; type_both < 2; type_both ++ ) {
|
@@ -1629,27 +2465,27 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
|
|
1629
2465
|
|
1630
2466
|
read = parse(buf1, buf1_len);
|
1631
2467
|
|
1632
|
-
if (
|
2468
|
+
if (parser->upgrade) goto test;
|
1633
2469
|
|
1634
2470
|
if (read != buf1_len) {
|
1635
2471
|
print_error(buf1, read);
|
1636
2472
|
goto error;
|
1637
2473
|
}
|
1638
2474
|
|
1639
|
-
read
|
2475
|
+
read += parse(buf2, buf2_len);
|
1640
2476
|
|
1641
|
-
if (
|
2477
|
+
if (parser->upgrade) goto test;
|
1642
2478
|
|
1643
|
-
if (read != buf2_len) {
|
2479
|
+
if (read != buf1_len + buf2_len) {
|
1644
2480
|
print_error(buf2, read);
|
1645
2481
|
goto error;
|
1646
2482
|
}
|
1647
2483
|
|
1648
|
-
read
|
2484
|
+
read += parse(buf3, buf3_len);
|
1649
2485
|
|
1650
|
-
if (
|
2486
|
+
if (parser->upgrade) goto test;
|
1651
2487
|
|
1652
|
-
if (read != buf3_len) {
|
2488
|
+
if (read != buf1_len + buf2_len + buf3_len) {
|
1653
2489
|
print_error(buf3, read);
|
1654
2490
|
goto error;
|
1655
2491
|
}
|
@@ -1657,9 +2493,13 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
|
|
1657
2493
|
parse(NULL, 0);
|
1658
2494
|
|
1659
2495
|
test:
|
2496
|
+
if (parser->upgrade) {
|
2497
|
+
upgrade_message_fix(total, read, 3, r1, r2, r3);
|
2498
|
+
}
|
1660
2499
|
|
1661
|
-
if (
|
1662
|
-
fprintf(stderr, "\n\nParser didn't see
|
2500
|
+
if (message_count != num_messages) {
|
2501
|
+
fprintf(stderr, "\n\nParser didn't see %d messages only %d\n",
|
2502
|
+
message_count, num_messages);
|
1663
2503
|
goto error;
|
1664
2504
|
}
|
1665
2505
|
|
@@ -1668,12 +2508,12 @@ test:
|
|
1668
2508
|
goto error;
|
1669
2509
|
}
|
1670
2510
|
|
1671
|
-
if (!message_eq(1, r2)) {
|
2511
|
+
if (message_count > 1 && !message_eq(1, r2)) {
|
1672
2512
|
fprintf(stderr, "\n\nError matching messages[1] in test_scan.\n");
|
1673
2513
|
goto error;
|
1674
2514
|
}
|
1675
2515
|
|
1676
|
-
if (!message_eq(2, r3)) {
|
2516
|
+
if (message_count > 2 && !message_eq(2, r3)) {
|
1677
2517
|
fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n");
|
1678
2518
|
goto error;
|
1679
2519
|
}
|
@@ -1724,6 +2564,58 @@ create_large_chunked_message (int body_size_in_kb, const char* headers)
|
|
1724
2564
|
return buf;
|
1725
2565
|
}
|
1726
2566
|
|
2567
|
+
/* Verify that we can pause parsing at any of the bytes in the
|
2568
|
+
* message and still get the result that we're expecting. */
|
2569
|
+
void
|
2570
|
+
test_message_pause (const struct message *msg)
|
2571
|
+
{
|
2572
|
+
char *buf = (char*) msg->raw;
|
2573
|
+
size_t buflen = strlen(msg->raw);
|
2574
|
+
size_t nread;
|
2575
|
+
|
2576
|
+
parser_init(msg->type);
|
2577
|
+
|
2578
|
+
do {
|
2579
|
+
nread = parse_pause(buf, buflen);
|
2580
|
+
|
2581
|
+
// We can only set the upgrade buffer once we've gotten our message
|
2582
|
+
// completion callback.
|
2583
|
+
if (messages[0].message_complete_cb_called &&
|
2584
|
+
msg->upgrade &&
|
2585
|
+
parser->upgrade) {
|
2586
|
+
messages[0].upgrade = buf + nread;
|
2587
|
+
goto test;
|
2588
|
+
}
|
2589
|
+
|
2590
|
+
if (nread < buflen) {
|
2591
|
+
|
2592
|
+
// Not much do to if we failed a strict-mode check
|
2593
|
+
if (HTTP_PARSER_ERRNO(parser) == HPE_STRICT) {
|
2594
|
+
parser_free();
|
2595
|
+
return;
|
2596
|
+
}
|
2597
|
+
|
2598
|
+
assert (HTTP_PARSER_ERRNO(parser) == HPE_PAUSED);
|
2599
|
+
}
|
2600
|
+
|
2601
|
+
buf += nread;
|
2602
|
+
buflen -= nread;
|
2603
|
+
http_parser_pause(parser, 0);
|
2604
|
+
} while (buflen > 0);
|
2605
|
+
|
2606
|
+
nread = parse_pause(NULL, 0);
|
2607
|
+
assert (nread == 0);
|
2608
|
+
|
2609
|
+
test:
|
2610
|
+
if (num_messages != 1) {
|
2611
|
+
printf("\n*** num_messages != 1 after testing '%s' ***\n\n", msg->name);
|
2612
|
+
exit(1);
|
2613
|
+
}
|
2614
|
+
|
2615
|
+
if(!message_eq(0, msg)) exit(1);
|
2616
|
+
|
2617
|
+
parser_free();
|
2618
|
+
}
|
1727
2619
|
|
1728
2620
|
int
|
1729
2621
|
main (void)
|
@@ -1738,6 +2630,10 @@ main (void)
|
|
1738
2630
|
for (request_count = 0; requests[request_count].name; request_count++);
|
1739
2631
|
for (response_count = 0; responses[response_count].name; response_count++);
|
1740
2632
|
|
2633
|
+
//// API
|
2634
|
+
test_preserve_data();
|
2635
|
+
test_parse_url();
|
2636
|
+
|
1741
2637
|
//// OVERFLOW CONDITIONS
|
1742
2638
|
|
1743
2639
|
test_header_overflow_error(HTTP_REQUEST);
|
@@ -1748,12 +2644,19 @@ main (void)
|
|
1748
2644
|
test_no_overflow_long_body(HTTP_RESPONSE, 1000);
|
1749
2645
|
test_no_overflow_long_body(HTTP_RESPONSE, 100000);
|
1750
2646
|
|
2647
|
+
test_header_content_length_overflow_error();
|
2648
|
+
test_chunk_content_length_overflow_error();
|
2649
|
+
|
1751
2650
|
//// RESPONSES
|
1752
2651
|
|
1753
2652
|
for (i = 0; i < response_count; i++) {
|
1754
2653
|
test_message(&responses[i]);
|
1755
2654
|
}
|
1756
2655
|
|
2656
|
+
for (i = 0; i < response_count; i++) {
|
2657
|
+
test_message_pause(&responses[i]);
|
2658
|
+
}
|
2659
|
+
|
1757
2660
|
for (i = 0; i < response_count; i++) {
|
1758
2661
|
if (!responses[i].should_keep_alive) continue;
|
1759
2662
|
for (j = 0; j < response_count; j++) {
|
@@ -1798,7 +2701,7 @@ main (void)
|
|
1798
2701
|
|
1799
2702
|
printf("response scan 1/2 ");
|
1800
2703
|
test_scan( &responses[TRAILING_SPACE_ON_CHUNKED_BODY]
|
1801
|
-
, &responses[
|
2704
|
+
, &responses[NO_BODY_HTTP10_KA_204]
|
1802
2705
|
, &responses[NO_REASON_PHRASE]
|
1803
2706
|
);
|
1804
2707
|
|
@@ -1813,13 +2716,13 @@ main (void)
|
|
1813
2716
|
|
1814
2717
|
/// REQUESTS
|
1815
2718
|
|
1816
|
-
test_simple("hello world",
|
1817
|
-
test_simple("GET / HTP/1.1\r\n\r\n",
|
2719
|
+
test_simple("hello world", HPE_INVALID_METHOD);
|
2720
|
+
test_simple("GET / HTP/1.1\r\n\r\n", HPE_INVALID_VERSION);
|
1818
2721
|
|
1819
2722
|
|
1820
|
-
test_simple("ASDF / HTTP/1.1\r\n\r\n",
|
1821
|
-
test_simple("PROPPATCHA / HTTP/1.1\r\n\r\n",
|
1822
|
-
test_simple("GETA / HTTP/1.1\r\n\r\n",
|
2723
|
+
test_simple("ASDF / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD);
|
2724
|
+
test_simple("PROPPATCHA / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD);
|
2725
|
+
test_simple("GETA / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD);
|
1823
2726
|
|
1824
2727
|
// Well-formed but incomplete
|
1825
2728
|
test_simple("GET / HTTP/1.1\r\n"
|
@@ -1827,7 +2730,7 @@ main (void)
|
|
1827
2730
|
"Content-Length: 6\r\n"
|
1828
2731
|
"\r\n"
|
1829
2732
|
"fooba",
|
1830
|
-
|
2733
|
+
HPE_OK);
|
1831
2734
|
|
1832
2735
|
static const char *all_methods[] = {
|
1833
2736
|
"DELETE",
|
@@ -1845,12 +2748,31 @@ main (void)
|
|
1845
2748
|
"PROPFIND",
|
1846
2749
|
"PROPPATCH",
|
1847
2750
|
"UNLOCK",
|
2751
|
+
"REPORT",
|
2752
|
+
"MKACTIVITY",
|
2753
|
+
"CHECKOUT",
|
2754
|
+
"MERGE",
|
2755
|
+
"M-SEARCH",
|
2756
|
+
"NOTIFY",
|
2757
|
+
"SUBSCRIBE",
|
2758
|
+
"UNSUBSCRIBE",
|
2759
|
+
"PATCH",
|
1848
2760
|
0 };
|
1849
2761
|
const char **this_method;
|
1850
2762
|
for (this_method = all_methods; *this_method; this_method++) {
|
1851
2763
|
char buf[200];
|
1852
2764
|
sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
|
1853
|
-
test_simple(buf,
|
2765
|
+
test_simple(buf, HPE_OK);
|
2766
|
+
}
|
2767
|
+
|
2768
|
+
static const char *bad_methods[] = {
|
2769
|
+
"C******",
|
2770
|
+
"M****",
|
2771
|
+
0 };
|
2772
|
+
for (this_method = bad_methods; *this_method; this_method++) {
|
2773
|
+
char buf[200];
|
2774
|
+
sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
|
2775
|
+
test_simple(buf, HPE_UNKNOWN);
|
1854
2776
|
}
|
1855
2777
|
|
1856
2778
|
const char *dumbfuck2 =
|
@@ -1888,7 +2810,7 @@ main (void)
|
|
1888
2810
|
"\tRA==\r\n"
|
1889
2811
|
"\t-----END CERTIFICATE-----\r\n"
|
1890
2812
|
"\r\n";
|
1891
|
-
test_simple(dumbfuck2,
|
2813
|
+
test_simple(dumbfuck2, HPE_OK);
|
1892
2814
|
|
1893
2815
|
#if 0
|
1894
2816
|
// NOTE(Wed Nov 18 11:57:27 CET 2009) this seems okay. we just read body
|
@@ -1910,7 +2832,9 @@ main (void)
|
|
1910
2832
|
test_message(&requests[i]);
|
1911
2833
|
}
|
1912
2834
|
|
1913
|
-
|
2835
|
+
for (i = 0; i < request_count; i++) {
|
2836
|
+
test_message_pause(&requests[i]);
|
2837
|
+
}
|
1914
2838
|
|
1915
2839
|
for (i = 0; i < request_count; i++) {
|
1916
2840
|
if (!requests[i].should_keep_alive) continue;
|