testHttp1Parser.cc
Go to the documentation of this file.
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"
18#include "http/RequestMethod.h"
19#include "MemBuf.h"
20#include "SquidConfig.h"
21#include "testHttp1Parser.h"
22#include "unitTestMain.h"
23
25
26void
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.
38
39 Config.maxRequestHeaderSize = 1024; // XXX: unit test the RequestParser handling of this limit
40}
41
42struct resultSet {
43 bool parsed;
49 const char *uri;
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
56static void
57Replace(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
73static 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
81static void
82testResults(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
123void
125{
126 // whether the constructor works
127 {
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 {
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
152void
154{
155 // ensure MemPools etc exist
156 globalSetup();
157
158 SBuf input;
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,
174 .uri = "/",
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(),
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,
210 .uri = "/",
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,
228 .uri = "/",
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,
246 .uri = "/",
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,
266 .uri = "/",
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,
281 .uri = "/",
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(),
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(),
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(),
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(),
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(),
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(),
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(),
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(),
425 .uri = "/",
426 .version = AnyP::ProtocolVersion()
427 };
428 output.clear();
429 testResults(__LINE__, input, output, expect);
430 input.clear();
431 }
432}
433
434void
436{
437 // ensure MemPools etc exist
438 globalSetup();
439
440 SBuf input;
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
448 struct resultSet expect = {
449 .parsed = false,
450 .needsMore = true,
451 .parserState = Http1::HTTP_PARSE_MIME,
452 .status = Http::scOkay,
453 .suffixSz = 0,
455 .uri = "/",
457 };
458 output.clear();
459 testResults(__LINE__, input, output, expect);
460
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);
481 struct resultSet expect = {
482 .parsed = false,
483 .needsMore = true,
484 .parserState = Http1::HTTP_PARSE_MIME,
485 .status = Http::scOkay,
486 .suffixSz = 0,
488 .uri = "/fo o/",
490 };
491 output.clear();
492 testResults(__LINE__, input, output, expect);
493
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!")
520 .uri = "/",
522 };
523 output.clear();
524 testResults(__LINE__, input, output, expect);
525 input.clear();
527 }
528}
529
530void
532{
533 // ensure MemPools etc exist
534 globalSetup();
535
536 SBuf input;
538
539 // alternative EOL sequence: NL-only
540 // RFC 7230 tolerance permits omitted CR
541 {
542 input.append("GET / HTTP/1.1\n", 15);
544 struct resultSet expect = {
545 .parsed = false,
546 .needsMore = true,
547 .parserState = Http1::HTTP_PARSE_MIME,
548 .status = Http::scOkay,
549 .suffixSz = 0,
551 .uri = "/",
553 };
554 output.clear();
555 testResults(__LINE__, input, output, expect);
556
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);
579 struct resultSet expect = {
580 .parsed = true,
581 .needsMore = false,
582 .parserState = Http1::HTTP_PARSE_DONE,
583 .status = Http::scOkay,
584 .suffixSz = 0,
586 .uri = "/",
588 };
589 output.clear();
590 testResults(__LINE__, input, output, expect);
591
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
628void
630{
631 // ensure MemPools etc exist
632 globalSetup();
633
634 SBuf input;
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 = "/",
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 = "/",
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,
683 .uri = "*",
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 = "/",
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);
766 struct resultSet expect = {
767 .parsed = false,
768 .needsMore = true,
769 .parserState = Http1::HTTP_PARSE_MIME,
770 .status = Http::scOkay,
771 .suffixSz = 0,
773 .uri = "/",
775 };
776 output.clear();
777 testResults(__LINE__, input, output, expect);
778
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);
818 struct resultSet expect = {
819 .parsed = false,
820 .needsMore = true,
821 .parserState = Http1::HTTP_PARSE_MIME,
822 .status = Http::scOkay,
823 .suffixSz = 0,
825 .uri = "/",
827 };
828 output.clear();
829 testResults(__LINE__, input, output, expect);
830
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);
852 struct resultSet expect = {
853 .parsed = false,
854 .needsMore = true,
855 .parserState = Http1::HTTP_PARSE_MIME,
856 .status = Http::scOkay,
857 .suffixSz = 0,
859 .uri = "/",
861 };
862 output.clear();
863 testResults(__LINE__, input, output, expect);
864
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
882void
884{
885 // ensure MemPools etc exist
886 globalSetup();
887
888 SBuf input;
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);
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
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
1072void
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;
1092
1093 // start with strict and move on to relaxed
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
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)
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) {
1140 expect.suffixSz = 0; // and a checkpoint buffer reset
1141 expect.status = Http::scOkay;
1143 expect.uri = "http://example.com/";
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
1166
1167}
1168
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:154
SBuf buf_
bytes remaining to be parsed
Definition: Parser.h:148
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:151
bool needsMoreData() const
Definition: Parser.h:66
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
virtual bool parse(const SBuf &aBuf)
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::@111 onoff
size_t maxRequestHeaderSize
Definition: SquidConfig.h:132
int relaxed_header_parser
Definition: SquidConfig.h:313
void testParseRequestLineStrange()
void testParseRequestLineTerminators()
void testParseRequestLineInvalid()
void testParseRequestLineProtocols()
void testParseRequestLineMethods()
@ 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
static void testResults(int line, const SBuf &input, Http1::RequestParser &output, struct resultSet &expect)
CPPUNIT_TEST_SUITE_REGISTRATION(testHttp1Parser)

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors