]> git.ipfire.org Git - thirdparty/squid.git/blob - src/tests/testHttp1Parser.cc
Maintenance: Removed most NULLs using modernize-use-nullptr (#1075)
[thirdparty/squid.git] / src / tests / testHttp1Parser.cc
1 /*
2 * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 #include "squid.h"
10
11 #include <cppunit/TestAssert.h>
12
13 #define private public
14 #define protected public
15
16 #include "debug/Stream.h"
17 #include "http/one/RequestParser.h"
18 #include "http/RequestMethod.h"
19 #include "MemBuf.h"
20 #include "SquidConfig.h"
21 #include "testHttp1Parser.h"
22 #include "unitTestMain.h"
23
24 CPPUNIT_TEST_SUITE_REGISTRATION( testHttp1Parser );
25
26 void
27 testHttp1Parser::globalSetup()
28 {
29 static bool setup_done = false;
30 if (setup_done)
31 return;
32
33 Mem::Init();
34 setup_done = true;
35
36 // default to strict parser. set for loose parsing specifically where behaviour differs.
37 Config.onoff.relaxed_header_parser = 0;
38
39 Config.maxRequestHeaderSize = 1024; // XXX: unit test the RequestParser handling of this limit
40 }
41
42 struct resultSet {
43 bool parsed;
44 bool needsMore;
45 Http1::ParseState parserState;
46 Http::StatusCode status;
47 SBuf::size_type suffixSz;
48 HttpRequestMethod method;
49 const char *uri;
50 AnyP::ProtocolVersion version;
51 };
52
53 // define SQUID_DEBUG_TESTS to see exactly which test sub-cases fail and where
54 #ifdef SQUID_DEBUG_TESTS
55 // not optimized for runtime use
56 static void
57 Replace(SBuf &where, const SBuf &what, const SBuf &with)
58 {
59 // prevent infinite loops
60 if (!what.length() || with.find(what) != SBuf::npos)
61 return;
62
63 SBuf::size_type pos = 0;
64 while ((pos = where.find(what, pos)) != SBuf::npos) {
65 SBuf buf = where.substr(0, pos);
66 buf.append(with);
67 buf.append(where.substr(pos+what.length()));
68 where = buf;
69 pos += with.length();
70 }
71 }
72
73 static SBuf Pretty(SBuf raw)
74 {
75 Replace(raw, SBuf("\r"), SBuf("\\r"));
76 Replace(raw, SBuf("\n"), SBuf("\\n"));
77 return raw;
78 }
79 #endif
80
81 static void
82 testResults(int line, const SBuf &input, Http1::RequestParser &output, struct resultSet &expect)
83 {
84 #ifdef SQUID_DEBUG_TESTS
85 std::cerr << "TEST @" << line << ", in=" << Pretty(input) << "\n";
86 #else
87 (void)line;
88 #endif
89
90 const bool parsed = output.parse(input);
91
92 #ifdef SQUID_DEBUG_TESTS
93 if (expect.parsed != parsed)
94 std::cerr << "\tparse-FAILED: " << expect.parsed << "!=" << parsed << "\n";
95 else if (parsed && expect.method != output.method_)
96 std::cerr << "\tmethod-FAILED: " << expect.method << "!=" << output.method_ << "\n";
97 if (expect.status != output.parseStatusCode)
98 std::cerr << "\tscode-FAILED: " << expect.status << "!=" << output.parseStatusCode << "\n";
99 if (expect.suffixSz != output.buf_.length())
100 std::cerr << "\tsuffixSz-FAILED: " << expect.suffixSz << "!=" << output.buf_.length() << "\n";
101 #endif
102
103 // runs the parse
104 CPPUNIT_ASSERT_EQUAL(expect.parsed, parsed);
105
106 // if parsing was successful, check easily visible field outputs
107 if (parsed) {
108 CPPUNIT_ASSERT_EQUAL(expect.method, output.method_);
109 if (expect.uri != nullptr)
110 CPPUNIT_ASSERT_EQUAL(0, output.uri_.cmp(expect.uri));
111 CPPUNIT_ASSERT_EQUAL(expect.version, output.msgProtocol_);
112 }
113
114 CPPUNIT_ASSERT_EQUAL(expect.status, output.parseStatusCode);
115
116 // check more obscure states
117 CPPUNIT_ASSERT_EQUAL(expect.needsMore, output.needsMoreData());
118 if (output.needsMoreData())
119 CPPUNIT_ASSERT_EQUAL(expect.parserState, output.parsingStage_);
120 CPPUNIT_ASSERT_EQUAL(expect.suffixSz, output.buf_.length());
121 }
122
123 void
124 testHttp1Parser::testParserConstruct()
125 {
126 // whether the constructor works
127 {
128 Http1::RequestParser output;
129 CPPUNIT_ASSERT_EQUAL(true, output.needsMoreData());
130 CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_NONE, output.parsingStage_);
131 CPPUNIT_ASSERT_EQUAL(Http::scNone, output.parseStatusCode); // XXX: clear() not being called.
132 CPPUNIT_ASSERT(output.buf_.isEmpty());
133 CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_NONE), output.method_);
134 CPPUNIT_ASSERT(output.uri_.isEmpty());
135 CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_);
136 }
137
138 // whether new() works
139 {
140 Http1::RequestParser *output = new Http1::RequestParser;
141 CPPUNIT_ASSERT_EQUAL(true, output->needsMoreData());
142 CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_NONE, output->parsingStage_);
143 CPPUNIT_ASSERT_EQUAL(Http::scNone, output->parseStatusCode);
144 CPPUNIT_ASSERT(output->buf_.isEmpty());
145 CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_NONE), output->method_);
146 CPPUNIT_ASSERT(output->uri_.isEmpty());
147 CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output->msgProtocol_);
148 delete output;
149 }
150 }
151
152 void
153 testHttp1Parser::testParseRequestLineProtocols()
154 {
155 // ensure MemPools etc exist
156 globalSetup();
157
158 SBuf input;
159 Http1::RequestParser output;
160
161 // TEST: Do we comply with RFC 1945 section 5.1 ?
162 // TEST: Do we comply with RFC 7230 sections 2.6, 3.1.1 and 3.5 ?
163
164 // RFC 1945 : HTTP/0.9 simple-request
165 {
166 input.append("GET /\r\n", 7);
167 struct resultSet expect = {
168 .parsed = true,
169 .needsMore = false,
170 .parserState = Http1::HTTP_PARSE_DONE,
171 .status = Http::scOkay,
172 .suffixSz = 0,
173 .method = HttpRequestMethod(Http::METHOD_GET),
174 .uri = "/",
175 .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9)
176 };
177 output.clear();
178 testResults(__LINE__, input, output, expect);
179 input.clear();
180 }
181
182 // RFC 1945 : invalid HTTP/0.9 simple-request (only GET is valid)
183 {
184 input.append("POST /\r\n", 8);
185 struct resultSet expect = {
186 .parsed = false,
187 .needsMore = false,
188 .parserState = Http1::HTTP_PARSE_DONE,
189 .status = Http::scBadRequest,
190 .suffixSz = input.length(),
191 .method = HttpRequestMethod(Http::METHOD_POST),
192 .uri = nullptr,
193 .version = AnyP::ProtocolVersion()
194 };
195 output.clear();
196 testResults(__LINE__, input, output, expect);
197 input.clear();
198 }
199
200 // RFC 1945 and 7230 : HTTP/1.0 request
201 {
202 input.append("GET / HTTP/1.0\r\n", 16);
203 struct resultSet expect = {
204 .parsed = false,
205 .needsMore = true,
206 .parserState = Http1::HTTP_PARSE_MIME,
207 .status = Http::scOkay,
208 .suffixSz = 0,
209 .method = HttpRequestMethod(Http::METHOD_GET),
210 .uri = "/",
211 .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,0)
212 };
213 output.clear();
214 testResults(__LINE__, input, output, expect);
215 input.clear();
216 }
217
218 // RFC 7230 : HTTP/1.1 request
219 {
220 input.append("GET / HTTP/1.1\r\n", 16);
221 struct resultSet expect = {
222 .parsed = false,
223 .needsMore = true,
224 .parserState = Http1::HTTP_PARSE_MIME,
225 .status = Http::scOkay,
226 .suffixSz = 0,
227 .method = HttpRequestMethod(Http::METHOD_GET),
228 .uri = "/",
229 .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
230 };
231 output.clear();
232 testResults(__LINE__, input, output, expect);
233 input.clear();
234 }
235
236 // RFC 7230 : future 1.x version full-request
237 {
238 input.append("GET / HTTP/1.2\r\n", 16);
239 struct resultSet expect = {
240 .parsed = false,
241 .needsMore = true,
242 .parserState = Http1::HTTP_PARSE_MIME,
243 .status = Http::scOkay,
244 .suffixSz = 0,
245 .method = HttpRequestMethod(Http::METHOD_GET),
246 .uri = "/",
247 .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,2)
248 };
249 output.clear();
250 testResults(__LINE__, input, output, expect);
251 input.clear();
252 }
253
254 // RFC 7230 : future versions do not use 1.x message syntax.
255 // However, it is still valid syntax for the single-digit forms
256 // to appear. The parser we are testing should accept them.
257 {
258 input.append("GET / HTTP/2.0\r\n", 16);
259 struct resultSet expectA = {
260 .parsed = true,
261 .needsMore = false,
262 .parserState = Http1::HTTP_PARSE_DONE,
263 .status = Http::scOkay,
264 .suffixSz = 0,
265 .method = HttpRequestMethod(Http::METHOD_GET),
266 .uri = "/",
267 .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,2,0)
268 };
269 output.clear();
270 testResults(__LINE__, input, output, expectA);
271 input.clear();
272
273 input.append("GET / HTTP/9.9\r\n", 16);
274 struct resultSet expectB = {
275 .parsed = true,
276 .needsMore = false,
277 .parserState = Http1::HTTP_PARSE_DONE,
278 .status = Http::scOkay,
279 .suffixSz = 0,
280 .method = HttpRequestMethod(Http::METHOD_GET),
281 .uri = "/",
282 .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,9,9)
283 };
284 output.clear();
285 testResults(__LINE__, input, output, expectB);
286 input.clear();
287 }
288
289 // RFC 7230 : future versions >= 10.0 are invalid syntax
290 {
291 input.append("GET / HTTP/10.12\r\n", 18);
292 struct resultSet expect = {
293 .parsed = false,
294 .needsMore = false,
295 .parserState = Http1::HTTP_PARSE_MIME,
296 .status = Http::scBadRequest,
297 .suffixSz = input.length(),
298 .method = HttpRequestMethod(Http::METHOD_GET),
299 .uri = "/",
300 .version = AnyP::ProtocolVersion()
301 };
302 output.clear();
303 testResults(__LINE__, input, output, expect);
304 input.clear();
305 }
306
307 // unknown non-HTTP protocol names
308 {
309 input.append("GET / FOO/1.0\r\n", 15);
310 struct resultSet expect = {
311 .parsed = false,
312 .needsMore = false,
313 .parserState = Http1::HTTP_PARSE_DONE,
314 .status = Http::scBadRequest,
315 .suffixSz = input.length(),
316 .method = HttpRequestMethod(Http::METHOD_GET),
317 .uri = "/",
318 .version = AnyP::ProtocolVersion()
319 };
320 output.clear();
321 testResults(__LINE__, input, output, expect);
322 input.clear();
323 }
324
325 // no version digits
326 {
327 input.append("GET / HTTP/\r\n", 13);
328 struct resultSet expect = {
329 .parsed = false,
330 .needsMore = false,
331 .parserState = Http1::HTTP_PARSE_DONE,
332 .status = Http::scBadRequest,
333 .suffixSz = input.length(),
334 .method = HttpRequestMethod(Http::METHOD_GET),
335 .uri = "/",
336 .version = AnyP::ProtocolVersion()
337 };
338 output.clear();
339 testResults(__LINE__, input, output, expect);
340 input.clear();
341 }
342
343 // no major version
344 {
345 input.append("GET / HTTP/.1\r\n", 15);
346 struct resultSet expect = {
347 .parsed = false,
348 .needsMore = false,
349 .parserState = Http1::HTTP_PARSE_DONE,
350 .status = Http::scBadRequest,
351 .suffixSz = input.length(),
352 .method = HttpRequestMethod(Http::METHOD_GET),
353 .uri = "/",
354 .version = AnyP::ProtocolVersion()
355 };
356 output.clear();
357 testResults(__LINE__, input, output, expect);
358 input.clear();
359 }
360
361 // no version dot
362 {
363 input.append("GET / HTTP/11\r\n", 15);
364 struct resultSet expect = {
365 .parsed = false,
366 .needsMore = false,
367 .parserState = Http1::HTTP_PARSE_DONE,
368 .status = Http::scBadRequest,
369 .suffixSz = input.length(),
370 .method = HttpRequestMethod(Http::METHOD_GET),
371 .uri = "/",
372 .version = AnyP::ProtocolVersion()
373 };
374 output.clear();
375 testResults(__LINE__, input, output, expect);
376 input.clear();
377 }
378
379 // negative major version (bug 3062)
380 {
381 input.append("GET / HTTP/-999999.1\r\n", 22);
382 struct resultSet expect = {
383 .parsed = false,
384 .needsMore = false,
385 .parserState = Http1::HTTP_PARSE_DONE,
386 .status = Http::scBadRequest,
387 .suffixSz = input.length(),
388 .method = HttpRequestMethod(Http::METHOD_GET),
389 .uri = "/",
390 .version = AnyP::ProtocolVersion()
391 };
392 output.clear();
393 testResults(__LINE__, input, output, expect);
394 input.clear();
395 }
396
397 // no minor version
398 {
399 input.append("GET / HTTP/1.\r\n", 15);
400 struct resultSet expect = {
401 .parsed = false,
402 .needsMore = false,
403 .parserState = Http1::HTTP_PARSE_DONE,
404 .status = Http::scBadRequest,
405 .suffixSz = input.length(),
406 .method = HttpRequestMethod(Http::METHOD_GET),
407 .uri = "/",
408 .version = AnyP::ProtocolVersion()
409 };
410 output.clear();
411 testResults(__LINE__, input, output, expect);
412 input.clear();
413 }
414
415 // negative major version (bug 3062 corollary)
416 {
417 input.append("GET / HTTP/1.-999999\r\n", 22);
418 struct resultSet expect = {
419 .parsed = false,
420 .needsMore = false,
421 .parserState = Http1::HTTP_PARSE_DONE,
422 .status = Http::scBadRequest,
423 .suffixSz = input.length(),
424 .method = HttpRequestMethod(Http::METHOD_GET),
425 .uri = "/",
426 .version = AnyP::ProtocolVersion()
427 };
428 output.clear();
429 testResults(__LINE__, input, output, expect);
430 input.clear();
431 }
432 }
433
434 void
435 testHttp1Parser::testParseRequestLineStrange()
436 {
437 // ensure MemPools etc exist
438 globalSetup();
439
440 SBuf input;
441 Http1::RequestParser output;
442
443 // space padded URL
444 {
445 input.append("GET / HTTP/1.1\r\n", 21);
446 // when being tolerant extra (sequential) SP delimiters are acceptable
447 Config.onoff.relaxed_header_parser = 1;
448 struct resultSet expect = {
449 .parsed = false,
450 .needsMore = true,
451 .parserState = Http1::HTTP_PARSE_MIME,
452 .status = Http::scOkay,
453 .suffixSz = 0,
454 .method = HttpRequestMethod(Http::METHOD_GET),
455 .uri = "/",
456 .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
457 };
458 output.clear();
459 testResults(__LINE__, input, output, expect);
460
461 Config.onoff.relaxed_header_parser = 0;
462 struct resultSet expectStrict = {
463 .parsed = false,
464 .needsMore = false,
465 .parserState = Http1::HTTP_PARSE_DONE,
466 .status = Http::scBadRequest,
467 .suffixSz = input.length(),
468 .method = HttpRequestMethod(),
469 .uri = nullptr,
470 .version = AnyP::ProtocolVersion()
471 };
472 output.clear();
473 testResults(__LINE__, input, output, expectStrict);
474 input.clear();
475 }
476
477 // whitespace inside URI. (nasty but happens)
478 {
479 input.append("GET /fo o/ HTTP/1.1\r\n", 21);
480 Config.onoff.relaxed_header_parser = 1;
481 struct resultSet expect = {
482 .parsed = false,
483 .needsMore = true,
484 .parserState = Http1::HTTP_PARSE_MIME,
485 .status = Http::scOkay,
486 .suffixSz = 0,
487 .method = HttpRequestMethod(Http::METHOD_GET),
488 .uri = "/fo o/",
489 .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
490 };
491 output.clear();
492 testResults(__LINE__, input, output, expect);
493
494 Config.onoff.relaxed_header_parser = 0;
495 struct resultSet expectStrict = {
496 .parsed = false,
497 .needsMore = false,
498 .parserState = Http1::HTTP_PARSE_DONE,
499 .status = Http::scBadRequest,
500 .suffixSz = input.length(),
501 .method = HttpRequestMethod(),
502 .uri = nullptr,
503 .version = AnyP::ProtocolVersion()
504 };
505 output.clear();
506 testResults(__LINE__, input, output, expectStrict);
507 input.clear();
508 }
509
510 // additional data in buffer
511 {
512 input.append("GET / HTTP/1.1\r\nboo!", 20);
513 struct resultSet expect = {
514 .parsed = false,
515 .needsMore = true,
516 .parserState = Http1::HTTP_PARSE_MIME,
517 .status = Http::scOkay,
518 .suffixSz = 4, // strlen("boo!")
519 .method = HttpRequestMethod(Http::METHOD_GET),
520 .uri = "/",
521 .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
522 };
523 output.clear();
524 testResults(__LINE__, input, output, expect);
525 input.clear();
526 Config.onoff.relaxed_header_parser = 0;
527 }
528 }
529
530 void
531 testHttp1Parser::testParseRequestLineTerminators()
532 {
533 // ensure MemPools etc exist
534 globalSetup();
535
536 SBuf input;
537 Http1::RequestParser output;
538
539 // alternative EOL sequence: NL-only
540 // RFC 7230 tolerance permits omitted CR
541 {
542 input.append("GET / HTTP/1.1\n", 15);
543 Config.onoff.relaxed_header_parser = 1;
544 struct resultSet expect = {
545 .parsed = false,
546 .needsMore = true,
547 .parserState = Http1::HTTP_PARSE_MIME,
548 .status = Http::scOkay,
549 .suffixSz = 0,
550 .method = HttpRequestMethod(Http::METHOD_GET),
551 .uri = "/",
552 .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
553 };
554 output.clear();
555 testResults(__LINE__, input, output, expect);
556
557 Config.onoff.relaxed_header_parser = 0;
558 struct resultSet expectStrict = {
559 .parsed = false,
560 .needsMore = false,
561 .parserState = Http1::HTTP_PARSE_DONE,
562 .status = Http::scBadRequest,
563 .suffixSz = input.length(),
564 .method = HttpRequestMethod(),
565 .uri = nullptr,
566 .version = AnyP::ProtocolVersion()
567 };
568 output.clear();
569 testResults(__LINE__, input, output, expectStrict);
570 input.clear();
571 }
572
573 // alternative EOL sequence: double-NL-only
574 // RFC 7230 tolerance permits omitted CR
575 // NP: represents a request with no mime headers
576 {
577 input.append("GET / HTTP/1.1\n\n", 16);
578 Config.onoff.relaxed_header_parser = 1;
579 struct resultSet expect = {
580 .parsed = true,
581 .needsMore = false,
582 .parserState = Http1::HTTP_PARSE_DONE,
583 .status = Http::scOkay,
584 .suffixSz = 0,
585 .method = HttpRequestMethod(Http::METHOD_GET),
586 .uri = "/",
587 .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
588 };
589 output.clear();
590 testResults(__LINE__, input, output, expect);
591
592 Config.onoff.relaxed_header_parser = 0;
593 struct resultSet expectStrict = {
594 .parsed = false,
595 .needsMore = false,
596 .parserState = Http1::HTTP_PARSE_DONE,
597 .status = Http::scBadRequest,
598 .suffixSz = input.length(),
599 .method = HttpRequestMethod(),
600 .uri = nullptr,
601 .version = AnyP::ProtocolVersion()
602 };
603 output.clear();
604 testResults(__LINE__, input, output, expectStrict);
605 input.clear();
606 }
607
608 // space padded version
609 {
610 // RFC 7230 specifies version is followed by CRLF. No intermediary bytes.
611 input.append("GET / HTTP/1.1 \r\n", 17);
612 struct resultSet expect = {
613 .parsed = false,
614 .needsMore = false,
615 .parserState = Http1::HTTP_PARSE_DONE,
616 .status = Http::scBadRequest,
617 .suffixSz = input.length(),
618 .method = HttpRequestMethod(),
619 .uri = nullptr,
620 .version = AnyP::ProtocolVersion()
621 };
622 output.clear();
623 testResults(__LINE__, input, output, expect);
624 input.clear();
625 }
626 }
627
628 void
629 testHttp1Parser::testParseRequestLineMethods()
630 {
631 // ensure MemPools etc exist
632 globalSetup();
633
634 SBuf input;
635 Http1::RequestParser output;
636
637 // RFC 7230 : dot method
638 {
639 input.append(". / HTTP/1.1\r\n", 14);
640 struct resultSet expect = {
641 .parsed = false,
642 .needsMore = true,
643 .parserState = Http1::HTTP_PARSE_MIME,
644 .status = Http::scOkay,
645 .suffixSz = 0,
646 .method = HttpRequestMethod(SBuf(".")),
647 .uri = "/",
648 .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
649 };
650 output.clear();
651 testResults(__LINE__, input, output, expect);
652 input.clear();
653 }
654
655 // RFC 7230 : special TCHAR method chars
656 {
657 input.append("!#$%&'*+-.^_`|~ / HTTP/1.1\r\n", 28);
658 struct resultSet expect = {
659 .parsed = false,
660 .needsMore = true,
661 .parserState = Http1::HTTP_PARSE_MIME,
662 .status = Http::scOkay,
663 .suffixSz = 0,
664 .method = HttpRequestMethod(SBuf("!#$%&'*+-.^_`|~")),
665 .uri = "/",
666 .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
667 };
668 output.clear();
669 testResults(__LINE__, input, output, expect);
670 input.clear();
671 }
672
673 // OPTIONS with * URL
674 {
675 input.append("OPTIONS * HTTP/1.1\r\n", 20);
676 struct resultSet expect = {
677 .parsed = false,
678 .needsMore = true,
679 .parserState = Http1::HTTP_PARSE_MIME,
680 .status = Http::scOkay,
681 .suffixSz = 0,
682 .method = HttpRequestMethod(Http::METHOD_OPTIONS),
683 .uri = "*",
684 .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
685 };
686 output.clear();
687 testResults(__LINE__, input, output, expect);
688 input.clear();
689 }
690
691 // unknown method
692 {
693 input.append("HELLOWORLD / HTTP/1.1\r\n", 23);
694 struct resultSet expect = {
695 .parsed = false,
696 .needsMore = true,
697 .parserState = Http1::HTTP_PARSE_MIME,
698 .status = Http::scOkay,
699 .suffixSz = 0,
700 .method = HttpRequestMethod(SBuf("HELLOWORLD")),
701 .uri = "/",
702 .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
703 };
704 output.clear();
705 testResults(__LINE__, input, output, expect);
706 input.clear();
707 }
708
709 // method-only
710 {
711 input.append("A\n", 2);
712 struct resultSet expect = {
713 .parsed = false,
714 .needsMore = false,
715 .parserState = Http1::HTTP_PARSE_DONE,
716 .status = Http::scBadRequest,
717 .suffixSz = input.length(),
718 .method = HttpRequestMethod(),
719 .uri = nullptr,
720 .version = AnyP::ProtocolVersion()
721 };
722 output.clear();
723 testResults(__LINE__, input, output, expect);
724 input.clear();
725 }
726
727 {
728 input.append("GET\n", 4);
729 struct resultSet expect = {
730 .parsed = false,
731 .needsMore = false,
732 .parserState = Http1::HTTP_PARSE_DONE,
733 .status = Http::scBadRequest,
734 .suffixSz = input.length(),
735 .method = HttpRequestMethod(),
736 .uri = nullptr,
737 .version = AnyP::ProtocolVersion()
738 };
739 output.clear();
740 testResults(__LINE__, input, output, expect);
741 input.clear();
742 }
743
744 // space padded method (SP is reserved so invalid as a method byte)
745 {
746 input.append(" GET / HTTP/1.1\r\n", 17);
747 struct resultSet expect = {
748 .parsed = false,
749 .needsMore = false,
750 .parserState = Http1::HTTP_PARSE_DONE,
751 .status = Http::scBadRequest,
752 .suffixSz = input.length(),
753 .method = HttpRequestMethod(),
754 .uri = nullptr,
755 .version = AnyP::ProtocolVersion()
756 };
757 output.clear();
758 testResults(__LINE__, input, output, expect);
759 input.clear();
760 }
761
762 // RFC 7230 defined tolerance: ignore empty line(s) prefix on messages
763 {
764 input.append("\r\n\r\n\nGET / HTTP/1.1\r\n", 21);
765 Config.onoff.relaxed_header_parser = 1;
766 struct resultSet expect = {
767 .parsed = false,
768 .needsMore = true,
769 .parserState = Http1::HTTP_PARSE_MIME,
770 .status = Http::scOkay,
771 .suffixSz = 0,
772 .method = HttpRequestMethod(Http::METHOD_GET),
773 .uri = "/",
774 .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
775 };
776 output.clear();
777 testResults(__LINE__, input, output, expect);
778
779 Config.onoff.relaxed_header_parser = 0;
780 struct resultSet expectStrict = {
781 .parsed = false,
782 .needsMore = false,
783 .parserState = Http1::HTTP_PARSE_DONE,
784 .status = Http::scBadRequest,
785 .suffixSz = input.length(),
786 .method = HttpRequestMethod(),
787 .uri = nullptr,
788 .version = AnyP::ProtocolVersion()
789 };
790 output.clear();
791 testResults(__LINE__, input, output, expectStrict);
792 input.clear();
793 }
794
795 // forbidden character in method
796 {
797 input.append("\tGET / HTTP/1.1\r\n", 17);
798 struct resultSet expect = {
799 .parsed = false,
800 .needsMore = false,
801 .parserState = Http1::HTTP_PARSE_DONE,
802 .status = Http::scBadRequest,
803 .suffixSz = input.length(),
804 .method = HttpRequestMethod(),
805 .uri = nullptr,
806 .version = AnyP::ProtocolVersion()
807 };
808 output.clear();
809 testResults(__LINE__, input, output, expect);
810 input.clear();
811 }
812
813 // CR in method delimiters
814 {
815 // RFC 7230 section 3.5 permits CR in whitespace but only for tolerant parsers
816 input.append("GET\r / HTTP/1.1\r\n", 17);
817 Config.onoff.relaxed_header_parser = 1;
818 struct resultSet expect = {
819 .parsed = false,
820 .needsMore = true,
821 .parserState = Http1::HTTP_PARSE_MIME,
822 .status = Http::scOkay,
823 .suffixSz = 0,
824 .method = HttpRequestMethod(Http::METHOD_GET),
825 .uri = "/",
826 .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
827 };
828 output.clear();
829 testResults(__LINE__, input, output, expect);
830
831 Config.onoff.relaxed_header_parser = 0;
832 struct resultSet expectStrict = {
833 .parsed = false,
834 .needsMore = false,
835 .parserState = Http1::HTTP_PARSE_DONE,
836 .status = Http::scBadRequest,
837 .suffixSz = input.length(),
838 .method = HttpRequestMethod(),
839 .uri = nullptr,
840 .version = AnyP::ProtocolVersion()
841 };
842 output.clear();
843 testResults(__LINE__, input, output, expectStrict);
844 input.clear();
845 }
846
847 // tolerant parser delimiters
848 {
849 // RFC 7230 section 3.5 permits certain binary characters as whitespace delimiters
850 input.append("GET\r\t\x0B\x0C / HTTP/1.1\r\n", 20);
851 Config.onoff.relaxed_header_parser = 1;
852 struct resultSet expect = {
853 .parsed = false,
854 .needsMore = true,
855 .parserState = Http1::HTTP_PARSE_MIME,
856 .status = Http::scOkay,
857 .suffixSz = 0,
858 .method = HttpRequestMethod(Http::METHOD_GET),
859 .uri = "/",
860 .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
861 };
862 output.clear();
863 testResults(__LINE__, input, output, expect);
864
865 Config.onoff.relaxed_header_parser = 0;
866 struct resultSet expectStrict = {
867 .parsed = false,
868 .needsMore = false,
869 .parserState = Http1::HTTP_PARSE_DONE,
870 .status = Http::scBadRequest,
871 .suffixSz = input.length(),
872 .method = HttpRequestMethod(),
873 .uri = nullptr,
874 .version = AnyP::ProtocolVersion()
875 };
876 output.clear();
877 testResults(__LINE__, input, output, expectStrict);
878 input.clear();
879 }
880 }
881
882 void
883 testHttp1Parser::testParseRequestLineInvalid()
884 {
885 // ensure MemPools etc exist
886 globalSetup();
887
888 SBuf input;
889 Http1::RequestParser output;
890
891 // no method (or method delimiter)
892 {
893 // HTTP/0.9 requires method to be "GET"
894 input.append("/ HTTP/1.0\n", 11);
895 struct resultSet expect = {
896 .parsed = false,
897 .needsMore = false,
898 .parserState = Http1::HTTP_PARSE_DONE,
899 .status = Http::scBadRequest,
900 .suffixSz = input.length(),
901 .method = HttpRequestMethod(),
902 .uri = nullptr,
903 .version = AnyP::ProtocolVersion()
904 };
905 output.clear();
906 testResults(__LINE__, input, output, expect);
907 input.clear();
908 }
909
910 // no method (with method delimiter)
911 {
912 input.append(" / HTTP/1.0\n", 12);
913 struct resultSet expectStrict = {
914 .parsed = false,
915 .needsMore = false,
916 .parserState = Http1::HTTP_PARSE_DONE,
917 .status = Http::scBadRequest,
918 .suffixSz = input.length(),
919 .method = HttpRequestMethod(),
920 .uri = nullptr,
921 .version = AnyP::ProtocolVersion()
922 };
923 output.clear();
924 testResults(__LINE__, input, output, expectStrict);
925 input.clear();
926 }
927
928 // binary code after method (invalid)
929 {
930 input.append("GET\x16 / HTTP/1.1\r\n", 17);
931 struct resultSet expect = {
932 .parsed = false,
933 .needsMore = false,
934 .parserState = Http1::HTTP_PARSE_DONE,
935 .status = Http::scBadRequest,
936 .suffixSz = input.length(),
937 .method = HttpRequestMethod(),
938 .uri = nullptr,
939 .version = AnyP::ProtocolVersion()
940 };
941 output.clear();
942 testResults(__LINE__, input, output, expect);
943 input.clear();
944 }
945
946 // binary code NUL! after method (always invalid)
947 {
948 input.append("GET\0 / HTTP/1.1\r\n", 17);
949 struct resultSet expect = {
950 .parsed = false,
951 .needsMore = false,
952 .parserState = Http1::HTTP_PARSE_DONE,
953 .status = Http::scBadRequest,
954 .suffixSz = input.length(),
955 .method = HttpRequestMethod(),
956 .uri = nullptr,
957 .version = AnyP::ProtocolVersion()
958 };
959 output.clear();
960 testResults(__LINE__, input, output, expect);
961 input.clear();
962 }
963
964 // Either an RFC 1945 HTTP/0.9 simple-request for an "HTTP/1.1" URI or
965 // an invalid (no URI) HTTP/1.1 request. We treat this as latter, naturally.
966 {
967 input.append("GET HTTP/1.1\r\n", 15);
968 Config.onoff.relaxed_header_parser = 1;
969 struct resultSet expect = {
970 .parsed = false,
971 .needsMore = false,
972 .parserState = Http1::HTTP_PARSE_DONE,
973 .status = Http::scBadRequest,
974 .suffixSz = input.length(),
975 .method = HttpRequestMethod(),
976 .uri = nullptr,
977 .version = AnyP::ProtocolVersion()
978 };
979 output.clear();
980 testResults(__LINE__, input, output, expect);
981
982 Config.onoff.relaxed_header_parser = 0;
983 struct resultSet expectStrict = {
984 .parsed = false,
985 .needsMore = false,
986 .parserState = Http1::HTTP_PARSE_DONE,
987 .status = Http::scBadRequest,
988 .suffixSz = input.length(),
989 .method = HttpRequestMethod(),
990 .uri = nullptr,
991 .version = AnyP::ProtocolVersion()
992 };
993 output.clear();
994 testResults(__LINE__, input, output, expectStrict);
995 input.clear();
996 }
997
998 // Either an RFC 1945 HTTP/0.9 simple-request for an "HTTP/1.1" URI or
999 // an invalid (no URI) HTTP/1.1 request. We treat this as latter, naturally.
1000 {
1001 input.append("GET HTTP/1.1\r\n", 14);
1002 struct resultSet expect = {
1003 .parsed = false,
1004 .needsMore = false,
1005 .parserState = Http1::HTTP_PARSE_DONE,
1006 .status = Http::scBadRequest,
1007 .suffixSz = input.length(),
1008 .method = HttpRequestMethod(),
1009 .uri = nullptr,
1010 .version = AnyP::ProtocolVersion()
1011 };
1012 output.clear();
1013 testResults(__LINE__, input, output, expect);
1014 input.clear();
1015 }
1016
1017 // binary line
1018 {
1019 input.append("\xB\xC\xE\xF\n", 5);
1020 struct resultSet expect = {
1021 .parsed = false,
1022 .needsMore = false,
1023 .parserState = Http1::HTTP_PARSE_DONE,
1024 .status = Http::scBadRequest,
1025 .suffixSz = input.length(),
1026 .method = HttpRequestMethod(),
1027 .uri = nullptr,
1028 .version = AnyP::ProtocolVersion()
1029 };
1030 output.clear();
1031 testResults(__LINE__, input, output, expect);
1032 input.clear();
1033 }
1034
1035 // mixed whitespace line
1036 {
1037 input.append("\t \t \t\n", 6);
1038 struct resultSet expect = {
1039 .parsed = false,
1040 .needsMore = false,
1041 .parserState = Http1::HTTP_PARSE_DONE,
1042 .status = Http::scBadRequest,
1043 .suffixSz = input.length(),
1044 .method = HttpRequestMethod(),
1045 .uri = nullptr,
1046 .version = AnyP::ProtocolVersion()
1047 };
1048 output.clear();
1049 testResults(__LINE__, input, output, expect);
1050 input.clear();
1051 }
1052
1053 // mixed whitespace line with CR
1054 {
1055 input.append("\r \t \n", 6);
1056 struct resultSet expect = {
1057 .parsed = false,
1058 .needsMore = false,
1059 .parserState = Http1::HTTP_PARSE_DONE,
1060 .status = Http::scBadRequest,
1061 .suffixSz = input.length(),
1062 .method = HttpRequestMethod(),
1063 .uri = nullptr,
1064 .version = AnyP::ProtocolVersion()
1065 };
1066 output.clear();
1067 testResults(__LINE__, input, output, expect);
1068 input.clear();
1069 }
1070 }
1071
1072 void
1073 testHttp1Parser::testDripFeed()
1074 {
1075 // Simulate a client drip-feeding Squid a few bytes at a time.
1076 // extend the size of the buffer from 0 bytes to full request length
1077 // calling the parser repeatedly as visible data grows.
1078
1079 SBuf data;
1080 data.append("\n\n\n\n\n\n\n\n\n\n\n\n", 12);
1081 SBuf::size_type garbageEnd = data.length();
1082 data.append("GET ", 4);
1083 data.append("http://example.com/ ", 20);
1084 data.append("HTTP/1.1\r\n", 10);
1085 SBuf::size_type reqLineEnd = data.length() - 1;
1086 data.append("Host: example.com\r\n\r\n", 21);
1087 SBuf::size_type mimeEnd = data.length() - 1;
1088 data.append("...", 3); // trailer to catch mime EOS errors.
1089
1090 SBuf ioBuf;
1091 Http1::RequestParser hp;
1092
1093 // start with strict and move on to relaxed
1094 Config.onoff.relaxed_header_parser = 2;
1095
1096 Config.maxRequestHeaderSize = 1024; // large enough to hold the test data.
1097
1098 do {
1099
1100 // state of things we expect right now
1101 struct resultSet expect = {
1102 .parsed = false,
1103 .needsMore = true,
1104 .parserState = Http1::HTTP_PARSE_NONE,
1105 .status = Http::scNone,
1106 .suffixSz = 0,
1107 .method = HttpRequestMethod(),
1108 .uri = nullptr,
1109 .version = AnyP::ProtocolVersion()
1110 };
1111
1112 ioBuf.clear(); // begins empty for each parser type
1113 hp.clear();
1114
1115 --Config.onoff.relaxed_header_parser;
1116
1117 for (SBuf::size_type pos = 0; pos <= data.length(); ++pos) {
1118
1119 // simulate reading one more byte
1120 ioBuf.append(data.substr(pos,1));
1121
1122 // strict does not permit the garbage prefix
1123 if (pos < garbageEnd && !Config.onoff.relaxed_header_parser) {
1124 ioBuf.clear();
1125 continue;
1126 }
1127
1128 // when the garbage is passed we expect to start seeing first-line bytes
1129 if (pos == garbageEnd)
1130 expect.parserState = Http1::HTTP_PARSE_FIRST;
1131
1132 // all points after garbage start to see accumulated bytes looking for end of current section
1133 if (pos >= garbageEnd)
1134 expect.suffixSz = ioBuf.length();
1135
1136 // at end of request line expect to see method, URI, version details
1137 // and switch to seeking Mime header section
1138 if (pos == reqLineEnd) {
1139 expect.parserState = Http1::HTTP_PARSE_MIME;
1140 expect.suffixSz = 0; // and a checkpoint buffer reset
1141 expect.status = Http::scOkay;
1142 expect.method = HttpRequestMethod(Http::METHOD_GET);
1143 expect.uri = "http://example.com/";
1144 expect.version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1);
1145 }
1146
1147 // one mime header is done we are expecting a new request
1148 // parse results say true and initial data is all gone from the buffer
1149 if (pos == mimeEnd) {
1150 expect.parsed = true;
1151 expect.needsMore = false;
1152 expect.suffixSz = 0; // and a checkpoint buffer reset
1153 }
1154
1155 testResults(__LINE__, ioBuf, hp, expect);
1156
1157 // sync the buffers like Squid does
1158 ioBuf = hp.remaining();
1159
1160 // Squid stops using the parser once it has parsed the first message.
1161 if (!hp.needsMoreData())
1162 break;
1163 }
1164
1165 } while (Config.onoff.relaxed_header_parser);
1166
1167 }
1168