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
bufis(const char * s,size_t l,const char * t)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
test_request(void)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(" / HTTP/1.0\r\n\r\n", 0, -1, "empty method");
118 PARSE("GET HTTP/1.0\r\n\r\n", 0, -1, "empty request-target");
119
120 PARSE("GET / HTTP/1.0\r\n:a\r\n\r\n", 0, -1, "empty header name");
121 PARSE("GET / HTTP/1.0\r\n :a\r\n\r\n", 0, -1, "header name (space only)");
122
123 PARSE("G\0T / HTTP/1.0\r\n\r\n", 0, -1, "NUL in method");
124 PARSE("G\tT / HTTP/1.0\r\n\r\n", 0, -1, "tab in method");
125 PARSE("GET /\x7fhello HTTP/1.0\r\n\r\n", 0, -1, "DEL in uri-path");
126 PARSE("GET / HTTP/1.0\r\na\0b: c\r\n\r\n", 0, -1, "NUL in header name");
127 PARSE("GET / HTTP/1.0\r\nab: c\0d\r\n\r\n", 0, -1, "NUL in header value");
128 PARSE("GET / HTTP/1.0\r\na\033b: c\r\n\r\n", 0, -1, "CTL in header name");
129 PARSE("GET / HTTP/1.0\r\nab: c\033\r\n\r\n", 0, -1, "CTL in header value");
130 PARSE("GET / HTTP/1.0\r\n/: 1\r\n\r\n", 0, -1, "invalid char in header value");
131 PARSE("GET /\xa0 HTTP/1.0\r\nh: c\xa2y\r\n\r\n", 0, 0, "accept MSB chars");
132 ok(num_headers == 1);
133 ok(bufis(method, method_len, "GET"));
134 ok(bufis(path, path_len, "/\xa0"));
135 ok(minor_version == 0);
136 ok(bufis(headers[0].name, headers[0].name_len, "h"));
137 ok(bufis(headers[0].value, headers[0].value_len, "c\xa2y"));
138
139 PARSE("GET / HTTP/1.0\r\n\x7c\x7e: 1\r\n\r\n", 0, 0, "accept |~ (though forbidden by SSE)");
140 ok(num_headers == 1);
141 ok(bufis(headers[0].name, headers[0].name_len, "\x7c\x7e"));
142 ok(bufis(headers[0].value, headers[0].value_len, "1"));
143
144 PARSE("GET / HTTP/1.0\r\n\x7b: 1\r\n\r\n", 0, -1, "disallow {");
145
146 PARSE("GET / HTTP/1.0\r\nfoo: a \t \r\n\r\n", 0, 0, "exclude leading and trailing spaces in header value");
147 ok(bufis(headers[0].value, headers[0].value_len, "a"));
148
149 #undef PARSE
150 }
151
test_response(void)152 static void test_response(void)
153 {
154 int minor_version;
155 int status;
156 const char *msg;
157 size_t msg_len;
158 struct phr_header headers[4];
159 size_t num_headers;
160
161 #define PARSE(s, last_len, exp, comment) \
162 do { \
163 note(comment); \
164 num_headers = sizeof(headers) / sizeof(headers[0]); \
165 ok(phr_parse_response(s, strlen(s), &minor_version, &status, &msg, &msg_len, headers, &num_headers, last_len) == \
166 (exp == 0 ? strlen(s) : exp)); \
167 } while (0)
168
169 PARSE("HTTP/1.0 200 OK\r\n\r\n", 0, 0, "simple");
170 ok(num_headers == 0);
171 ok(status == 200);
172 ok(minor_version == 0);
173 ok(bufis(msg, msg_len, "OK"));
174
175 PARSE("HTTP/1.0 200 OK\r\n\r", 0, -2, "partial");
176
177 PARSE("HTTP/1.1 200 OK\r\nHost: example.com\r\nCookie: \r\n\r\n", 0, 0, "parse headers");
178 ok(num_headers == 2);
179 ok(minor_version == 1);
180 ok(status == 200);
181 ok(bufis(msg, msg_len, "OK"));
182 ok(bufis(headers[0].name, headers[0].name_len, "Host"));
183 ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
184 ok(bufis(headers[1].name, headers[1].name_len, "Cookie"));
185 ok(bufis(headers[1].value, headers[1].value_len, ""));
186
187 PARSE("HTTP/1.0 200 OK\r\nfoo: \r\nfoo: b\r\n \tc\r\n\r\n", 0, 0, "parse multiline");
188 ok(num_headers == 3);
189 ok(minor_version == 0);
190 ok(status == 200);
191 ok(bufis(msg, msg_len, "OK"));
192 ok(bufis(headers[0].name, headers[0].name_len, "foo"));
193 ok(bufis(headers[0].value, headers[0].value_len, ""));
194 ok(bufis(headers[1].name, headers[1].name_len, "foo"));
195 ok(bufis(headers[1].value, headers[1].value_len, "b"));
196 ok(headers[2].name == NULL);
197 ok(bufis(headers[2].value, headers[2].value_len, " \tc"));
198
199 PARSE("HTTP/1.0 500 Internal Server Error\r\n\r\n", 0, 0, "internal server error");
200 ok(num_headers == 0);
201 ok(minor_version == 0);
202 ok(status == 500);
203 ok(bufis(msg, msg_len, "Internal Server Error"));
204 ok(msg_len == sizeof("Internal Server Error") - 1);
205
206 PARSE("H", 0, -2, "incomplete 1");
207 PARSE("HTTP/1.", 0, -2, "incomplete 2");
208 PARSE("HTTP/1.1", 0, -2, "incomplete 3");
209 ok(minor_version == -1);
210 PARSE("HTTP/1.1 ", 0, -2, "incomplete 4");
211 ok(minor_version == 1);
212 PARSE("HTTP/1.1 2", 0, -2, "incomplete 5");
213 PARSE("HTTP/1.1 200", 0, -2, "incomplete 6");
214 ok(status == 0);
215 PARSE("HTTP/1.1 200 ", 0, -2, "incomplete 7");
216 ok(status == 200);
217 PARSE("HTTP/1.1 200 O", 0, -2, "incomplete 8");
218 PARSE("HTTP/1.1 200 OK\r", 0, -2, "incomplete 9");
219 ok(msg == NULL);
220 PARSE("HTTP/1.1 200 OK\r\n", 0, -2, "incomplete 10");
221 ok(bufis(msg, msg_len, "OK"));
222 PARSE("HTTP/1.1 200 OK\n", 0, -2, "incomplete 11");
223 ok(bufis(msg, msg_len, "OK"));
224
225 PARSE("HTTP/1.1 200 OK\r\nA: 1\r", 0, -2, "incomplete 11");
226 ok(num_headers == 0);
227 PARSE("HTTP/1.1 200 OK\r\nA: 1\r\n", 0, -2, "incomplete 12");
228 ok(num_headers == 1);
229 ok(bufis(headers[0].name, headers[0].name_len, "A"));
230 ok(bufis(headers[0].value, headers[0].value_len, "1"));
231
232 PARSE("HTTP/1.0 200 OK\r\n\r", strlen("HTTP/1.0 200 OK\r\n\r") - 1, -2, "slowloris (incomplete)");
233 PARSE("HTTP/1.0 200 OK\r\n\r\n", strlen("HTTP/1.0 200 OK\r\n\r\n") - 1, 0, "slowloris (complete)");
234
235 PARSE("HTTP/1. 200 OK\r\n\r\n", 0, -1, "invalid http version");
236 PARSE("HTTP/1.2z 200 OK\r\n\r\n", 0, -1, "invalid http version 2");
237 PARSE("HTTP/1.1 OK\r\n\r\n", 0, -1, "no status code");
238
239 PARSE("HTTP/1.1 200 OK\r\nbar: \t b\t \t\r\n\r\n", 0, 0, "exclude leading and trailing spaces in header value");
240 ok(bufis(headers[0].value, headers[0].value_len, "b"));
241
242 #undef PARSE
243 }
244
test_headers(void)245 static void test_headers(void)
246 {
247 /* only test the interface; the core parser is tested by the tests above */
248
249 struct phr_header headers[4];
250 size_t num_headers;
251
252 #define PARSE(s, last_len, exp, comment) \
253 do { \
254 note(comment); \
255 num_headers = sizeof(headers) / sizeof(headers[0]); \
256 ok(phr_parse_headers(s, strlen(s), headers, &num_headers, last_len) == (exp == 0 ? strlen(s) : exp)); \
257 } while (0)
258
259 PARSE("Host: example.com\r\nCookie: \r\n\r\n", 0, 0, "simple");
260 ok(num_headers == 2);
261 ok(bufis(headers[0].name, headers[0].name_len, "Host"));
262 ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
263 ok(bufis(headers[1].name, headers[1].name_len, "Cookie"));
264 ok(bufis(headers[1].value, headers[1].value_len, ""));
265
266 PARSE("Host: example.com\r\nCookie: \r\n\r\n", 1, 0, "slowloris");
267 ok(num_headers == 2);
268 ok(bufis(headers[0].name, headers[0].name_len, "Host"));
269 ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
270 ok(bufis(headers[1].name, headers[1].name_len, "Cookie"));
271 ok(bufis(headers[1].value, headers[1].value_len, ""));
272
273 PARSE("Host: example.com\r\nCookie: \r\n\r", 0, -2, "partial");
274
275 PARSE("Host: e\7fample.com\r\nCookie: \r\n\r", 0, -1, "error");
276
277 #undef PARSE
278 }
279
test_chunked_at_once(int line,int consume_trailer,const char * encoded,const char * decoded,ssize_t expected)280 static void test_chunked_at_once(int line, int consume_trailer, const char *encoded, const char *decoded, ssize_t expected)
281 {
282 struct phr_chunked_decoder dec = {0};
283 char *buf;
284 size_t bufsz;
285 ssize_t ret;
286
287 dec.consume_trailer = consume_trailer;
288
289 note("testing at-once, source at line %d", line);
290
291 buf = strdup(encoded);
292 bufsz = strlen(buf);
293
294 ret = phr_decode_chunked(&dec, buf, &bufsz);
295
296 ok(ret == expected);
297 ok(bufsz == strlen(decoded));
298 ok(bufis(buf, bufsz, decoded));
299 if (expected >= 0) {
300 if (ret == expected)
301 ok(bufis(buf + bufsz, ret, encoded + strlen(encoded) - ret));
302 else
303 ok(0);
304 }
305
306 free(buf);
307 }
308
test_chunked_per_byte(int line,int consume_trailer,const char * encoded,const char * decoded,ssize_t expected)309 static void test_chunked_per_byte(int line, int consume_trailer, const char *encoded, const char *decoded, ssize_t expected)
310 {
311 struct phr_chunked_decoder dec = {0};
312 char *buf = malloc(strlen(encoded) + 1);
313 size_t bytes_to_consume = strlen(encoded) - (expected >= 0 ? expected : 0), bytes_ready = 0, bufsz, i;
314 ssize_t ret;
315
316 dec.consume_trailer = consume_trailer;
317
318 note("testing per-byte, source at line %d", line);
319
320 for (i = 0; i < bytes_to_consume - 1; ++i) {
321 buf[bytes_ready] = encoded[i];
322 bufsz = 1;
323 ret = phr_decode_chunked(&dec, buf + bytes_ready, &bufsz);
324 if (ret != -2) {
325 ok(0);
326 goto cleanup;
327 }
328 bytes_ready += bufsz;
329 }
330 strcpy(buf + bytes_ready, encoded + bytes_to_consume - 1);
331 bufsz = strlen(buf + bytes_ready);
332 ret = phr_decode_chunked(&dec, buf + bytes_ready, &bufsz);
333 ok(ret == expected);
334 bytes_ready += bufsz;
335 ok(bytes_ready == strlen(decoded));
336 ok(bufis(buf, bytes_ready, decoded));
337 if (expected >= 0) {
338 if (ret == expected)
339 ok(bufis(buf + bytes_ready, expected, encoded + bytes_to_consume));
340 else
341 ok(0);
342 }
343
344 cleanup:
345 free(buf);
346 }
347
test_chunked_failure(int line,const char * encoded,ssize_t expected)348 static void test_chunked_failure(int line, const char *encoded, ssize_t expected)
349 {
350 struct phr_chunked_decoder dec = {0};
351 char *buf = strdup(encoded);
352 size_t bufsz, i;
353 ssize_t ret;
354
355 note("testing failure at-once, source at line %d", line);
356 bufsz = strlen(buf);
357 ret = phr_decode_chunked(&dec, buf, &bufsz);
358 ok(ret == expected);
359
360 note("testing failure per-byte, source at line %d", line);
361 memset(&dec, 0, sizeof(dec));
362 for (i = 0; encoded[i] != '\0'; ++i) {
363 buf[0] = encoded[i];
364 bufsz = 1;
365 ret = phr_decode_chunked(&dec, buf, &bufsz);
366 if (ret == -1) {
367 ok(ret == expected);
368 goto cleanup;
369 } else if (ret == -2) {
370 /* continue */
371 } else {
372 ok(0);
373 goto cleanup;
374 }
375 }
376 ok(ret == expected);
377
378 cleanup:
379 free(buf);
380 }
381
382 static void (*chunked_test_runners[])(int, int, const char *, const char *, ssize_t) = {test_chunked_at_once, test_chunked_per_byte,
383 NULL};
384
test_chunked(void)385 static void test_chunked(void)
386 {
387 size_t i;
388
389 for (i = 0; chunked_test_runners[i] != NULL; ++i) {
390 chunked_test_runners[i](__LINE__, 0, "b\r\nhello world\r\n0\r\n", "hello world", 0);
391 chunked_test_runners[i](__LINE__, 0, "6\r\nhello \r\n5\r\nworld\r\n0\r\n", "hello world", 0);
392 chunked_test_runners[i](__LINE__, 0, "6;comment=hi\r\nhello \r\n5\r\nworld\r\n0\r\n", "hello world", 0);
393 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",
394 sizeof("a: b\r\nc: d\r\n\r\n") - 1);
395 chunked_test_runners[i](__LINE__, 0, "b\r\nhello world\r\n0\r\n", "hello world", 0);
396 }
397
398 note("failures");
399 test_chunked_failure(__LINE__, "z\r\nabcdefg", -1);
400 if (sizeof(size_t) == 8) {
401 test_chunked_failure(__LINE__, "6\r\nhello \r\nffffffffffffffff\r\nabcdefg", -2);
402 test_chunked_failure(__LINE__, "6\r\nhello \r\nfffffffffffffffff\r\nabcdefg", -1);
403 }
404 }
405
test_chunked_consume_trailer(void)406 static void test_chunked_consume_trailer(void)
407 {
408 size_t i;
409
410 for (i = 0; chunked_test_runners[i] != NULL; ++i) {
411 chunked_test_runners[i](__LINE__, 1, "b\r\nhello world\r\n0\r\n", "hello world", -2);
412 chunked_test_runners[i](__LINE__, 1, "6\r\nhello \r\n5\r\nworld\r\n0\r\n", "hello world", -2);
413 chunked_test_runners[i](__LINE__, 1, "6;comment=hi\r\nhello \r\n5\r\nworld\r\n0\r\n", "hello world", -2);
414 chunked_test_runners[i](__LINE__, 1, "b\r\nhello world\r\n0\r\n\r\n", "hello world", 0);
415 chunked_test_runners[i](__LINE__, 1, "b\nhello world\n0\n\n", "hello world", 0);
416 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);
417 }
418 }
419
main(int argc,char ** argv)420 int main(int argc, char **argv)
421 {
422 subtest("request", test_request);
423 subtest("response", test_response);
424 subtest("headers", test_headers);
425 subtest("chunked", test_chunked);
426 subtest("chunked-consume-trailer", test_chunked_consume_trailer);
427 return done_testing();
428 }
429