mizu 0.1.1 → 0.1.2
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/ext/mizu/picohttpparser/.clang-format +7 -0
- data/ext/mizu/picohttpparser/.gitattributes +1 -0
- data/ext/mizu/picohttpparser/.gitmodules +3 -0
- data/ext/mizu/picohttpparser/.travis.yml +6 -0
- data/ext/mizu/picohttpparser/Jamfile +7 -0
- data/ext/mizu/picohttpparser/Makefile +40 -0
- data/ext/mizu/picohttpparser/README.md +116 -0
- data/ext/mizu/picohttpparser/bench.c +66 -0
- data/ext/mizu/picohttpparser/picohttpparser.c +620 -0
- data/ext/mizu/picohttpparser/picohttpparser.h +89 -0
- data/ext/mizu/picohttpparser/test.c +419 -0
- metadata +12 -1
@@ -0,0 +1,89 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
|
3
|
+
* Shigeo Mitsunari
|
4
|
+
*
|
5
|
+
* The software is licensed under either the MIT License (below) or the Perl
|
6
|
+
* license.
|
7
|
+
*
|
8
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
9
|
+
* of this software and associated documentation files (the "Software"), to
|
10
|
+
* deal in the Software without restriction, including without limitation the
|
11
|
+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
12
|
+
* sell copies of the Software, and to permit persons to whom the Software is
|
13
|
+
* furnished to do so, subject to the following conditions:
|
14
|
+
*
|
15
|
+
* The above copyright notice and this permission notice shall be included in
|
16
|
+
* all copies or substantial portions of the Software.
|
17
|
+
*
|
18
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
19
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
20
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
21
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
22
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
23
|
+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
24
|
+
* IN THE SOFTWARE.
|
25
|
+
*/
|
26
|
+
|
27
|
+
#ifndef picohttpparser_h
|
28
|
+
#define picohttpparser_h
|
29
|
+
|
30
|
+
#include <sys/types.h>
|
31
|
+
|
32
|
+
#ifdef _MSC_VER
|
33
|
+
#define ssize_t intptr_t
|
34
|
+
#endif
|
35
|
+
|
36
|
+
/* $Id$ */
|
37
|
+
|
38
|
+
#ifdef __cplusplus
|
39
|
+
extern "C" {
|
40
|
+
#endif
|
41
|
+
|
42
|
+
/* contains name and value of a header (name == NULL if is a continuing line
|
43
|
+
* of a multiline header */
|
44
|
+
struct phr_header {
|
45
|
+
const char *name;
|
46
|
+
size_t name_len;
|
47
|
+
const char *value;
|
48
|
+
size_t value_len;
|
49
|
+
};
|
50
|
+
|
51
|
+
/* returns number of bytes consumed if successful, -2 if request is partial,
|
52
|
+
* -1 if failed */
|
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);
|
59
|
+
|
60
|
+
/* ditto */
|
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);
|
81
|
+
|
82
|
+
/* returns if the chunked decoder is in middle of chunked data */
|
83
|
+
int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder);
|
84
|
+
|
85
|
+
#ifdef __cplusplus
|
86
|
+
}
|
87
|
+
#endif
|
88
|
+
|
89
|
+
#endif
|
@@ -0,0 +1,419 @@
|
|
1
|
+
/* use `make test` to run the test */
|
2
|
+
/*
|
3
|
+
* Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
|
4
|
+
* Shigeo Mitsunari
|
5
|
+
*
|
6
|
+
* The software is licensed under either the MIT License (below) or the Perl
|
7
|
+
* license.
|
8
|
+
*
|
9
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
10
|
+
* of this software and associated documentation files (the "Software"), to
|
11
|
+
* deal in the Software without restriction, including without limitation the
|
12
|
+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
13
|
+
* sell copies of the Software, and to permit persons to whom the Software is
|
14
|
+
* furnished to do so, subject to the following conditions:
|
15
|
+
*
|
16
|
+
* The above copyright notice and this permission notice shall be included in
|
17
|
+
* all copies or substantial portions of the Software.
|
18
|
+
*
|
19
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
20
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
21
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
22
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
23
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
24
|
+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
25
|
+
* IN THE SOFTWARE.
|
26
|
+
*/
|
27
|
+
|
28
|
+
#include <stdio.h>
|
29
|
+
#include <stdlib.h>
|
30
|
+
#include <string.h>
|
31
|
+
#include "picotest/picotest.h"
|
32
|
+
#include "picohttpparser.h"
|
33
|
+
|
34
|
+
static int bufis(const char *s, size_t l, const char *t)
|
35
|
+
{
|
36
|
+
return strlen(t) == l && memcmp(s, t, l) == 0;
|
37
|
+
}
|
38
|
+
|
39
|
+
static void test_request(void)
|
40
|
+
{
|
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, -1, "parse header name with trailing space");
|
98
|
+
|
99
|
+
PARSE("GET", 0, -2, "incomplete 1");
|
100
|
+
ok(method == NULL);
|
101
|
+
PARSE("GET ", 0, -2, "incomplete 2");
|
102
|
+
ok(bufis(method, method_len, "GET"));
|
103
|
+
PARSE("GET /", 0, -2, "incomplete 3");
|
104
|
+
ok(path == NULL);
|
105
|
+
PARSE("GET / ", 0, -2, "incomplete 4");
|
106
|
+
ok(bufis(path, path_len, "/"));
|
107
|
+
PARSE("GET / H", 0, -2, "incomplete 5");
|
108
|
+
PARSE("GET / HTTP/1.", 0, -2, "incomplete 6");
|
109
|
+
PARSE("GET / HTTP/1.0", 0, -2, "incomplete 7");
|
110
|
+
ok(minor_version == -1);
|
111
|
+
PARSE("GET / HTTP/1.0\r", 0, -2, "incomplete 8");
|
112
|
+
ok(minor_version == 0);
|
113
|
+
|
114
|
+
PARSE("GET /hoge HTTP/1.0\r\n\r", strlen("GET /hoge HTTP/1.0\r\n\r") - 1, -2, "slowloris (incomplete)");
|
115
|
+
PARSE("GET /hoge HTTP/1.0\r\n\r\n", strlen("GET /hoge HTTP/1.0\r\n\r\n") - 1, 0, "slowloris (complete)");
|
116
|
+
|
117
|
+
PARSE("GET / HTTP/1.0\r\n:a\r\n\r\n", 0, -1, "empty header name");
|
118
|
+
PARSE("GET / HTTP/1.0\r\n :a\r\n\r\n", 0, -1, "header name (space only)");
|
119
|
+
|
120
|
+
PARSE("G\0T / HTTP/1.0\r\n\r\n", 0, -1, "NUL in method");
|
121
|
+
PARSE("G\tT / HTTP/1.0\r\n\r\n", 0, -1, "tab in method");
|
122
|
+
PARSE("GET /\x7fhello HTTP/1.0\r\n\r\n", 0, -1, "DEL in uri-path");
|
123
|
+
PARSE("GET / HTTP/1.0\r\na\0b: c\r\n\r\n", 0, -1, "NUL in header name");
|
124
|
+
PARSE("GET / HTTP/1.0\r\nab: c\0d\r\n\r\n", 0, -1, "NUL in header value");
|
125
|
+
PARSE("GET / HTTP/1.0\r\na\033b: c\r\n\r\n", 0, -1, "CTL in header name");
|
126
|
+
PARSE("GET / HTTP/1.0\r\nab: c\033\r\n\r\n", 0, -1, "CTL in header value");
|
127
|
+
PARSE("GET / HTTP/1.0\r\n/: 1\r\n\r\n", 0, -1, "invalid char in header value");
|
128
|
+
PARSE("GET /\xa0 HTTP/1.0\r\nh: c\xa2y\r\n\r\n", 0, 0, "accept MSB chars");
|
129
|
+
ok(num_headers == 1);
|
130
|
+
ok(bufis(method, method_len, "GET"));
|
131
|
+
ok(bufis(path, path_len, "/\xa0"));
|
132
|
+
ok(minor_version == 0);
|
133
|
+
ok(bufis(headers[0].name, headers[0].name_len, "h"));
|
134
|
+
ok(bufis(headers[0].value, headers[0].value_len, "c\xa2y"));
|
135
|
+
|
136
|
+
PARSE("GET / HTTP/1.0\r\n\x7c\x7e: 1\r\n\r\n", 0, 0, "accept |~ (though forbidden by SSE)");
|
137
|
+
ok(num_headers == 1);
|
138
|
+
ok(bufis(headers[0].name, headers[0].name_len, "\x7c\x7e"));
|
139
|
+
ok(bufis(headers[0].value, headers[0].value_len, "1"));
|
140
|
+
|
141
|
+
PARSE("GET / HTTP/1.0\r\n\x7b: 1\r\n\r\n", 0, -1, "disallow {");
|
142
|
+
|
143
|
+
#undef PARSE
|
144
|
+
}
|
145
|
+
|
146
|
+
static void test_response(void)
|
147
|
+
{
|
148
|
+
int minor_version;
|
149
|
+
int status;
|
150
|
+
const char *msg;
|
151
|
+
size_t msg_len;
|
152
|
+
struct phr_header headers[4];
|
153
|
+
size_t num_headers;
|
154
|
+
|
155
|
+
#define PARSE(s, last_len, exp, comment) \
|
156
|
+
do { \
|
157
|
+
note(comment); \
|
158
|
+
num_headers = sizeof(headers) / sizeof(headers[0]); \
|
159
|
+
ok(phr_parse_response(s, strlen(s), &minor_version, &status, &msg, &msg_len, headers, &num_headers, last_len) == \
|
160
|
+
(exp == 0 ? strlen(s) : exp)); \
|
161
|
+
} while (0)
|
162
|
+
|
163
|
+
PARSE("HTTP/1.0 200 OK\r\n\r\n", 0, 0, "simple");
|
164
|
+
ok(num_headers == 0);
|
165
|
+
ok(status == 200);
|
166
|
+
ok(minor_version == 0);
|
167
|
+
ok(bufis(msg, msg_len, "OK"));
|
168
|
+
|
169
|
+
PARSE("HTTP/1.0 200 OK\r\n\r", 0, -2, "partial");
|
170
|
+
|
171
|
+
PARSE("HTTP/1.1 200 OK\r\nHost: example.com\r\nCookie: \r\n\r\n", 0, 0, "parse headers");
|
172
|
+
ok(num_headers == 2);
|
173
|
+
ok(minor_version == 1);
|
174
|
+
ok(status == 200);
|
175
|
+
ok(bufis(msg, msg_len, "OK"));
|
176
|
+
ok(bufis(headers[0].name, headers[0].name_len, "Host"));
|
177
|
+
ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
|
178
|
+
ok(bufis(headers[1].name, headers[1].name_len, "Cookie"));
|
179
|
+
ok(bufis(headers[1].value, headers[1].value_len, ""));
|
180
|
+
|
181
|
+
PARSE("HTTP/1.0 200 OK\r\nfoo: \r\nfoo: b\r\n \tc\r\n\r\n", 0, 0, "parse multiline");
|
182
|
+
ok(num_headers == 3);
|
183
|
+
ok(minor_version == 0);
|
184
|
+
ok(status == 200);
|
185
|
+
ok(bufis(msg, msg_len, "OK"));
|
186
|
+
ok(bufis(headers[0].name, headers[0].name_len, "foo"));
|
187
|
+
ok(bufis(headers[0].value, headers[0].value_len, ""));
|
188
|
+
ok(bufis(headers[1].name, headers[1].name_len, "foo"));
|
189
|
+
ok(bufis(headers[1].value, headers[1].value_len, "b"));
|
190
|
+
ok(headers[2].name == NULL);
|
191
|
+
ok(bufis(headers[2].value, headers[2].value_len, " \tc"));
|
192
|
+
|
193
|
+
PARSE("HTTP/1.0 500 Internal Server Error\r\n\r\n", 0, 0, "internal server error");
|
194
|
+
ok(num_headers == 0);
|
195
|
+
ok(minor_version == 0);
|
196
|
+
ok(status == 500);
|
197
|
+
ok(bufis(msg, msg_len, "Internal Server Error"));
|
198
|
+
ok(msg_len == sizeof("Internal Server Error") - 1);
|
199
|
+
|
200
|
+
PARSE("H", 0, -2, "incomplete 1");
|
201
|
+
PARSE("HTTP/1.", 0, -2, "incomplete 2");
|
202
|
+
PARSE("HTTP/1.1", 0, -2, "incomplete 3");
|
203
|
+
ok(minor_version == -1);
|
204
|
+
PARSE("HTTP/1.1 ", 0, -2, "incomplete 4");
|
205
|
+
ok(minor_version == 1);
|
206
|
+
PARSE("HTTP/1.1 2", 0, -2, "incomplete 5");
|
207
|
+
PARSE("HTTP/1.1 200", 0, -2, "incomplete 6");
|
208
|
+
ok(status == 0);
|
209
|
+
PARSE("HTTP/1.1 200 ", 0, -2, "incomplete 7");
|
210
|
+
ok(status == 200);
|
211
|
+
PARSE("HTTP/1.1 200 O", 0, -2, "incomplete 8");
|
212
|
+
PARSE("HTTP/1.1 200 OK\r", 0, -2, "incomplete 9");
|
213
|
+
ok(msg == NULL);
|
214
|
+
PARSE("HTTP/1.1 200 OK\r\n", 0, -2, "incomplete 10");
|
215
|
+
ok(bufis(msg, msg_len, "OK"));
|
216
|
+
PARSE("HTTP/1.1 200 OK\n", 0, -2, "incomplete 11");
|
217
|
+
ok(bufis(msg, msg_len, "OK"));
|
218
|
+
|
219
|
+
PARSE("HTTP/1.1 200 OK\r\nA: 1\r", 0, -2, "incomplete 11");
|
220
|
+
ok(num_headers == 0);
|
221
|
+
PARSE("HTTP/1.1 200 OK\r\nA: 1\r\n", 0, -2, "incomplete 12");
|
222
|
+
ok(num_headers == 1);
|
223
|
+
ok(bufis(headers[0].name, headers[0].name_len, "A"));
|
224
|
+
ok(bufis(headers[0].value, headers[0].value_len, "1"));
|
225
|
+
|
226
|
+
PARSE("HTTP/1.0 200 OK\r\n\r", strlen("HTTP/1.0 200 OK\r\n\r") - 1, -2, "slowloris (incomplete)");
|
227
|
+
PARSE("HTTP/1.0 200 OK\r\n\r\n", strlen("HTTP/1.0 200 OK\r\n\r\n") - 1, 0, "slowloris (complete)");
|
228
|
+
|
229
|
+
PARSE("HTTP/1. 200 OK\r\n\r\n", 0, -1, "invalid http version");
|
230
|
+
PARSE("HTTP/1.2z 200 OK\r\n\r\n", 0, -1, "invalid http version 2");
|
231
|
+
PARSE("HTTP/1.1 OK\r\n\r\n", 0, -1, "no status code");
|
232
|
+
|
233
|
+
#undef PARSE
|
234
|
+
}
|
235
|
+
|
236
|
+
static void test_headers(void)
|
237
|
+
{
|
238
|
+
/* only test the interface; the core parser is tested by the tests above */
|
239
|
+
|
240
|
+
struct phr_header headers[4];
|
241
|
+
size_t num_headers;
|
242
|
+
|
243
|
+
#define PARSE(s, last_len, exp, comment) \
|
244
|
+
do { \
|
245
|
+
note(comment); \
|
246
|
+
num_headers = sizeof(headers) / sizeof(headers[0]); \
|
247
|
+
ok(phr_parse_headers(s, strlen(s), headers, &num_headers, last_len) == (exp == 0 ? strlen(s) : exp)); \
|
248
|
+
} while (0)
|
249
|
+
|
250
|
+
PARSE("Host: example.com\r\nCookie: \r\n\r\n", 0, 0, "simple");
|
251
|
+
ok(num_headers == 2);
|
252
|
+
ok(bufis(headers[0].name, headers[0].name_len, "Host"));
|
253
|
+
ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
|
254
|
+
ok(bufis(headers[1].name, headers[1].name_len, "Cookie"));
|
255
|
+
ok(bufis(headers[1].value, headers[1].value_len, ""));
|
256
|
+
|
257
|
+
PARSE("Host: example.com\r\nCookie: \r\n\r\n", 1, 0, "slowloris");
|
258
|
+
ok(num_headers == 2);
|
259
|
+
ok(bufis(headers[0].name, headers[0].name_len, "Host"));
|
260
|
+
ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
|
261
|
+
ok(bufis(headers[1].name, headers[1].name_len, "Cookie"));
|
262
|
+
ok(bufis(headers[1].value, headers[1].value_len, ""));
|
263
|
+
|
264
|
+
PARSE("Host: example.com\r\nCookie: \r\n\r", 0, -2, "partial");
|
265
|
+
|
266
|
+
PARSE("Host: e\7fample.com\r\nCookie: \r\n\r", 0, -1, "error");
|
267
|
+
|
268
|
+
#undef PARSE
|
269
|
+
}
|
270
|
+
|
271
|
+
static void test_chunked_at_once(int line, int consume_trailer, const char *encoded, const char *decoded, ssize_t expected)
|
272
|
+
{
|
273
|
+
struct phr_chunked_decoder dec = {0};
|
274
|
+
char *buf;
|
275
|
+
size_t bufsz;
|
276
|
+
ssize_t ret;
|
277
|
+
|
278
|
+
dec.consume_trailer = consume_trailer;
|
279
|
+
|
280
|
+
note("testing at-once, source at line %d", line);
|
281
|
+
|
282
|
+
buf = strdup(encoded);
|
283
|
+
bufsz = strlen(buf);
|
284
|
+
|
285
|
+
ret = phr_decode_chunked(&dec, buf, &bufsz);
|
286
|
+
|
287
|
+
ok(ret == expected);
|
288
|
+
ok(bufsz == strlen(decoded));
|
289
|
+
ok(bufis(buf, bufsz, decoded));
|
290
|
+
if (expected >= 0) {
|
291
|
+
if (ret == expected)
|
292
|
+
ok(bufis(buf + bufsz, ret, encoded + strlen(encoded) - ret));
|
293
|
+
else
|
294
|
+
ok(0);
|
295
|
+
}
|
296
|
+
|
297
|
+
free(buf);
|
298
|
+
}
|
299
|
+
|
300
|
+
static void test_chunked_per_byte(int line, int consume_trailer, const char *encoded, const char *decoded, ssize_t expected)
|
301
|
+
{
|
302
|
+
struct phr_chunked_decoder dec = {0};
|
303
|
+
char *buf = malloc(strlen(encoded) + 1);
|
304
|
+
size_t bytes_to_consume = strlen(encoded) - (expected >= 0 ? expected : 0), bytes_ready = 0, bufsz, i;
|
305
|
+
ssize_t ret;
|
306
|
+
|
307
|
+
dec.consume_trailer = consume_trailer;
|
308
|
+
|
309
|
+
note("testing per-byte, source at line %d", line);
|
310
|
+
|
311
|
+
for (i = 0; i < bytes_to_consume - 1; ++i) {
|
312
|
+
buf[bytes_ready] = encoded[i];
|
313
|
+
bufsz = 1;
|
314
|
+
ret = phr_decode_chunked(&dec, buf + bytes_ready, &bufsz);
|
315
|
+
if (ret != -2) {
|
316
|
+
ok(0);
|
317
|
+
goto cleanup;
|
318
|
+
}
|
319
|
+
bytes_ready += bufsz;
|
320
|
+
}
|
321
|
+
strcpy(buf + bytes_ready, encoded + bytes_to_consume - 1);
|
322
|
+
bufsz = strlen(buf + bytes_ready);
|
323
|
+
ret = phr_decode_chunked(&dec, buf + bytes_ready, &bufsz);
|
324
|
+
ok(ret == expected);
|
325
|
+
bytes_ready += bufsz;
|
326
|
+
ok(bytes_ready == strlen(decoded));
|
327
|
+
ok(bufis(buf, bytes_ready, decoded));
|
328
|
+
if (expected >= 0) {
|
329
|
+
if (ret == expected)
|
330
|
+
ok(bufis(buf + bytes_ready, expected, encoded + bytes_to_consume));
|
331
|
+
else
|
332
|
+
ok(0);
|
333
|
+
}
|
334
|
+
|
335
|
+
cleanup:
|
336
|
+
free(buf);
|
337
|
+
}
|
338
|
+
|
339
|
+
static void test_chunked_failure(int line, const char *encoded, ssize_t expected)
|
340
|
+
{
|
341
|
+
struct phr_chunked_decoder dec = {0};
|
342
|
+
char *buf = strdup(encoded);
|
343
|
+
size_t bufsz, i;
|
344
|
+
ssize_t ret;
|
345
|
+
|
346
|
+
note("testing failure at-once, source at line %d", line);
|
347
|
+
bufsz = strlen(buf);
|
348
|
+
ret = phr_decode_chunked(&dec, buf, &bufsz);
|
349
|
+
ok(ret == expected);
|
350
|
+
|
351
|
+
note("testing failure per-byte, source at line %d", line);
|
352
|
+
memset(&dec, 0, sizeof(dec));
|
353
|
+
for (i = 0; encoded[i] != '\0'; ++i) {
|
354
|
+
buf[0] = encoded[i];
|
355
|
+
bufsz = 1;
|
356
|
+
ret = phr_decode_chunked(&dec, buf, &bufsz);
|
357
|
+
if (ret == -1) {
|
358
|
+
ok(ret == expected);
|
359
|
+
goto cleanup;
|
360
|
+
} else if (ret == -2) {
|
361
|
+
/* continue */
|
362
|
+
} else {
|
363
|
+
ok(0);
|
364
|
+
goto cleanup;
|
365
|
+
}
|
366
|
+
}
|
367
|
+
ok(ret == expected);
|
368
|
+
|
369
|
+
cleanup:
|
370
|
+
free(buf);
|
371
|
+
}
|
372
|
+
|
373
|
+
static void (*chunked_test_runners[])(int, int, const char *, const char *, ssize_t) = {test_chunked_at_once, test_chunked_per_byte,
|
374
|
+
NULL};
|
375
|
+
|
376
|
+
static void test_chunked(void)
|
377
|
+
{
|
378
|
+
size_t i;
|
379
|
+
|
380
|
+
for (i = 0; chunked_test_runners[i] != NULL; ++i) {
|
381
|
+
chunked_test_runners[i](__LINE__, 0, "b\r\nhello world\r\n0\r\n", "hello world", 0);
|
382
|
+
chunked_test_runners[i](__LINE__, 0, "6\r\nhello \r\n5\r\nworld\r\n0\r\n", "hello world", 0);
|
383
|
+
chunked_test_runners[i](__LINE__, 0, "6;comment=hi\r\nhello \r\n5\r\nworld\r\n0\r\n", "hello world", 0);
|
384
|
+
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",
|
385
|
+
sizeof("a: b\r\nc: d\r\n\r\n") - 1);
|
386
|
+
chunked_test_runners[i](__LINE__, 0, "b\r\nhello world\r\n0\r\n", "hello world", 0);
|
387
|
+
}
|
388
|
+
|
389
|
+
note("failures");
|
390
|
+
test_chunked_failure(__LINE__, "z\r\nabcdefg", -1);
|
391
|
+
if (sizeof(size_t) == 8) {
|
392
|
+
test_chunked_failure(__LINE__, "6\r\nhello \r\nffffffffffffffff\r\nabcdefg", -2);
|
393
|
+
test_chunked_failure(__LINE__, "6\r\nhello \r\nfffffffffffffffff\r\nabcdefg", -1);
|
394
|
+
}
|
395
|
+
}
|
396
|
+
|
397
|
+
static void test_chunked_consume_trailer(void)
|
398
|
+
{
|
399
|
+
size_t i;
|
400
|
+
|
401
|
+
for (i = 0; chunked_test_runners[i] != NULL; ++i) {
|
402
|
+
chunked_test_runners[i](__LINE__, 1, "b\r\nhello world\r\n0\r\n", "hello world", -2);
|
403
|
+
chunked_test_runners[i](__LINE__, 1, "6\r\nhello \r\n5\r\nworld\r\n0\r\n", "hello world", -2);
|
404
|
+
chunked_test_runners[i](__LINE__, 1, "6;comment=hi\r\nhello \r\n5\r\nworld\r\n0\r\n", "hello world", -2);
|
405
|
+
chunked_test_runners[i](__LINE__, 1, "b\r\nhello world\r\n0\r\n\r\n", "hello world", 0);
|
406
|
+
chunked_test_runners[i](__LINE__, 1, "b\nhello world\n0\n\n", "hello world", 0);
|
407
|
+
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);
|
408
|
+
}
|
409
|
+
}
|
410
|
+
|
411
|
+
int main(int argc, char **argv)
|
412
|
+
{
|
413
|
+
subtest("request", test_request);
|
414
|
+
subtest("response", test_response);
|
415
|
+
subtest("headers", test_headers);
|
416
|
+
subtest("chunked", test_chunked);
|
417
|
+
subtest("chunked-consume-trailer", test_chunked_consume_trailer);
|
418
|
+
return done_testing();
|
419
|
+
}
|