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