1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 * IN THE SOFTWARE.
20 */
21
22 #include <zephyr/net/http/parser.h>
23 #include <string.h>
24 #include <errno.h>
25
26 #include <zephyr/sys/printk.h>
27 #include <zephyr/tc_util.h>
28 #include <zephyr/ztest.h>
29
30 static
31 struct http_parser_settings settings_null = {
32 .on_message_begin = 0,
33 .on_header_field = 0,
34 .on_header_value = 0,
35 .on_url = 0,
36 .on_status = 0,
37 .on_body = 0,
38 .on_headers_complete = 0,
39 .on_message_complete = 0,
40 .on_chunk_header = 0,
41 .on_chunk_complete = 0};
42
43 struct url_test {
44 const char *name;
45 const char *url;
46 int is_connect;
47 struct http_parser_url u;
48 int rv;
49 };
50
51 const struct url_test url_tests[] = {
52 {
53 .name = "proxy request",
54 .url = "http://hostname/",
55 .is_connect = 0,
56 .u = { .field_set = (1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH),
57 .port = 0,
58 .field_data = {
59 { 0, 4 }, /* UF_SCHEMA */
60 { 7, 8 }, /* UF_HOST */
61 { 0, 0 }, /* UF_PORT */
62 { 15, 1 }, /* UF_PATH */
63 { 0, 0 }, /* UF_QUERY */
64 { 0, 0 }, /* UF_FRAGMENT */
65 { 0, 0 } } }, /* UF_USERINFO */
66 .rv = 0
67 },
68
69 {
70 .name = "proxy request with port",
71 .url = "http://hostname:444/",
72 .is_connect = 0,
73 .u = { .field_set = (1 << UF_SCHEMA) | (1 << UF_HOST) |
74 (1 << UF_PORT) | (1 << UF_PATH),
75 .port = 444,
76 .field_data = {
77 { 0, 4 }, /* UF_SCHEMA */
78 { 7, 8 }, /* UF_HOST */
79 { 16, 3 }, /* UF_PORT */
80 { 19, 1 }, /* UF_PATH */
81 { 0, 0 }, /* UF_QUERY */
82 { 0, 0 }, /* UF_FRAGMENT */
83 { 0, 0 } } }, /* UF_USERINFO */
84 .rv = 0
85 },
86
87 {
88 .name = "CONNECT request",
89 .url = "hostname:443",
90 .is_connect = 1,
91 .u = { .field_set = (1 << UF_HOST) | (1 << UF_PORT),
92 .port = 443,
93 .field_data = {
94 { 0, 0 }, /* UF_SCHEMA */
95 { 0, 8 }, /* UF_HOST */
96 { 9, 3 }, /* UF_PORT */
97 { 0, 0 }, /* UF_PATH */
98 { 0, 0 }, /* UF_QUERY */
99 { 0, 0 }, /* UF_FRAGMENT */
100 { 0, 0 } } }, /* UF_USERINFO */
101 .rv = 0
102 },
103
104 {
105 .name = "CONNECT request but not connect",
106 .url = "hostname:443",
107 .is_connect = 0,
108 .rv = 1
109 },
110
111 {
112 .name = "proxy ipv6 request",
113 .url = "http://[1:2::3:4]/",
114 .is_connect = 0,
115 .u = { .field_set = (1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH),
116 .port = 0,
117 .field_data = {
118 { 0, 4 }, /* UF_SCHEMA */
119 { 8, 8 }, /* UF_HOST */
120 { 0, 0 }, /* UF_PORT */
121 { 17, 1 }, /* UF_PATH */
122 { 0, 0 }, /* UF_QUERY */
123 { 0, 0 }, /* UF_FRAGMENT */
124 { 0, 0 } } }, /* UF_USERINFO */
125 .rv = 0
126 },
127
128 {
129 .name = "proxy ipv6 request with port",
130 .url = "http://[1:2::3:4]:67/",
131 .is_connect = 0,
132 .u = { .field_set = (1 << UF_SCHEMA) | (1 << UF_HOST) |
133 (1 << UF_PORT) | (1 << UF_PATH),
134 .port = 67,
135 .field_data = {
136 { 0, 4 }, /* UF_SCHEMA */
137 { 8, 8 }, /* UF_HOST */
138 { 18, 2 }, /* UF_PORT */
139 { 20, 1 }, /* UF_PATH */
140 { 0, 0 }, /* UF_QUERY */
141 { 0, 0 }, /* UF_FRAGMENT */
142 { 0, 0 } } }, /* UF_USERINFO */
143 .rv = 0
144 },
145
146 {
147 .name = "CONNECT ipv6 address",
148 .url = "[1:2::3:4]:443",
149 .is_connect = 1,
150 .u = { .field_set = (1 << UF_HOST) | (1 << UF_PORT),
151 .port = 443,
152 .field_data = {
153 { 0, 0 }, /* UF_SCHEMA */
154 { 1, 8 }, /* UF_HOST */
155 { 11, 3 }, /* UF_PORT */
156 { 0, 0 }, /* UF_PATH */
157 { 0, 0 }, /* UF_QUERY */
158 { 0, 0 }, /* UF_FRAGMENT */
159 { 0, 0 } } }, /* UF_USERINFO */
160 .rv = 0
161 },
162
163 {
164 .name = "ipv4 in ipv6 address",
165 .url = "http://[2001:0000:0000:0000:0000:0000:1.9.1.1]/",
166 .is_connect = 0,
167 .u = { .field_set = (1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH),
168 .port = 0,
169 .field_data = {
170 { 0, 4 }, /* UF_SCHEMA */
171 { 8, 37 }, /* UF_HOST */
172 { 0, 0 }, /* UF_PORT */
173 { 46, 1 }, /* UF_PATH */
174 { 0, 0 }, /* UF_QUERY */
175 { 0, 0 }, /* UF_FRAGMENT */
176 { 0, 0 } } }, /* UF_USERINFO */
177 .rv = 0
178 },
179
180 {
181 .name = "extra ? in query string",
182 .url = "http://a.tbcdn.cn/p/fp/2010c/??fp-header-min.css,"
183 "fp-base-min.css,fp-channel-min.css,fp-product-min.css,fp-mall-"
184 "min.css,fp-category-min.css,fp-sub-min.css,fp-gdp4p-min.css,"
185 "fp-css3-min.css,fp-misc-min.css?t=20101022.css",
186 .is_connect = 0,
187 .u = { .field_set = (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) |
188 (1<<UF_QUERY),
189 .port = 0,
190 .field_data = {
191 { 0, 4 }, /* UF_SCHEMA */
192 { 7, 10 }, /* UF_HOST */
193 { 0, 0 }, /* UF_PORT */
194 { 17, 12 }, /* UF_PATH */
195 { 30, 187}, /* UF_QUERY */
196 { 0, 0 }, /* UF_FRAGMENT */
197 { 0, 0 } } }, /* UF_USERINFO */
198 .rv = 0
199 },
200
201 {
202 .name = "space URL encoded",
203 .url = "/toto.html?toto=a%20b",
204 .is_connect = 0,
205 .u = { .field_set = (1<<UF_PATH) | (1<<UF_QUERY),
206 .port = 0,
207 .field_data = {
208 { 0, 0 }, /* UF_SCHEMA */
209 { 0, 0 }, /* UF_HOST */
210 { 0, 0 }, /* UF_PORT */
211 { 0, 10 }, /* UF_PATH */
212 { 11, 10 }, /* UF_QUERY */
213 { 0, 0 }, /* UF_FRAGMENT */
214 { 0, 0 } } }, /* UF_USERINFO */
215 .rv = 0
216 },
217
218 {
219 .name = "URL fragment",
220 .url = "/toto.html#titi",
221 .is_connect = 0,
222 .u = { .field_set = (1<<UF_PATH) | (1<<UF_FRAGMENT),
223 .port = 0,
224 .field_data = {
225 { 0, 0 }, /* UF_SCHEMA */
226 { 0, 0 }, /* UF_HOST */
227 { 0, 0 }, /* UF_PORT */
228 { 0, 10 }, /* UF_PATH */
229 { 0, 0 }, /* UF_QUERY */
230 { 11, 4 }, /* UF_FRAGMENT */
231 { 0, 0 } } }, /* UF_USERINFO */
232 .rv = 0
233 },
234
235 {
236 .name = "complex URL fragment",
237 .url = "http://www.webmasterworld.com/r.cgi?f=21&d=8405&url="
238 "http://www.example.com/index.html?foo=bar&hello=world#midpage",
239 .is_connect = 0,
240 .u = { .field_set = (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) |
241 (1<<UF_QUERY) | (1<<UF_FRAGMENT),
242 .port = 0,
243 .field_data = {
244 { 0, 4 }, /* UF_SCHEMA */
245 { 7, 22 }, /* UF_HOST */
246 { 0, 0 }, /* UF_PORT */
247 { 29, 6 }, /* UF_PATH */
248 { 36, 69 }, /* UF_QUERY */
249 {106, 7 }, /* UF_FRAGMENT */
250 { 0, 0 } } }, /* UF_USERINFO */
251 .rv = 0
252 },
253
254 {
255 .name = "complex URL from node js url parser doc",
256 .url = "http://host.com:8080/p/a/t/h?query=string#hash",
257 .is_connect = 0,
258 .u = { .field_set = (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) |
259 (1<<UF_PATH) | (1<<UF_QUERY) | (1<<UF_FRAGMENT),
260 .port = 8080,
261 .field_data = {
262 { 0, 4 }, /* UF_SCHEMA */
263 { 7, 8 }, /* UF_HOST */
264 { 16, 4 }, /* UF_PORT */
265 { 20, 8 }, /* UF_PATH */
266 { 29, 12 }, /* UF_QUERY */
267 { 42, 4 }, /* UF_FRAGMENT */
268 { 0, 0 } } }, /* UF_USERINFO */
269 .rv = 0
270 },
271
272 {
273 .name = "complex URL with basic auth from node js url parser doc",
274 .url = "http://a:b@host.com:8080/p/a/t/h?query=string#hash",
275 .is_connect = 0,
276 .u = { .field_set = (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) |
277 (1<<UF_PATH) | (1<<UF_QUERY) | (1<<UF_FRAGMENT) |
278 (1<<UF_USERINFO),
279 .port = 8080,
280 .field_data = {
281 { 0, 4 }, /* UF_SCHEMA */
282 { 11, 8 }, /* UF_HOST */
283 { 20, 4 }, /* UF_PORT */
284 { 24, 8 }, /* UF_PATH */
285 { 33, 12 }, /* UF_QUERY */
286 { 46, 4 }, /* UF_FRAGMENT */
287 { 7, 3 } } }, /* UF_USERINFO */
288 .rv = 0
289 },
290
291 {
292 .name = "double @",
293 .url = "http://a:b@@hostname:443/",
294 .is_connect = 0,
295 .rv = 1
296 },
297
298 {
299 .name = "proxy empty host",
300 .url = "http://:443/",
301 .is_connect = 0,
302 .rv = 1
303 },
304
305 {
306 .name = "proxy empty port",
307 .url = "http://hostname:/",
308 .is_connect = 0,
309 .rv = 1,
310 },
311
312 {
313 .name = "CONNECT with basic auth",
314 .url = "a:b@hostname:443",
315 .is_connect = 1,
316 .rv = 1,
317 },
318
319 {
320 .name = "CONNECT empty host",
321 .url = ":443",
322 .is_connect = 1,
323 .rv = 1,
324 },
325
326 {
327 .name = "CONNECT empty port",
328 .url = "hostname:",
329 .is_connect = 1,
330 .rv = 1,
331 },
332
333 {
334 .name = "CONNECT with extra bits",
335 .url = "hostname:443/",
336 .is_connect = 1,
337 .rv = 1,
338 },
339
340 {
341 .name = "space in URL",
342 .url = "/foo bar/",
343 .rv = 1, /* s_dead */
344 },
345
346 {
347 .name = "proxy basic auth with space url encoded",
348 .url = "http://a%20:b@host.com/",
349 .is_connect = 0,
350 .u = { .field_set = (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) |
351 (1<<UF_USERINFO),
352 .port = 0,
353 .field_data = {
354 { 0, 4 }, /* UF_SCHEMA */
355 { 14, 8 }, /* UF_HOST */
356 { 0, 0 }, /* UF_PORT */
357 { 22, 1 }, /* UF_PATH */
358 { 0, 0 }, /* UF_QUERY */
359 { 0, 0 }, /* UF_FRAGMENT */
360 { 7, 6 } } }, /* UF_USERINFO */
361 .rv = 0
362 },
363
364 {
365 .name = "carriage return in URL",
366 .url = "/foo\rbar/",
367 .rv = 1 /* s_dead */
368 },
369
370 {
371 .name = "proxy double : in URL",
372 .url = "http://hostname::443/",
373 .rv = 1 /* s_dead */
374 },
375
376 {
377 .name = "proxy basic auth with double :",
378 .url = "http://a::b@host.com/",
379 .is_connect = 0,
380 .u = { .field_set = (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) |
381 (1<<UF_USERINFO),
382 .port = 0,
383 .field_data = {
384 { 0, 4 }, /* UF_SCHEMA */
385 { 12, 8 }, /* UF_HOST */
386 { 0, 0 }, /* UF_PORT */
387 { 20, 1 }, /* UF_PATH */
388 { 0, 0 }, /* UF_QUERY */
389 { 0, 0 }, /* UF_FRAGMENT */
390 { 7, 4 } } }, /* UF_USERINFO */
391 .rv = 0
392 },
393
394 {
395 .name = "line feed in URL",
396 .url = "/foo\nbar/",
397 .rv = 1 /* s_dead */
398 },
399
400 {
401 .name = "proxy empty basic auth",
402 .url = "http://@hostname/fo",
403 .u = { .field_set = (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH),
404 .port = 0,
405 .field_data = {
406 { 0, 4 }, /* UF_SCHEMA */
407 { 8, 8 }, /* UF_HOST */
408 { 0, 0 }, /* UF_PORT */
409 { 16, 3 }, /* UF_PATH */
410 { 0, 0 }, /* UF_QUERY */
411 { 0, 0 }, /* UF_FRAGMENT */
412 { 0, 0 } } }, /* UF_USERINFO */
413 .rv = 0
414 },
415
416 {
417 .name = "proxy line feed in hostname",
418 .url = "http://host\name/fo",
419 .rv = 1 /* s_dead */
420 },
421
422 {
423 .name = "proxy % in hostname",
424 .url = "http://host%name/fo",
425 .rv = 1 /* s_dead */
426 },
427
428 {
429 .name = "proxy ; in hostname",
430 .url = "http://host;ame/fo",
431 .rv = 1 /* s_dead */
432 },
433
434 {
435 .name = "proxy basic auth with unreservedchars",
436 .url = "http://a!;-_!=+$@host.com/",
437 .is_connect = 0,
438 .u = { .field_set = (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) |
439 (1<<UF_USERINFO),
440 .port = 0,
441 .field_data = {
442 { 0, 4 }, /* UF_SCHEMA */
443 { 17, 8 }, /* UF_HOST */
444 { 0, 0 }, /* UF_PORT */
445 { 25, 1 }, /* UF_PATH */
446 { 0, 0 }, /* UF_QUERY */
447 { 0, 0 }, /* UF_FRAGMENT */
448 { 7, 9 } } }, /* UF_USERINFO */
449 .rv = 0
450 },
451
452 {
453 .name = "proxy only empty basic auth",
454 .url = "http://@/fo",
455 .rv = 1 /* s_dead */
456 },
457
458 {
459 .name = "proxy only basic auth",
460 .url = "http://toto@/fo",
461 .rv = 1 /* s_dead */
462 },
463
464 {
465 .name = "proxy empty hostname",
466 .url = "http:///fo",
467 .rv = 1 /* s_dead */
468 },
469
470 {
471 .name = "proxy = in URL",
472 .url = "http://host=ame/fo",
473 .rv = 1 /* s_dead */
474 },
475
476 {
477 .name = "ipv6 address with Zone ID",
478 .url = "http://[fe80::a%25eth0]/",
479 .is_connect = 0,
480 .u = { .field_set = (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH),
481 .port = 0,
482 .field_data = {
483 { 0, 4 }, /* UF_SCHEMA */
484 { 8, 14 }, /* UF_HOST */
485 { 0, 0 }, /* UF_PORT */
486 { 23, 1 }, /* UF_PATH */
487 { 0, 0 }, /* UF_QUERY */
488 { 0, 0 }, /* UF_FRAGMENT */
489 { 0, 0 } } }, /* UF_USERINFO */
490 .rv = 0
491 },
492
493 {
494 .name = "ipv6 address with Zone ID, but '%' is not percent-encoded",
495 .url = "http://[fe80::a%eth0]/",
496 .is_connect = 0,
497 .u = { .field_set = (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH),
498 .port = 0,
499 .field_data = {
500 { 0, 4 }, /* UF_SCHEMA */
501 { 8, 12 }, /* UF_HOST */
502 { 0, 0 }, /* UF_PORT */
503 { 21, 1 }, /* UF_PATH */
504 { 0, 0 }, /* UF_QUERY */
505 { 0, 0 }, /* UF_FRAGMENT */
506 { 0, 0 } } }, /* UF_USERINFO */
507 .rv = 0
508 },
509
510 {
511 .name = "ipv6 address ending with '%'",
512 .url = "http://[fe80::a%]/",
513 .rv = 1 /* s_dead */
514 },
515
516 {
517 .name = "ipv6 address with Zone ID including bad character",
518 .url = "http://[fe80::a%$HOME]/",
519 .rv = 1 /* s_dead */
520 },
521
522 {
523 .name = "just ipv6 Zone ID",
524 .url = "http://[%eth0]/",
525 .rv = 1 /* s_dead */
526 },
527
528 #ifdef CONFIG_HTTP_PARSER_STRICT
529 {
530 .name = "tab in URL",
531 .url = "/foo\tbar/",
532 .rv = 1 /* s_dead */
533 },
534
535 {
536 .name = "form feed in URL",
537 .url = "/foo\fbar/",
538 .rv = 1 /* s_dead */
539 },
540
541 #else /* !HTTP_PARSER_STRICT */
542
543 {
544 .name = "tab in URL",
545 .url = "/foo\tbar/",
546 .u = { .field_set = (1 << UF_PATH),
547 .field_data = {
548 { 0, 0 }, /* UF_SCHEMA */
549 { 0, 0 }, /* UF_HOST */
550 { 0, 0 }, /* UF_PORT */
551 { 0, 9 }, /* UF_PATH */
552 { 0, 0 }, /* UF_QUERY */
553 { 0, 0 }, /* UF_FRAGMENT */
554 { 0, 0 } } }, /* UF_USERINFO */
555 .rv = 0
556 },
557
558 {
559 .name = "form feed in URL",
560 .url = "/foo\fbar/",
561 .u = { .field_set = (1 << UF_PATH),
562 .field_data = {
563 { 0, 0 }, /* UF_SCHEMA */
564 { 0, 0 }, /* UF_HOST */
565 { 0, 0 }, /* UF_PORT */
566 { 0, 9 }, /* UF_PATH */
567 { 0, 0 }, /* UF_QUERY */
568 { 0, 0 }, /* UF_FRAGMENT */
569 { 0, 0 } } }, /* UF_USERINFO */
570 .rv = 0
571 }
572 #endif
573 };
574
ZTEST(http_header_fields_fn,test_preserve_data)575 ZTEST(http_header_fields_fn, test_preserve_data)
576 {
577 struct http_parser parser = { 0 };
578 char my_data[] = "application-specific data";
579
580 parser.data = my_data;
581 http_parser_init(&parser, HTTP_REQUEST);
582
583 /**TESTPOINT: Check results*/
584 zassert_equal(parser.data, my_data, "test_preserve_data error");
585 }
586
ZTEST(http_header_fields_fn,test_parse_url)587 ZTEST(http_header_fields_fn, test_parse_url)
588 {
589 struct http_parser_url u;
590 const struct url_test *test;
591 unsigned int elements;
592 unsigned int i;
593 int rv;
594
595 elements = ARRAY_SIZE(url_tests);
596 for (i = 0U; i < elements; i++) {
597 test = &url_tests[i];
598 (void)memset(&u, 0, sizeof(u));
599
600 rv = http_parser_parse_url(test->url,
601 strlen(test->url),
602 test->is_connect,
603 &u);
604
605 if (test->rv == 0) {
606
607 /**TESTPOINT: Check test_parse_url functions*/
608 zassert_false(rv, "http_parser_parse_url error");
609
610 zassert_false(memcmp(&u, &test->u, sizeof(u)),
611 "test_parse_url failed");
612 } else {
613 /* test->rv != 0 */
614 zassert_true(rv, "http_parser_parse_url error");
615 }
616 }
617 }
618
ZTEST(http_header_fields_fn,test_method_str)619 ZTEST(http_header_fields_fn, test_method_str)
620 {
621 /**TESTPOINT: Check test_method_str function*/
622 zassert_false(strcmp("GET", http_method_str(HTTP_GET)),
623 "http_method_str error");
624 zassert_false(strcmp("<unknown>", http_method_str(127)),
625 "http_method_str error");
626 }
627
ZTEST(http_header_fields_fn,test_header_nread_value)628 ZTEST(http_header_fields_fn, test_header_nread_value)
629 {
630 struct http_parser parser = { 0 };
631 const char *buf;
632 size_t parsed;
633
634 http_parser_init(&parser, HTTP_REQUEST);
635 buf = "GET / HTTP/1.1\r\nheader: value\nhdr: value\r\n";
636 parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
637
638 zassert_equal(parsed, strlen(buf),
639 "http_parser error");
640 zassert_equal(parser.nread, strlen(buf),
641 "http_parser error");
642 }
643
test_invalid_header_content(int req,const char * str)644 int test_invalid_header_content(int req, const char *str)
645 {
646 struct http_parser parser = { 0 };
647 const char *buf;
648 size_t parsed;
649 size_t buflen;
650
651 http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
652 buf = req ? "GET / HTTP/1.1\r\n" : "HTTP/1.1 200 OK\r\n";
653 parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
654
655 /**TESTPOINTS: Check test_invalid_header_content functions*/
656 zassert_equal(parsed, strlen(buf),
657 "http_parser_execute error");
658
659 buf = str;
660 buflen = strlen(buf);
661 parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
662 if (parsed != buflen) {
663 zassert_equal(HTTP_PARSER_ERRNO(&parser),
664 HPE_INVALID_HEADER_TOKEN,
665 "http_parser_execute error");
666
667 return TC_PASS;
668 }
669
670 return TC_FAIL;
671 }
672
test_invalid_header_field_content_error(int req)673 int test_invalid_header_field_content_error(int req)
674 {
675 int rc;
676
677 rc = test_invalid_header_content(req, "Foo: F\01ailure");
678
679 /**TESTPOINTS: Check test_invalid_header_field_content_error*/
680 zassert_false(rc, "test_invalid_header_content error");
681
682 rc = test_invalid_header_content(req, "Foo: B\02ar");
683
684 zassert_false(rc, "test_invalid_header_content error");
685
686 return TC_PASS;
687 }
688
test_invalid_header_field(int req,const char * str)689 int test_invalid_header_field(int req, const char *str)
690 {
691 struct http_parser parser = { 0 };
692 const char *buf;
693 size_t parsed;
694 size_t buflen;
695
696 http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
697 buf = req ? "GET / HTTP/1.1\r\n" : "HTTP/1.1 200 OK\r\n";
698 parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
699
700 /**TESTPOINTS: Check http_parser_execute*/
701 zassert_equal(parsed, strlen(buf),
702 "http_parser_execute error");
703 buf = str;
704 buflen = strlen(buf);
705 parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
706 if (parsed != buflen) {
707 zassert_equal(HTTP_PARSER_ERRNO(&parser),
708 HPE_INVALID_HEADER_TOKEN,
709 "http_parser_execute error");
710
711 return TC_PASS;
712 }
713
714 return TC_FAIL;
715 }
716
test_invalid_header_field_token_error(int req)717 int test_invalid_header_field_token_error(int req)
718 {
719 int rc;
720
721 rc = test_invalid_header_field(req, "Fo@: Failure");
722
723 /**TESTPOINTS: Check test_invalid_header_field_token_error*/
724 zassert_false(rc, "test_invalid_header_field error");
725
726 rc = test_invalid_header_field(req, "Foo\01\test: Bar");
727 zassert_false(rc, "test_invalid_header_field error");
728
729 return TC_PASS;
730 }
731
test_double_content_length_error(int req)732 int test_double_content_length_error(int req)
733 {
734 struct http_parser parser = { 0 };
735 const char *buf;
736 size_t parsed;
737 size_t buflen;
738
739 http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
740 buf = req ? "GET / HTTP/1.1\r\n" : "HTTP/1.1 200 OK\r\n";
741 parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
742
743 /**TESTPOINTS: Check http_parser_execute*/
744 zassert_equal(parsed, strlen(buf),
745 "http_parser_execute error");
746
747 buf = "Content-Length: 0\r\nContent-Length: 1\r\n\r\n";
748 buflen = strlen(buf);
749 parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
750 if (parsed != buflen) {
751 int error = HTTP_PARSER_ERRNO(&parser);
752
753 zassert_equal(error, HPE_UNEXPECTED_CONTENT_LENGTH,
754 "http_parser_execute error");
755
756 return TC_PASS;
757 }
758
759 return TC_FAIL;
760 }
761
test_chunked_content_length_error(int req)762 int test_chunked_content_length_error(int req)
763 {
764 struct http_parser parser = { 0 };
765 const char *buf;
766 size_t parsed;
767 size_t buflen;
768
769 http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
770
771 buf = req ? "GET / HTTP/1.1\r\n" : "HTTP/1.1 200 OK\r\n";
772 parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
773
774 /**TESTPOINTS: Check http_parser_execute*/
775 zassert_equal(parsed, strlen(buf),
776 "http_parser_execute error");
777
778 buf = "Transfer-Encoding: chunked\r\nContent-Length: 1\r\n\r\n";
779 buflen = strlen(buf);
780 parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
781 if (parsed != buflen) {
782 int error = HTTP_PARSER_ERRNO(&parser);
783
784 zassert_equal(error, HPE_UNEXPECTED_CONTENT_LENGTH,
785 "http_parser_execute error");
786
787 return TC_PASS;
788 }
789
790 return TC_FAIL;
791 }
792
test_header_cr_no_lf_error(int req)793 int test_header_cr_no_lf_error(int req)
794 {
795 struct http_parser parser = { 0 };
796 const char *buf;
797 size_t parsed;
798 size_t buflen;
799
800 http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
801 buf = req ? "GET / HTTP/1.1\r\n" : "HTTP/1.1 200 OK\r\n";
802 parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
803
804 /**TESTPOINTS: Check http_parser_execute*/
805 zassert_equal(parsed, strlen(buf),
806 "http_parser_execute error");
807
808
809 buf = "Foo: 1\rBar: 1\r\n\r\n";
810 buflen = strlen(buf);
811 parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
812 if (parsed != buflen) {
813 int error = HTTP_PARSER_ERRNO(&parser);
814
815 zassert_equal(error, HPE_LF_EXPECTED,
816 "http_parser_execute error");
817
818 return TC_PASS;
819 }
820
821 return TC_FAIL;
822 }
823
ZTEST(http_header_fields_fn,test_http_header_fields)824 ZTEST(http_header_fields_fn, test_http_header_fields)
825 {
826 int rc;
827
828 /* header field tests */
829 rc = test_double_content_length_error(HTTP_REQUEST);
830
831 /**TESTPOINT: Check test_double_content_length_error*/
832 zassert_false(rc, "test_double_content_length_error failed");
833
834 rc = test_chunked_content_length_error(HTTP_REQUEST);
835
836 /**TESTPOINT: Check test_chunked_content_length_error*/
837 zassert_false(rc, "test_chunked_content_length_error failed");
838
839 rc = test_header_cr_no_lf_error(HTTP_REQUEST);
840
841 /**TESTPOINT: Check test_header_cr_no_lf_error*/
842 zassert_false(rc, "test_header_cr_no_lf_error failed");
843
844 rc = test_invalid_header_field_token_error(HTTP_REQUEST);
845
846 /**TESTPOINT: Check test_invalid_header_field_token_error*/
847 zassert_false(rc, "test_invalid_header_field_token_error failed");
848
849 rc = test_invalid_header_field_content_error(HTTP_REQUEST);
850
851 /**TESTPOINT: Check test_invalid_header_field_content_error*/
852 zassert_false(rc, "test_invalid_header_field_content_error failed");
853
854 rc = test_double_content_length_error(HTTP_RESPONSE);
855
856 /**TESTPOINT: Check test_double_content_length_error*/
857 zassert_false(rc, "test_double_content_length_error failed");
858
859 rc = test_chunked_content_length_error(HTTP_RESPONSE);
860
861 /**TESTPOINT: Check test_chunked_content_length_error*/
862 zassert_false(rc, "test_chunked_content_length_error failed");
863
864 rc = test_header_cr_no_lf_error(HTTP_RESPONSE);
865
866 /**TESTPOINT: Check test_header_cr_no_lf_error*/
867 zassert_false(rc, "test_header_cr_no_lf_error failed");
868
869 rc = test_invalid_header_field_token_error(HTTP_RESPONSE);
870
871 /**TESTPOINT: Check test_invalid_header_field_token_error*/
872 zassert_false(rc, "test_invalid_header_field_token_error failed");
873
874 rc = test_invalid_header_field_content_error(HTTP_RESPONSE);
875
876 /**TESTPOINT: Check test_invalid_header_field_content_error*/
877 zassert_false(rc, "test_invalid_header_field_content_error failed");
878 }
879
880 ZTEST_SUITE(http_header_fields_fn, NULL, NULL, NULL, NULL, NULL);
881