mizu 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }