1 #define SQUID_UNIT_TEST 1
4 #include <cppunit/TestAssert.h>
7 #define protected public
9 #include "testHttp1Parser.h"
10 #include "http/one/RequestParser.h"
11 #include "http/RequestMethod.h"
14 #include "SquidConfig.h"
15 #include "testHttp1Parser.h"
17 CPPUNIT_TEST_SUITE_REGISTRATION( testHttp1Parser
);
20 testHttp1Parser::globalSetup()
22 static bool setup_done
= false;
29 // default to strict parser. set for loose parsing specifically where behaviour differs.
30 Config
.onoff
.relaxed_header_parser
= 0;
32 Config
.maxRequestHeaderSize
= 1024; // XXX: unit test the RequestParser handling of this limit
38 Http1::ParseState parserState
;
39 Http::StatusCode status
;
42 SBuf::size_type suffixSz
;
45 HttpRequestMethod method
;
51 AnyP::ProtocolVersion version
;
55 testResults(int line
, const SBuf
&input
, Http1::RequestParser
&output
, struct resultSet
&expect
)
57 #if WHEN_TEST_DEBUG_IS_NEEDED
58 printf("TEST @%d, in=%u: " SQUIDSBUFPH
"\n", line
, input
.length(), SQUIDSBUFPRINT(input
));
61 CPPUNIT_ASSERT_EQUAL(expect
.parsed
, output
.parse(input
));
62 CPPUNIT_ASSERT_EQUAL(expect
.needsMore
, output
.needsMoreData());
63 if (output
.needsMoreData())
64 CPPUNIT_ASSERT_EQUAL(expect
.parserState
, output
.parsingStage_
);
65 CPPUNIT_ASSERT_EQUAL(expect
.status
, output
.request_parse_status
);
66 CPPUNIT_ASSERT_EQUAL(expect
.msgStart
, output
.req
.start
);
67 CPPUNIT_ASSERT_EQUAL(expect
.msgEnd
, output
.req
.end
);
68 CPPUNIT_ASSERT_EQUAL(expect
.suffixSz
, output
.buf_
.length());
69 CPPUNIT_ASSERT_EQUAL(expect
.methodStart
, output
.req
.m_start
);
70 CPPUNIT_ASSERT_EQUAL(expect
.methodEnd
, output
.req
.m_end
);
71 CPPUNIT_ASSERT_EQUAL(expect
.method
, output
.method_
);
72 CPPUNIT_ASSERT_EQUAL(expect
.uriStart
, output
.req
.u_start
);
73 CPPUNIT_ASSERT_EQUAL(expect
.uriEnd
, output
.req
.u_end
);
74 if (expect
.uri
!= NULL
)
75 CPPUNIT_ASSERT_EQUAL(0, output
.uri_
.cmp(expect
.uri
));
76 CPPUNIT_ASSERT_EQUAL(expect
.versionStart
, output
.req
.v_start
);
77 CPPUNIT_ASSERT_EQUAL(expect
.versionEnd
, output
.req
.v_end
);
78 CPPUNIT_ASSERT_EQUAL(expect
.version
, output
.msgProtocol_
);
82 testHttp1Parser::testParserConstruct()
84 // whether the constructor works
86 Http1::RequestParser output
;
87 CPPUNIT_ASSERT_EQUAL(true, output
.needsMoreData());
88 CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_NONE
, output
.parsingStage_
);
89 CPPUNIT_ASSERT_EQUAL(Http::scNone
, output
.request_parse_status
); // XXX: clear() not being called.
90 CPPUNIT_ASSERT_EQUAL(-1, output
.req
.start
);
91 CPPUNIT_ASSERT_EQUAL(-1, output
.req
.end
);
92 CPPUNIT_ASSERT(output
.buf_
.isEmpty());
93 CPPUNIT_ASSERT_EQUAL(-1, output
.req
.m_start
);
94 CPPUNIT_ASSERT_EQUAL(-1, output
.req
.m_end
);
95 CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_NONE
), output
.method_
);
96 CPPUNIT_ASSERT_EQUAL(-1, output
.req
.u_start
);
97 CPPUNIT_ASSERT_EQUAL(-1, output
.req
.u_end
);
98 CPPUNIT_ASSERT(output
.uri_
.isEmpty());
99 CPPUNIT_ASSERT_EQUAL(-1, output
.req
.v_start
);
100 CPPUNIT_ASSERT_EQUAL(-1, output
.req
.v_end
);
101 CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output
.msgProtocol_
);
104 // whether new() works
106 Http1::RequestParser
*output
= new Http1::RequestParser
;
107 CPPUNIT_ASSERT_EQUAL(true, output
->needsMoreData());
108 CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_NONE
, output
->parsingStage_
);
109 CPPUNIT_ASSERT_EQUAL(Http::scNone
, output
->request_parse_status
);
110 CPPUNIT_ASSERT_EQUAL(-1, output
->req
.start
);
111 CPPUNIT_ASSERT_EQUAL(-1, output
->req
.end
);
112 CPPUNIT_ASSERT(output
->buf_
.isEmpty());
113 CPPUNIT_ASSERT_EQUAL(-1, output
->req
.m_start
);
114 CPPUNIT_ASSERT_EQUAL(-1, output
->req
.m_end
);
115 CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_NONE
), output
->method_
);
116 CPPUNIT_ASSERT_EQUAL(-1, output
->req
.u_start
);
117 CPPUNIT_ASSERT_EQUAL(-1, output
->req
.u_end
);
118 CPPUNIT_ASSERT(output
->uri_
.isEmpty());
119 CPPUNIT_ASSERT_EQUAL(-1, output
->req
.v_start
);
120 CPPUNIT_ASSERT_EQUAL(-1, output
->req
.v_end
);
121 CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output
->msgProtocol_
);
127 testHttp1Parser::testParseRequestLineProtocols()
129 // ensure MemPools etc exist
133 Http1::RequestParser output
;
135 // TEST: Do we comply with RFC 1945 section 5.1 ?
136 // TEST: Do we comply with RFC 2616 section 5.1 ?
138 // RFC 1945 : HTTP/0.9 simple-request
140 input
.append("GET /\r\n", 7);
141 struct resultSet expect
= {
144 .parserState
= Http1::HTTP_PARSE_DONE
,
145 .status
= Http::scOkay
,
147 .msgEnd
= (int)input
.length()-1,
151 .method
= HttpRequestMethod(Http::METHOD_GET
),
157 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,0,9)
160 testResults(__LINE__
, input
, output
, expect
);
164 // RFC 1945 : invalid HTTP/0.9 simple-request (only GET is valid)
165 #if WHEN_RFC_COMPLIANT
167 input
.append("POST /\r\n", 7);
168 struct resultSet expect
= {
171 .parserState
= Http1::HTTP_PARSE_DONE
,
172 .status
= Http::scOkay
,
174 .msgEnd
= (int)input
.length()-1,
178 .method
= HttpRequestMethod(Http::METHOD_POST
),
184 .version
= AnyP::ProtocolVersion()
187 testResults(__LINE__
, input
, output
, expect
);
191 // RFC 1945 and 2616 : HTTP/1.0 request
193 input
.append("GET / HTTP/1.0\r\n", 16);
194 struct resultSet expect
= {
197 .parserState
= Http1::HTTP_PARSE_MIME
,
198 .status
= Http::scOkay
,
200 .msgEnd
= (int)input
.length()-1,
204 .method
= HttpRequestMethod(Http::METHOD_GET
),
210 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,0)
213 testResults(__LINE__
, input
, output
, expect
);
217 // RFC 2616 : HTTP/1.1 request
219 input
.append("GET / HTTP/1.1\r\n", 16);
220 struct resultSet expect
= {
223 .parserState
= Http1::HTTP_PARSE_MIME
,
224 .status
= Http::scOkay
,
226 .msgEnd
= (int)input
.length()-1,
230 .method
= HttpRequestMethod(Http::METHOD_GET
),
236 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
239 testResults(__LINE__
, input
, output
, expect
);
243 // RFC 2616 : future version full-request
245 input
.append("GET / HTTP/1.2\r\n", 16);
246 struct resultSet expect
= {
249 .parserState
= Http1::HTTP_PARSE_MIME
,
250 .status
= Http::scOkay
,
252 .msgEnd
= (int)input
.length()-1,
256 .method
= HttpRequestMethod(Http::METHOD_GET
),
262 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,2)
265 testResults(__LINE__
, input
, output
, expect
);
269 // RFC 7230 : future versions do not use request-line syntax
271 input
.append("GET / HTTP/10.12\r\n", 18);
272 struct resultSet expect
= {
275 .parserState
= Http1::HTTP_PARSE_MIME
,
276 .status
= Http::scHttpVersionNotSupported
,
278 .msgEnd
= (int)input
.length()-1,
279 .suffixSz
= input
.length(),
282 .method
= HttpRequestMethod(Http::METHOD_GET
),
288 .version
= AnyP::ProtocolVersion()
291 testResults(__LINE__
, input
, output
, expect
);
295 // unknown non-HTTP protocol names
297 input
.append("GET / FOO/1.0\n", 14);
298 struct resultSet expect
= {
301 .parserState
= Http1::HTTP_PARSE_DONE
,
302 .status
= Http::scHttpVersionNotSupported
,
304 .msgEnd
= (int)input
.length()-1,
305 .suffixSz
= input
.length(),
308 .method
= HttpRequestMethod(Http::METHOD_GET
),
314 .version
= AnyP::ProtocolVersion()
317 testResults(__LINE__
, input
, output
, expect
);
323 input
.append("GET / HTTP/\n", 12);
324 struct resultSet expect
= {
327 .parserState
= Http1::HTTP_PARSE_DONE
,
328 .status
= Http::scHttpVersionNotSupported
,
330 .msgEnd
= (int)input
.length()-1,
331 .suffixSz
= input
.length(),
334 .method
= HttpRequestMethod(Http::METHOD_GET
),
340 .version
= AnyP::ProtocolVersion()
343 testResults(__LINE__
, input
, output
, expect
);
349 input
.append("GET / HTTP/.1\n", 14);
350 struct resultSet expect
= {
353 .parserState
= Http1::HTTP_PARSE_DONE
,
354 .status
= Http::scHttpVersionNotSupported
,
356 .msgEnd
= (int)input
.length()-1,
357 .suffixSz
= input
.length(),
360 .method
= HttpRequestMethod(Http::METHOD_GET
),
366 .version
= AnyP::ProtocolVersion()
369 testResults(__LINE__
, input
, output
, expect
);
375 input
.append("GET / HTTP/11\n", 14);
376 struct resultSet expect
= {
379 .parserState
= Http1::HTTP_PARSE_DONE
,
380 .status
= Http::scHttpVersionNotSupported
,
382 .msgEnd
= (int)input
.length()-1,
383 .suffixSz
= input
.length(),
386 .method
= HttpRequestMethod(Http::METHOD_GET
),
392 .version
= AnyP::ProtocolVersion()
395 testResults(__LINE__
, input
, output
, expect
);
399 // negative major version (bug 3062)
401 input
.append("GET / HTTP/-999999.1\n", 21);
402 struct resultSet expect
= {
405 .parserState
= Http1::HTTP_PARSE_DONE
,
406 .status
= Http::scHttpVersionNotSupported
,
408 .msgEnd
= (int)input
.length()-1,
409 .suffixSz
= input
.length(),
412 .method
= HttpRequestMethod(Http::METHOD_GET
),
418 .version
= AnyP::ProtocolVersion()
421 testResults(__LINE__
, input
, output
, expect
);
427 input
.append("GET / HTTP/1.\n", 14);
428 struct resultSet expect
= {
431 .parserState
= Http1::HTTP_PARSE_DONE
,
432 .status
= Http::scHttpVersionNotSupported
,
434 .msgEnd
= (int)input
.length()-1,
435 .suffixSz
= input
.length(),
438 .method
= HttpRequestMethod(Http::METHOD_GET
),
444 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,0)
447 testResults(__LINE__
, input
, output
, expect
);
451 // negative major version (bug 3062 corollary)
453 input
.append("GET / HTTP/1.-999999\n", 21);
454 struct resultSet expect
= {
457 .parserState
= Http1::HTTP_PARSE_DONE
,
458 .status
= Http::scHttpVersionNotSupported
,
460 .msgEnd
= (int)input
.length()-1,
461 .suffixSz
= input
.length(),
464 .method
= HttpRequestMethod(Http::METHOD_GET
),
470 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,0)
473 testResults(__LINE__
, input
, output
, expect
);
479 testHttp1Parser::testParseRequestLineStrange()
481 // ensure MemPools etc exist
485 Http1::RequestParser output
;
489 input
.append("GET / HTTP/1.1\r\n", 21);
490 struct resultSet expect
= {
493 .parserState
= Http1::HTTP_PARSE_MIME
,
494 .status
= Http::scOkay
,
496 .msgEnd
= (int)input
.length()-1,
500 .method
= HttpRequestMethod(Http::METHOD_GET
),
506 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
509 testResults(__LINE__
, input
, output
, expect
);
513 // whitespace inside URI. (nasty but happens)
514 // XXX: depends on tolerant parser...
516 input
.append("GET /fo o/ HTTP/1.1\n", 20);
517 struct resultSet expect
= {
520 .parserState
= Http1::HTTP_PARSE_MIME
,
521 .status
= Http::scOkay
,
523 .msgEnd
= (int)input
.length()-1,
527 .method
= HttpRequestMethod(Http::METHOD_GET
),
533 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
536 testResults(__LINE__
, input
, output
, expect
);
540 // additional data in buffer
542 input
.append("GET / HTTP/1.1\nboo!", 23);
543 struct resultSet expect
= {
546 .parserState
= Http1::HTTP_PARSE_MIME
,
547 .status
= Http::scOkay
,
549 .msgEnd
= (int)input
.length()-5,
550 .suffixSz
= 4, // strlen("boo!")
553 .method
= HttpRequestMethod(Http::METHOD_GET
),
559 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
562 testResults(__LINE__
, input
, output
, expect
);
568 testHttp1Parser::testParseRequestLineTerminators()
570 // ensure MemPools etc exist
574 Http1::RequestParser output
;
576 // alternative EOL sequence: NL-only
578 input
.append("GET / HTTP/1.1\n", 15);
579 struct resultSet expect
= {
582 .parserState
= Http1::HTTP_PARSE_MIME
,
583 .status
= Http::scOkay
,
585 .msgEnd
= (int)input
.length()-1,
589 .method
= HttpRequestMethod(Http::METHOD_GET
),
595 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
598 testResults(__LINE__
, input
, output
, expect
);
602 // alternative EOL sequence: double-NL-only
604 input
.append("GET / HTTP/1.1\n\n", 16);
605 struct resultSet expect
= {
608 .parserState
= Http1::HTTP_PARSE_DONE
,
609 .status
= Http::scOkay
,
611 .msgEnd
= (int)input
.length()-2,
615 .method
= HttpRequestMethod(Http::METHOD_GET
),
621 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
624 testResults(__LINE__
, input
, output
, expect
);
628 // alternative EOL sequence: multi-CR-NL
630 input
.append("GET / HTTP/1.1\r\r\r\n", 18);
631 // Being tolerant we can ignore and elide these apparently benign CR
632 Config
.onoff
.relaxed_header_parser
= 1;
633 struct resultSet expectRelaxed
= {
636 .parserState
= Http1::HTTP_PARSE_MIME
,
637 .status
= Http::scOkay
,
639 .msgEnd
= (int)input
.length()-1,
643 .method
= HttpRequestMethod(Http::METHOD_GET
),
649 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
652 testResults(__LINE__
, input
, output
, expectRelaxed
);
654 // strict mode treats these as several bare-CR in the request line which is explicitly invalid.
655 Config
.onoff
.relaxed_header_parser
= 0;
656 struct resultSet expectStrict
= {
659 .parserState
= Http1::HTTP_PARSE_MIME
,
660 .status
= Http::scBadRequest
,
663 .suffixSz
= input
.length(),
666 .method
= HttpRequestMethod(),
672 .version
= AnyP::ProtocolVersion()
675 testResults(__LINE__
, input
, output
, expectStrict
);
679 // space padded version
681 // RFC 1945 and 2616 specify version is followed by CRLF. No intermediary bytes.
682 // NP: the terminal whitespace is a special case: invalid for even HTTP/0.9 with no version tag
683 input
.append("GET / HTTP/1.1 \n", 16);
684 struct resultSet expect
= {
687 .parserState
= Http1::HTTP_PARSE_DONE
,
688 .status
= Http::scBadRequest
,
690 .msgEnd
= (int)input
.length()-1,
691 .suffixSz
= input
.length(),
694 .method
= HttpRequestMethod(Http::METHOD_GET
),
700 .version
= AnyP::ProtocolVersion()
703 testResults(__LINE__
, input
, output
, expect
);
709 testHttp1Parser::testParseRequestLineMethods()
711 // ensure MemPools etc exist
715 Http1::RequestParser output
;
717 // RFC 2616 : . method
719 input
.append(". / HTTP/1.1\n", 13);
720 struct resultSet expect
= {
723 .parserState
= Http1::HTTP_PARSE_MIME
,
724 .status
= Http::scOkay
,
726 .msgEnd
= (int)input
.length()-1,
730 .method
= HttpRequestMethod("."),
736 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
739 testResults(__LINE__
, input
, output
, expect
);
743 // OPTIONS with * URL
745 input
.append("OPTIONS * HTTP/1.1\n", 19);
746 struct resultSet expect
= {
749 .parserState
= Http1::HTTP_PARSE_MIME
,
750 .status
= Http::scOkay
,
752 .msgEnd
= (int)input
.length()-1,
756 .method
= HttpRequestMethod(Http::METHOD_OPTIONS
),
762 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
765 testResults(__LINE__
, input
, output
, expect
);
771 input
.append("HELLOWORLD / HTTP/1.1\n", 22);
772 struct resultSet expect
= {
775 .parserState
= Http1::HTTP_PARSE_MIME
,
776 .status
= Http::scOkay
,
778 .msgEnd
= (int)input
.length()-1,
782 .method
= HttpRequestMethod("HELLOWORLD"),
788 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
791 testResults(__LINE__
, input
, output
, expect
);
797 input
.append("A\n", 2);
798 struct resultSet expect
= {
801 .parserState
= Http1::HTTP_PARSE_DONE
,
802 .status
= Http::scBadRequest
,
804 .msgEnd
= (int)input
.length()-1,
805 .suffixSz
= input
.length(),
808 .method
= HttpRequestMethod(),
814 .version
= AnyP::ProtocolVersion()
817 testResults(__LINE__
, input
, output
, expect
);
822 input
.append("GET\n", 4);
823 struct resultSet expect
= {
826 .parserState
= Http1::HTTP_PARSE_DONE
,
827 .status
= Http::scBadRequest
,
829 .msgEnd
= (int)input
.length()-1,
830 .suffixSz
= input
.length(),
833 .method
= HttpRequestMethod(),
839 .version
= AnyP::ProtocolVersion()
842 testResults(__LINE__
, input
, output
, expect
);
846 // space padded method (in strict mode SP is reserved so invalid as a method byte)
848 input
.append(" GET / HTTP/1.1\n", 16);
849 // RELAXED mode Squid custom tolerance ignores SP
850 #if USE_HTTP_VIOLATIONS
851 Config
.onoff
.relaxed_header_parser
= 1;
852 struct resultSet expectRelaxed
= {
855 .parserState
= Http1::HTTP_PARSE_MIME
,
856 .status
= Http::scOkay
,
857 .msgStart
= 0, // garbage collection consumes the SP
858 .msgEnd
= (int)input
.length()-2,
862 .method
= HttpRequestMethod(Http::METHOD_GET
),
868 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
871 testResults(__LINE__
, input
, output
, expectRelaxed
);
874 // STRICT mode obeys RFC syntax
875 Config
.onoff
.relaxed_header_parser
= 0;
876 struct resultSet expectStrict
= {
879 .parserState
= Http1::HTTP_PARSE_DONE
,
880 .status
= Http::scBadRequest
,
882 .msgEnd
= (int)input
.length()-1,
883 .suffixSz
= input
.length(),
886 .method
= HttpRequestMethod(),
892 .version
= AnyP::ProtocolVersion()
895 testResults(__LINE__
, input
, output
, expectStrict
);
899 // RFC 2616 defined tolerance: ignore empty line(s) prefix on messages
900 #if WHEN_RFC_COMPLIANT
902 input
.append("\r\n\r\n\nGET / HTTP/1.1\r\n", 21);
903 struct resultSet expect
= {
906 .parserState
= Http1::HTTP_PARSE_MIME
,
907 .status
= Http::scOkay
,
909 .msgEnd
= (int)input
.length()-1,
913 .method
= HttpRequestMethod(Http::METHOD_GET
),
919 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
922 testResults(__LINE__
, input
, output
, expect
);
927 // tab padded method (NP: tab is not SP so treated as any other binary)
929 input
.append("\tGET / HTTP/1.1\n", 16);
930 #if WHEN_RFC_COMPLIANT
931 struct resultSet expect
= {
934 .parserState
= Http1::HTTP_PARSE_DONE
,
935 .status
= Http::scBadRequest
,
937 .msgEnd
= (int)input
.length()-1,
938 .suffixSz
= input
.length(),
941 .method
= HttpRequestMethod(),
947 .version
= AnyP::ProtocolVersion()
949 #else // XXX: currently broken
950 struct resultSet expect
= {
953 .parserState
= Http1::HTTP_PARSE_MIME
,
954 .status
= Http::scOkay
,
955 .msgStart
= 0, // garbage collection consumes the SP
956 .msgEnd
= (int)input
.length()-1,
960 .method
= HttpRequestMethod(SBuf("\tGET")),
966 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
970 testResults(__LINE__
, input
, output
, expect
);
976 testHttp1Parser::testParseRequestLineInvalid()
978 // ensure MemPools etc exist
982 Http1::RequestParser output
;
984 // no method (but in a form which is ambiguous with HTTP/0.9 simple-request)
986 // XXX: HTTP/0.9 requires method to be "GET"
987 input
.append("/ HTTP/1.0\n", 11);
988 struct resultSet expect
= {
991 .parserState
= Http1::HTTP_PARSE_MIME
,
992 .status
= Http::scOkay
,
994 .msgEnd
= (int)input
.length()-1,
998 .method
= HttpRequestMethod("/"),
1004 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,0,9)
1007 testResults(__LINE__
, input
, output
, expect
);
1011 // no method (an invalid format)
1013 input
.append(" / HTTP/1.0\n", 12);
1015 // XXX: squid custom tolerance consumes initial SP.
1016 Config
.onoff
.relaxed_header_parser
= 1;
1017 struct resultSet expectRelaxed
= {
1020 .parserState
= Http1::HTTP_PARSE_MIME
,
1021 .status
= Http::scOkay
,
1023 .msgEnd
= (int)input
.length()-2,
1027 .method
= HttpRequestMethod("/"),
1033 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,0,9)
1036 testResults(__LINE__
, input
, output
, expectRelaxed
);
1038 // STRICT detect as invalid
1039 Config
.onoff
.relaxed_header_parser
= 0;
1040 #if WHEN_RFC_COMPLIANT
1041 // XXX: except Squid does not
1042 struct resultSet expectStrict
= {
1045 .parserState
= Http1::HTTP_PARSE_DONE
,
1046 .status
= Http::scBadRequest
,
1048 .msgEnd
= (int)input
.length()-1,
1052 .method
= HttpRequestMethod(),
1058 .version
= AnyP::ProtocolVersion()
1061 struct resultSet expectStrict
= {
1064 .parserState
= Http1::HTTP_PARSE_DONE
,
1065 .status
= Http::scBadRequest
,
1067 .msgEnd
= (int)input
.length()-1,
1068 .suffixSz
= input
.length(),
1071 .method
= HttpRequestMethod(),
1077 .version
= AnyP::ProtocolVersion()
1081 testResults(__LINE__
, input
, output
, expectStrict
);
1085 // binary code in method (invalid)
1087 input
.append("GET\x0B / HTTP/1.1\n", 16);
1088 #if WHEN_RFC_COMPLIANT
1089 struct resultSet expect
= {
1092 .parserState
= Http1::HTTP_PARSE_DONE
,
1093 .status
= Http::scBadRequest
,
1095 .msgEnd
= (int)input
.length()-1,
1096 .suffixSz
= input
.length(),
1099 .method
= HttpRequestMethod(),
1105 .version
= AnyP::ProtocolVersion()
1108 struct resultSet expect
= {
1111 .parserState
= Http1::HTTP_PARSE_MIME
,
1112 .status
= Http::scOkay
,
1113 .msgStart
= 0, // garbage collection consumes the SP
1114 .msgEnd
= (int)input
.length()-1,
1118 .method
= HttpRequestMethod(SBuf("GET\x0B")),
1124 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
1128 testResults(__LINE__
, input
, output
, expect
);
1134 // RFC 2616 sec 5.1 prohibits CR other than in terminator.
1135 input
.append("GET\r / HTTP/1.1\r\n", 16);
1136 struct resultSet expect
= {
1139 .parserState
= Http1::HTTP_PARSE_DONE
,
1140 .status
= Http::scBadRequest
,
1142 .msgEnd
= -1, // halt at the first \r
1143 .suffixSz
= input
.length(),
1146 .method
= HttpRequestMethod(),
1152 .version
= AnyP::ProtocolVersion()
1155 testResults(__LINE__
, input
, output
, expect
);
1159 // binary code NUL! in method (strange but ...)
1161 input
.append("GET\0 / HTTP/1.1\n", 16);
1162 #if WHEN_RFC_COMPLIANT
1163 struct resultSet expect
= {
1166 .parserState
= Http1::HTTP_PARSE_DONE
,
1167 .status
= Http::scBadRequest
,
1169 .msgEnd
= -1, // halt at the \0
1170 .suffixSz
= input
.length(),
1173 .method
= HttpRequestMethod(),
1179 .version
= AnyP::ProtocolVersion()
1182 struct resultSet expect
= {
1185 .parserState
= Http1::HTTP_PARSE_MIME
,
1186 .status
= Http::scOkay
,
1188 .msgEnd
= (int)input
.length()-1,
1192 .method
= HttpRequestMethod(SBuf("GET\0",4)),
1198 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
1202 testResults(__LINE__
, input
, output
, expect
);
1206 // no URL (grammer invalid, ambiguous with RFC 1945 HTTP/0.9 simple-request)
1208 input
.append("GET HTTP/1.1\n", 14);
1209 struct resultSet expect
= {
1212 .parserState
= Http1::HTTP_PARSE_DONE
,
1213 .status
= Http::scOkay
,
1215 .msgEnd
= (int)input
.length()-1,
1219 .method
= HttpRequestMethod(Http::METHOD_GET
),
1225 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,0,9)
1228 testResults(__LINE__
, input
, output
, expect
);
1232 // no URL (grammer invalid, ambiguous with RFC 1945 HTTP/0.9 simple-request)
1234 input
.append("GET HTTP/1.1\n", 13);
1235 struct resultSet expect
= {
1238 .parserState
= Http1::HTTP_PARSE_DONE
,
1239 .status
= Http::scOkay
,
1241 .msgEnd
= (int)input
.length()-1,
1245 .method
= HttpRequestMethod(Http::METHOD_GET
),
1251 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,0,9)
1254 testResults(__LINE__
, input
, output
, expect
);
1260 input
.append("\xB\xC\xE\xF\n", 5);
1261 struct resultSet expect
= {
1264 .parserState
= Http1::HTTP_PARSE_DONE
,
1265 .status
= Http::scBadRequest
,
1267 .msgEnd
= (int)input
.length()-1,
1268 .suffixSz
= input
.length(),
1271 .method
= HttpRequestMethod(),
1277 .version
= AnyP::ProtocolVersion()
1280 testResults(__LINE__
, input
, output
, expect
);
1284 // mixed whitespace line
1286 // We accept non-space binary bytes for method so first \t shows up as that
1287 // but remaining space and tabs are skipped searching for URI-start
1288 input
.append("\t \t \t\n", 6);
1289 struct resultSet expect
= {
1292 .parserState
= Http1::HTTP_PARSE_DONE
,
1293 .status
= Http::scBadRequest
,
1295 .msgEnd
= (int)input
.length()-1,
1296 .suffixSz
= input
.length(),
1299 .method
= HttpRequestMethod(SBuf("\t")),
1305 .version
= AnyP::ProtocolVersion()
1308 testResults(__LINE__
, input
, output
, expect
);
1312 // mixed whitespace line with CR middle
1314 // CR aborts on sight, so even initial \t method is not marked as above
1315 // (not when parsing clean with whole line available anyway)
1316 input
.append("\t \r \n", 6);
1317 struct resultSet expect
= {
1320 .parserState
= Http1::HTTP_PARSE_DONE
,
1321 .status
= Http::scBadRequest
,
1323 .msgEnd
= -1, // halt on the \r
1324 .suffixSz
= input
.length(),
1327 .method
= HttpRequestMethod(),
1333 .version
= AnyP::ProtocolVersion()
1336 testResults(__LINE__
, input
, output
, expect
);
1342 testHttp1Parser::testDripFeed()
1344 // Simulate a client drip-feeding Squid a few bytes at a time.
1345 // extend the size of the buffer from 0 bytes to full request length
1346 // calling the parser repeatedly as visible data grows.
1349 data
.append(" ", 12);
1350 SBuf::size_type garbageEnd
= data
.length();
1351 data
.append("GET http://example.com/ HTTP/1.1\r\n", 34);
1352 SBuf::size_type reqLineEnd
= data
.length() - 1;
1353 data
.append("Host: example.com\r\n\r\n", 21);
1354 SBuf::size_type mimeEnd
= data
.length() - 1;
1355 data
.append("...", 3); // trailer to catch mime EOS errors.
1357 SBuf ioBuf
; // begins empty
1358 Http1::RequestParser hp
;
1360 // only relaxed parser accepts the garbage whitespace
1361 Config
.onoff
.relaxed_header_parser
= 1;
1363 // state of things we expect right now
1364 struct resultSet expect
= {
1367 .parserState
= Http1::HTTP_PARSE_NONE
,
1368 .status
= Http::scNone
,
1374 .method
= HttpRequestMethod(),
1380 .version
= AnyP::ProtocolVersion()
1383 Config
.maxRequestHeaderSize
= 1024; // large enough to hold the test data.
1385 for (SBuf::size_type pos
= 0; pos
<= data
.length(); ++pos
) {
1387 // simulate reading one more byte
1388 ioBuf
.append(data
.substr(pos
,1));
1390 // when the garbage is passed we expect to start seeing first-line bytes
1391 if (pos
== garbageEnd
) {
1392 expect
.parserState
= Http1::HTTP_PARSE_FIRST
;
1393 expect
.msgStart
= 0;
1396 // all points after garbage start to see accumulated bytes looking for end of current section
1397 if (pos
>= garbageEnd
)
1398 expect
.suffixSz
= ioBuf
.length();
1400 // at end of request line expect to see method, URI, version details
1401 // and switch to seeking Mime header section
1402 if (pos
== reqLineEnd
) {
1403 expect
.parserState
= Http1::HTTP_PARSE_MIME
;
1404 expect
.suffixSz
= 0;
1405 expect
.msgEnd
= reqLineEnd
-garbageEnd
;
1406 expect
.status
= Http::scOkay
;
1407 expect
.methodStart
= 0;
1408 expect
.methodEnd
= 2;
1409 expect
.method
= HttpRequestMethod(Http::METHOD_GET
);
1410 expect
.uriStart
= 4;
1412 expect
.uri
= "http://example.com/";
1413 expect
.versionStart
= 24;
1414 expect
.versionEnd
= 31;
1415 expect
.version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1);
1418 // one mime header is done we are expectign a new request
1419 // parse results say true and initial data is all gone from the buffer
1420 if (pos
== mimeEnd
) {
1421 expect
.parsed
= true;
1422 expect
.needsMore
= false;
1423 expect
.suffixSz
= 0;
1426 testResults(__LINE__
, ioBuf
, hp
, expect
);
1428 // sync the buffers like Squid does
1429 ioBuf
= hp
.remaining();
1431 // Squid stops using the parser once it has parsed the first message.
1432 if (!hp
.needsMoreData())