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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors