pico_http_parser 0.0.3 → 0.0.4
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.
- checksums.yaml +4 -4
- data/benchmark/benchmark.rb +13 -5
- data/ext/pico_http_parser/pico_http_parser.c +20 -7
- data/ext/pico_http_parser/picohttpparser/.clang-format +6 -0
- data/ext/pico_http_parser/picohttpparser/Jamfile +7 -0
- data/ext/pico_http_parser/picohttpparser/README.md +92 -0
- data/ext/pico_http_parser/picohttpparser/bench.c +32 -19
- data/ext/pico_http_parser/picohttpparser/picohttpparser.c +509 -342
- data/ext/pico_http_parser/picohttpparser/picohttpparser.h +37 -14
- data/ext/pico_http_parser/picohttpparser/test.c +366 -204
- data/lib/pico_http_parser/version.rb +1 -1
- data/spec/01_simple_spec.rb +0 -3
- metadata +5 -3
@@ -27,6 +27,12 @@
|
|
27
27
|
#ifndef picohttpparser_h
|
28
28
|
#define picohttpparser_h
|
29
29
|
|
30
|
+
#include <sys/types.h>
|
31
|
+
|
32
|
+
#ifdef _MSC_VER
|
33
|
+
#define ssize_t intptr_t
|
34
|
+
#endif
|
35
|
+
|
30
36
|
/* $Id$ */
|
31
37
|
|
32
38
|
#ifdef __cplusplus
|
@@ -36,25 +42,42 @@ extern "C" {
|
|
36
42
|
/* contains name and value of a header (name == NULL if is a continuing line
|
37
43
|
* of a multiline header */
|
38
44
|
struct phr_header {
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
45
|
+
const char *name;
|
46
|
+
size_t name_len;
|
47
|
+
const char *value;
|
48
|
+
size_t value_len;
|
43
49
|
};
|
44
50
|
|
45
|
-
/* returns number of bytes
|
51
|
+
/* returns number of bytes consumed if successful, -2 if request is partial,
|
46
52
|
* -1 if failed */
|
47
|
-
int phr_parse_request(const char*
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
53
|
+
int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len,
|
54
|
+
int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len);
|
55
|
+
|
56
|
+
/* ditto */
|
57
|
+
int phr_parse_response(const char *_buf, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len,
|
58
|
+
struct phr_header *headers, size_t *num_headers, size_t last_len);
|
52
59
|
|
53
60
|
/* ditto */
|
54
|
-
int
|
55
|
-
|
56
|
-
|
57
|
-
|
61
|
+
int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len);
|
62
|
+
|
63
|
+
/* should be zero-filled before start */
|
64
|
+
struct phr_chunked_decoder {
|
65
|
+
size_t bytes_left_in_chunk; /* number of bytes left in current chunk */
|
66
|
+
char consume_trailer; /* if trailing headers should be consumed */
|
67
|
+
char _hex_count;
|
68
|
+
char _state;
|
69
|
+
};
|
70
|
+
|
71
|
+
/* the function rewrites the buffer given as (buf, bufsz) removing the chunked-
|
72
|
+
* encoding headers. When the function returns without an error, bufsz is
|
73
|
+
* updated to the length of the decoded data available. Applications should
|
74
|
+
* repeatedly call the function while it returns -2 (incomplete) every time
|
75
|
+
* supplying newly arrived data. If the end of the chunked-encoded data is
|
76
|
+
* found, the function returns a non-negative number indicating the number of
|
77
|
+
* octets left undecoded at the tail of the supplied buffer. Returns -1 on
|
78
|
+
* error.
|
79
|
+
*/
|
80
|
+
ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *bufsz);
|
58
81
|
|
59
82
|
#ifdef __cplusplus
|
60
83
|
}
|
@@ -26,228 +26,390 @@
|
|
26
26
|
*/
|
27
27
|
|
28
28
|
#include <stdio.h>
|
29
|
+
#include <stdlib.h>
|
29
30
|
#include <string.h>
|
30
31
|
#include "picotest/picotest.h"
|
31
32
|
#include "picohttpparser.h"
|
32
33
|
|
33
|
-
static int bufis(const char*
|
34
|
+
static int bufis(const char *s, size_t l, const char *t)
|
34
35
|
{
|
35
|
-
|
36
|
+
return strlen(t) == l && memcmp(s, t, l) == 0;
|
36
37
|
}
|
37
38
|
|
38
39
|
static void test_request(void)
|
39
40
|
{
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
#define PARSE(s, last_len, exp, comment)
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
PARSE("GET / HTTP/1.0\r\nab: c\033\r\n\r\n", 0, -1, "CTL in header value");
|
140
|
-
PARSE("GET /\xa0 HTTP/1.0\r\nh: c\xa2y\r\n\r\n", 0, 0, "accept MSB chars");
|
141
|
-
ok(num_headers == 1);
|
142
|
-
ok(bufis(method, method_len, "GET"));
|
143
|
-
ok(bufis(path, path_len, "/\xa0"));
|
144
|
-
ok(minor_version == 0);
|
145
|
-
ok(bufis(headers[0].name, headers[0].name_len, "h"));
|
146
|
-
ok(bufis(headers[0].value, headers[0].value_len, "c\xa2y"));
|
41
|
+
const char *method;
|
42
|
+
size_t method_len;
|
43
|
+
const char *path;
|
44
|
+
size_t path_len;
|
45
|
+
int minor_version;
|
46
|
+
struct phr_header headers[4];
|
47
|
+
size_t num_headers;
|
48
|
+
|
49
|
+
#define PARSE(s, last_len, exp, comment) \
|
50
|
+
do { \
|
51
|
+
note(comment); \
|
52
|
+
num_headers = sizeof(headers) / sizeof(headers[0]); \
|
53
|
+
ok(phr_parse_request(s, sizeof(s) - 1, &method, &method_len, &path, &path_len, &minor_version, headers, &num_headers, \
|
54
|
+
last_len) == (exp == 0 ? strlen(s) : exp)); \
|
55
|
+
} while (0)
|
56
|
+
|
57
|
+
PARSE("GET / HTTP/1.0\r\n\r\n", 0, 0, "simple");
|
58
|
+
ok(num_headers == 0);
|
59
|
+
ok(bufis(method, method_len, "GET"));
|
60
|
+
ok(bufis(path, path_len, "/"));
|
61
|
+
ok(minor_version == 0);
|
62
|
+
|
63
|
+
PARSE("GET / HTTP/1.0\r\n\r", 0, -2, "partial");
|
64
|
+
|
65
|
+
PARSE("GET /hoge HTTP/1.1\r\nHost: example.com\r\nCookie: \r\n\r\n", 0, 0, "parse headers");
|
66
|
+
ok(num_headers == 2);
|
67
|
+
ok(bufis(method, method_len, "GET"));
|
68
|
+
ok(bufis(path, path_len, "/hoge"));
|
69
|
+
ok(minor_version == 1);
|
70
|
+
ok(bufis(headers[0].name, headers[0].name_len, "Host"));
|
71
|
+
ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
|
72
|
+
ok(bufis(headers[1].name, headers[1].name_len, "Cookie"));
|
73
|
+
ok(bufis(headers[1].value, headers[1].value_len, ""));
|
74
|
+
|
75
|
+
PARSE("GET /hoge HTTP/1.1\r\nHost: example.com\r\nUser-Agent: \343\201\262\343/1.0\r\n\r\n", 0, 0, "multibyte included");
|
76
|
+
ok(num_headers == 2);
|
77
|
+
ok(bufis(method, method_len, "GET"));
|
78
|
+
ok(bufis(path, path_len, "/hoge"));
|
79
|
+
ok(minor_version == 1);
|
80
|
+
ok(bufis(headers[0].name, headers[0].name_len, "Host"));
|
81
|
+
ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
|
82
|
+
ok(bufis(headers[1].name, headers[1].name_len, "User-Agent"));
|
83
|
+
ok(bufis(headers[1].value, headers[1].value_len, "\343\201\262\343/1.0"));
|
84
|
+
|
85
|
+
PARSE("GET / HTTP/1.0\r\nfoo: \r\nfoo: b\r\n \tc\r\n\r\n", 0, 0, "parse multiline");
|
86
|
+
ok(num_headers == 3);
|
87
|
+
ok(bufis(method, method_len, "GET"));
|
88
|
+
ok(bufis(path, path_len, "/"));
|
89
|
+
ok(minor_version == 0);
|
90
|
+
ok(bufis(headers[0].name, headers[0].name_len, "foo"));
|
91
|
+
ok(bufis(headers[0].value, headers[0].value_len, ""));
|
92
|
+
ok(bufis(headers[1].name, headers[1].name_len, "foo"));
|
93
|
+
ok(bufis(headers[1].value, headers[1].value_len, "b"));
|
94
|
+
ok(headers[2].name == NULL);
|
95
|
+
ok(bufis(headers[2].value, headers[2].value_len, " \tc"));
|
96
|
+
|
97
|
+
PARSE("GET / HTTP/1.0\r\nfoo : ab\r\n\r\n", 0, 0, "parse header name with trailing space");
|
98
|
+
ok(num_headers == 1);
|
99
|
+
ok(bufis(method, method_len, "GET"));
|
100
|
+
ok(bufis(path, path_len, "/"));
|
101
|
+
ok(minor_version == 0);
|
102
|
+
ok(bufis(headers[0].name, headers[0].name_len, "foo "));
|
103
|
+
ok(bufis(headers[0].value, headers[0].value_len, "ab"));
|
104
|
+
|
105
|
+
PARSE("GET", 0, -2, "incomplete 1");
|
106
|
+
ok(method == NULL);
|
107
|
+
PARSE("GET ", 0, -2, "incomplete 2");
|
108
|
+
ok(bufis(method, method_len, "GET"));
|
109
|
+
PARSE("GET /", 0, -2, "incomplete 3");
|
110
|
+
ok(path == NULL);
|
111
|
+
PARSE("GET / ", 0, -2, "incomplete 4");
|
112
|
+
ok(bufis(path, path_len, "/"));
|
113
|
+
PARSE("GET / H", 0, -2, "incomplete 5");
|
114
|
+
PARSE("GET / HTTP/1.", 0, -2, "incomplete 6");
|
115
|
+
PARSE("GET / HTTP/1.0", 0, -2, "incomplete 7");
|
116
|
+
ok(minor_version == -1);
|
117
|
+
PARSE("GET / HTTP/1.0\r", 0, -2, "incomplete 8");
|
118
|
+
ok(minor_version == 0);
|
119
|
+
|
120
|
+
PARSE("GET /hoge HTTP/1.0\r\n\r", strlen("GET /hoge HTTP/1.0\r\n\r") - 1, -2, "slowloris (incomplete)");
|
121
|
+
PARSE("GET /hoge HTTP/1.0\r\n\r\n", strlen("GET /hoge HTTP/1.0\r\n\r\n") - 1, 0, "slowloris (complete)");
|
122
|
+
|
123
|
+
PARSE("GET / HTTP/1.0\r\n:a\r\n\r\n", 0, -1, "empty header name");
|
124
|
+
PARSE("GET / HTTP/1.0\r\n :a\r\n\r\n", 0, -1, "header name (space only)");
|
125
|
+
|
126
|
+
PARSE("G\0T / HTTP/1.0\r\n\r\n", 0, -1, "NUL in method");
|
127
|
+
PARSE("G\tT / HTTP/1.0\r\n\r\n", 0, -1, "tab in method");
|
128
|
+
PARSE("GET /\x7fhello HTTP/1.0\r\n\r\n", 0, -1, "DEL in uri-path");
|
129
|
+
PARSE("GET / HTTP/1.0\r\na\0b: c\r\n\r\n", 0, -1, "NUL in header name");
|
130
|
+
PARSE("GET / HTTP/1.0\r\nab: c\0d\r\n\r\n", 0, -1, "NUL in header value");
|
131
|
+
PARSE("GET / HTTP/1.0\r\na\033b: c\r\n\r\n", 0, -1, "CTL in header name");
|
132
|
+
PARSE("GET / HTTP/1.0\r\nab: c\033\r\n\r\n", 0, -1, "CTL in header value");
|
133
|
+
PARSE("GET /\xa0 HTTP/1.0\r\nh: c\xa2y\r\n\r\n", 0, 0, "accept MSB chars");
|
134
|
+
ok(num_headers == 1);
|
135
|
+
ok(bufis(method, method_len, "GET"));
|
136
|
+
ok(bufis(path, path_len, "/\xa0"));
|
137
|
+
ok(minor_version == 0);
|
138
|
+
ok(bufis(headers[0].name, headers[0].name_len, "h"));
|
139
|
+
ok(bufis(headers[0].value, headers[0].value_len, "c\xa2y"));
|
147
140
|
|
148
141
|
#undef PARSE
|
149
142
|
}
|
150
143
|
|
151
144
|
static void test_response(void)
|
152
145
|
{
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
#define PARSE(s, last_len, exp, comment)
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
146
|
+
int minor_version;
|
147
|
+
int status;
|
148
|
+
const char *msg;
|
149
|
+
size_t msg_len;
|
150
|
+
struct phr_header headers[4];
|
151
|
+
size_t num_headers;
|
152
|
+
|
153
|
+
#define PARSE(s, last_len, exp, comment) \
|
154
|
+
do { \
|
155
|
+
note(comment); \
|
156
|
+
num_headers = sizeof(headers) / sizeof(headers[0]); \
|
157
|
+
ok(phr_parse_response(s, strlen(s), &minor_version, &status, &msg, &msg_len, headers, &num_headers, last_len) == \
|
158
|
+
(exp == 0 ? strlen(s) : exp)); \
|
159
|
+
} while (0)
|
160
|
+
|
161
|
+
PARSE("HTTP/1.0 200 OK\r\n\r\n", 0, 0, "simple");
|
162
|
+
ok(num_headers == 0);
|
163
|
+
ok(status == 200);
|
164
|
+
ok(minor_version = 1);
|
165
|
+
ok(bufis(msg, msg_len, "OK"));
|
166
|
+
|
167
|
+
PARSE("HTTP/1.0 200 OK\r\n\r", 0, -2, "partial");
|
168
|
+
|
169
|
+
PARSE("HTTP/1.1 200 OK\r\nHost: example.com\r\nCookie: \r\n\r\n", 0, 0, "parse headers");
|
170
|
+
ok(num_headers == 2);
|
171
|
+
ok(minor_version == 1);
|
172
|
+
ok(status == 200);
|
173
|
+
ok(bufis(msg, msg_len, "OK"));
|
174
|
+
ok(bufis(headers[0].name, headers[0].name_len, "Host"));
|
175
|
+
ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
|
176
|
+
ok(bufis(headers[1].name, headers[1].name_len, "Cookie"));
|
177
|
+
ok(bufis(headers[1].value, headers[1].value_len, ""));
|
178
|
+
|
179
|
+
PARSE("HTTP/1.0 200 OK\r\nfoo: \r\nfoo: b\r\n \tc\r\n\r\n", 0, 0, "parse multiline");
|
180
|
+
ok(num_headers == 3);
|
181
|
+
ok(minor_version == 0);
|
182
|
+
ok(status == 200);
|
183
|
+
ok(bufis(msg, msg_len, "OK"));
|
184
|
+
ok(bufis(headers[0].name, headers[0].name_len, "foo"));
|
185
|
+
ok(bufis(headers[0].value, headers[0].value_len, ""));
|
186
|
+
ok(bufis(headers[1].name, headers[1].name_len, "foo"));
|
187
|
+
ok(bufis(headers[1].value, headers[1].value_len, "b"));
|
188
|
+
ok(headers[2].name == NULL);
|
189
|
+
ok(bufis(headers[2].value, headers[2].value_len, " \tc"));
|
190
|
+
|
191
|
+
PARSE("HTTP/1.0 500 Internal Server Error\r\n\r\n", 0, 0, "internal server error");
|
192
|
+
ok(num_headers == 0);
|
193
|
+
ok(minor_version == 0);
|
194
|
+
ok(status == 500);
|
195
|
+
ok(bufis(msg, msg_len, "Internal Server Error"));
|
196
|
+
ok(msg_len == sizeof("Internal Server Error") - 1);
|
197
|
+
|
198
|
+
PARSE("H", 0, -2, "incomplete 1");
|
199
|
+
PARSE("HTTP/1.", 0, -2, "incomplete 2");
|
200
|
+
PARSE("HTTP/1.1", 0, -2, "incomplete 3");
|
201
|
+
ok(minor_version == -1);
|
202
|
+
PARSE("HTTP/1.1 ", 0, -2, "incomplete 4");
|
203
|
+
ok(minor_version == 1);
|
204
|
+
PARSE("HTTP/1.1 2", 0, -2, "incomplete 5");
|
205
|
+
PARSE("HTTP/1.1 200", 0, -2, "incomplete 6");
|
206
|
+
ok(status == 0);
|
207
|
+
PARSE("HTTP/1.1 200 ", 0, -2, "incomplete 7");
|
208
|
+
ok(status == 200);
|
209
|
+
PARSE("HTTP/1.1 200 O", 0, -2, "incomplete 8");
|
210
|
+
PARSE("HTTP/1.1 200 OK\r", 0, -2, "incomplete 9");
|
211
|
+
ok(msg == NULL);
|
212
|
+
PARSE("HTTP/1.1 200 OK\r\n", 0, -2, "incomplete 10");
|
213
|
+
ok(bufis(msg, msg_len, "OK"));
|
214
|
+
PARSE("HTTP/1.1 200 OK\n", 0, -2, "incomplete 11");
|
215
|
+
ok(bufis(msg, msg_len, "OK"));
|
216
|
+
|
217
|
+
PARSE("HTTP/1.1 200 OK\r\nA: 1\r", 0, -2, "incomplete 11");
|
218
|
+
ok(num_headers == 0);
|
219
|
+
PARSE("HTTP/1.1 200 OK\r\nA: 1\r\n", 0, -2, "incomplete 12");
|
220
|
+
ok(num_headers == 1);
|
221
|
+
ok(bufis(headers[0].name, headers[0].name_len, "A"));
|
222
|
+
ok(bufis(headers[0].value, headers[0].value_len, "1"));
|
223
|
+
|
224
|
+
PARSE("HTTP/1.0 200 OK\r\n\r", strlen("HTTP/1.0 200 OK\r\n\r") - 1, -2, "slowloris (incomplete)");
|
225
|
+
PARSE("HTTP/1.0 200 OK\r\n\r\n", strlen("HTTP/1.0 200 OK\r\n\r\n") - 1, 0, "slowloris (complete)");
|
226
|
+
|
227
|
+
PARSE("HTTP/1. 200 OK\r\n\r\n", 0, -1, "invalid http version");
|
228
|
+
PARSE("HTTP/1.2z 200 OK\r\n\r\n", 0, -1, "invalid http version 2");
|
229
|
+
PARSE("HTTP/1.1 OK\r\n\r\n", 0, -1, "no status code");
|
230
|
+
|
231
|
+
#undef PARSE
|
232
|
+
}
|
233
|
+
|
234
|
+
static void test_headers(void)
|
235
|
+
{
|
236
|
+
/* only test the interface; the core parser is tested by the tests above */
|
237
|
+
|
238
|
+
struct phr_header headers[4];
|
239
|
+
size_t num_headers;
|
240
|
+
|
241
|
+
#define PARSE(s, last_len, exp, comment) \
|
242
|
+
do { \
|
243
|
+
note(comment); \
|
244
|
+
num_headers = sizeof(headers) / sizeof(headers[0]); \
|
245
|
+
ok(phr_parse_headers(s, strlen(s), headers, &num_headers, last_len) == (exp == 0 ? strlen(s) : exp)); \
|
246
|
+
} while (0)
|
247
|
+
|
248
|
+
PARSE("Host: example.com\r\nCookie: \r\n\r\n", 0, 0, "simple");
|
249
|
+
ok(num_headers == 2);
|
250
|
+
ok(bufis(headers[0].name, headers[0].name_len, "Host"));
|
251
|
+
ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
|
252
|
+
ok(bufis(headers[1].name, headers[1].name_len, "Cookie"));
|
253
|
+
ok(bufis(headers[1].value, headers[1].value_len, ""));
|
254
|
+
|
255
|
+
PARSE("Host: example.com\r\nCookie: \r\n\r\n", 1, 0, "slowloris");
|
256
|
+
ok(num_headers == 2);
|
257
|
+
ok(bufis(headers[0].name, headers[0].name_len, "Host"));
|
258
|
+
ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
|
259
|
+
ok(bufis(headers[1].name, headers[1].name_len, "Cookie"));
|
260
|
+
ok(bufis(headers[1].value, headers[1].value_len, ""));
|
261
|
+
|
262
|
+
PARSE("Host: example.com\r\nCookie: \r\n\r", 0, -2, "partial");
|
263
|
+
|
264
|
+
PARSE("Host: e\7fample.com\r\nCookie: \r\n\r", 0, -1, "error");
|
265
|
+
|
245
266
|
#undef PARSE
|
246
267
|
}
|
247
268
|
|
269
|
+
static void test_chunked_at_once(int line, int consume_trailer, const char *encoded, const char *decoded, ssize_t expected)
|
270
|
+
{
|
271
|
+
struct phr_chunked_decoder dec = {0};
|
272
|
+
char *buf;
|
273
|
+
size_t bufsz;
|
274
|
+
ssize_t ret;
|
275
|
+
|
276
|
+
dec.consume_trailer = consume_trailer;
|
277
|
+
|
278
|
+
note("testing at-once, source at line %d", line);
|
279
|
+
|
280
|
+
buf = strdup(encoded);
|
281
|
+
bufsz = strlen(buf);
|
282
|
+
|
283
|
+
ret = phr_decode_chunked(&dec, buf, &bufsz);
|
284
|
+
|
285
|
+
ok(ret == expected);
|
286
|
+
ok(bufsz == strlen(decoded));
|
287
|
+
ok(bufis(buf, bufsz, decoded));
|
288
|
+
if (expected >= 0) {
|
289
|
+
if (ret == expected)
|
290
|
+
ok(bufis(buf + bufsz, ret, encoded + strlen(encoded) - ret));
|
291
|
+
else
|
292
|
+
ok(0);
|
293
|
+
}
|
294
|
+
|
295
|
+
free(buf);
|
296
|
+
}
|
297
|
+
|
298
|
+
static void test_chunked_per_byte(int line, int consume_trailer, const char *encoded, const char *decoded, ssize_t expected)
|
299
|
+
{
|
300
|
+
struct phr_chunked_decoder dec = {0};
|
301
|
+
char *buf = malloc(strlen(encoded) + 1);
|
302
|
+
size_t bytes_to_consume = strlen(encoded) - (expected >= 0 ? expected : 0), bytes_ready = 0, bufsz, i;
|
303
|
+
ssize_t ret;
|
304
|
+
|
305
|
+
dec.consume_trailer = consume_trailer;
|
306
|
+
|
307
|
+
note("testing per-byte, source at line %d", line);
|
308
|
+
|
309
|
+
for (i = 0; i < bytes_to_consume - 1; ++i) {
|
310
|
+
buf[bytes_ready] = encoded[i];
|
311
|
+
bufsz = 1;
|
312
|
+
ret = phr_decode_chunked(&dec, buf + bytes_ready, &bufsz);
|
313
|
+
if (ret != -2) {
|
314
|
+
ok(0);
|
315
|
+
return;
|
316
|
+
}
|
317
|
+
bytes_ready += bufsz;
|
318
|
+
}
|
319
|
+
strcpy(buf + bytes_ready, encoded + bytes_to_consume - 1);
|
320
|
+
bufsz = strlen(buf + bytes_ready);
|
321
|
+
ret = phr_decode_chunked(&dec, buf + bytes_ready, &bufsz);
|
322
|
+
ok(ret == expected);
|
323
|
+
bytes_ready += bufsz;
|
324
|
+
ok(bytes_ready == strlen(decoded));
|
325
|
+
ok(bufis(buf, bytes_ready, decoded));
|
326
|
+
if (expected >= 0) {
|
327
|
+
if (ret == expected)
|
328
|
+
ok(bufis(buf + bytes_ready, expected, encoded + bytes_to_consume));
|
329
|
+
else
|
330
|
+
ok(0);
|
331
|
+
}
|
332
|
+
|
333
|
+
free(buf);
|
334
|
+
}
|
335
|
+
|
336
|
+
static void test_chunked_failure(int line, const char *encoded, ssize_t expected)
|
337
|
+
{
|
338
|
+
struct phr_chunked_decoder dec = {0};
|
339
|
+
char *buf = strdup(encoded);
|
340
|
+
size_t bufsz, i;
|
341
|
+
ssize_t ret;
|
342
|
+
|
343
|
+
note("testing failure at-once, source at line %d", line);
|
344
|
+
bufsz = strlen(buf);
|
345
|
+
ret = phr_decode_chunked(&dec, buf, &bufsz);
|
346
|
+
ok(ret == expected);
|
347
|
+
|
348
|
+
note("testing failure per-byte, source at line %d", line);
|
349
|
+
memset(&dec, 0, sizeof(dec));
|
350
|
+
for (i = 0; encoded[i] != '\0'; ++i) {
|
351
|
+
buf[0] = encoded[i];
|
352
|
+
bufsz = 1;
|
353
|
+
ret = phr_decode_chunked(&dec, buf, &bufsz);
|
354
|
+
if (ret == -1) {
|
355
|
+
ok(ret == expected);
|
356
|
+
return;
|
357
|
+
} else if (ret == -2) {
|
358
|
+
/* continue */
|
359
|
+
} else {
|
360
|
+
ok(0);
|
361
|
+
return;
|
362
|
+
}
|
363
|
+
}
|
364
|
+
ok(ret == expected);
|
365
|
+
|
366
|
+
free(buf);
|
367
|
+
}
|
368
|
+
|
369
|
+
static void (*chunked_test_runners[])(int, int, const char *, const char *, ssize_t) = {test_chunked_at_once, test_chunked_per_byte,
|
370
|
+
NULL};
|
371
|
+
|
372
|
+
static void test_chunked(void)
|
373
|
+
{
|
374
|
+
size_t i;
|
375
|
+
|
376
|
+
for (i = 0; chunked_test_runners[i] != NULL; ++i) {
|
377
|
+
chunked_test_runners[i](__LINE__, 0, "b\r\nhello world\r\n0\r\n", "hello world", 0);
|
378
|
+
chunked_test_runners[i](__LINE__, 0, "6\r\nhello \r\n5\r\nworld\r\n0\r\n", "hello world", 0);
|
379
|
+
chunked_test_runners[i](__LINE__, 0, "6;comment=hi\r\nhello \r\n5\r\nworld\r\n0\r\n", "hello world", 0);
|
380
|
+
chunked_test_runners[i](__LINE__, 0, "6\r\nhello \r\n5\r\nworld\r\n0\r\na: b\r\nc: d\r\n\r\n", "hello world",
|
381
|
+
sizeof("a: b\r\nc: d\r\n\r\n") - 1);
|
382
|
+
chunked_test_runners[i](__LINE__, 0, "b\r\nhello world\r\n0\r\n", "hello world", 0);
|
383
|
+
}
|
384
|
+
|
385
|
+
note("failures");
|
386
|
+
test_chunked_failure(__LINE__, "z\r\nabcdefg", -1);
|
387
|
+
if (sizeof(size_t) == 8) {
|
388
|
+
test_chunked_failure(__LINE__, "6\r\nhello \r\nffffffffffffffff\r\nabcdefg", -2);
|
389
|
+
test_chunked_failure(__LINE__, "6\r\nhello \r\nfffffffffffffffff\r\nabcdefg", -1);
|
390
|
+
}
|
391
|
+
}
|
392
|
+
|
393
|
+
static void test_chunked_consume_trailer(void)
|
394
|
+
{
|
395
|
+
size_t i;
|
396
|
+
|
397
|
+
for (i = 0; chunked_test_runners[i] != NULL; ++i) {
|
398
|
+
chunked_test_runners[i](__LINE__, 1, "b\r\nhello world\r\n0\r\n", "hello world", -2);
|
399
|
+
chunked_test_runners[i](__LINE__, 1, "6\r\nhello \r\n5\r\nworld\r\n0\r\n", "hello world", -2);
|
400
|
+
chunked_test_runners[i](__LINE__, 1, "6;comment=hi\r\nhello \r\n5\r\nworld\r\n0\r\n", "hello world", -2);
|
401
|
+
chunked_test_runners[i](__LINE__, 1, "b\r\nhello world\r\n0\r\n\r\n", "hello world", 0);
|
402
|
+
chunked_test_runners[i](__LINE__, 1, "b\nhello world\n0\n\n", "hello world", 0);
|
403
|
+
chunked_test_runners[i](__LINE__, 1, "6\r\nhello \r\n5\r\nworld\r\n0\r\na: b\r\nc: d\r\n\r\n", "hello world", 0);
|
404
|
+
}
|
405
|
+
}
|
406
|
+
|
248
407
|
int main(int argc, char **argv)
|
249
408
|
{
|
250
|
-
|
251
|
-
|
252
|
-
|
409
|
+
subtest("request", test_request);
|
410
|
+
subtest("response", test_response);
|
411
|
+
subtest("headers", test_headers);
|
412
|
+
subtest("chunked", test_chunked);
|
413
|
+
subtest("chunked-consume-trailer", test_chunked_consume_trailer);
|
414
|
+
return done_testing();
|
253
415
|
}
|