1 #define SQUID_UNIT_TEST 1
4 #include <cppunit/TestAssert.h>
7 #define protected public
9 #include "testHttp1Parser.h"
10 #include "http/Http1Parser.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());
62 CPPUNIT_ASSERT_EQUAL(expect
.done
, output
.isDone());
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::testParseRequestLineProtocols()
84 // ensure MemPools etc exist
88 Http1::RequestParser output
;
90 // TEST: Do we comply with RFC 1945 section 5.1 ?
91 // TEST: Do we comply with RFC 2616 section 5.1 ?
93 // RFC 1945 : HTTP/0.9 simple-request
95 input
.append("GET /\r\n", 7);
96 struct resultSet expect
= {
99 .parserState
= Http1::HTTP_PARSE_DONE
,
100 .status
= Http::scOkay
,
102 .msgEnd
= (int)input
.length()-1,
106 .method
= HttpRequestMethod(Http::METHOD_GET
),
112 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,0,9)
115 testResults(__LINE__
, input
, output
, expect
);
119 // RFC 1945 : invalid HTTP/0.9 simple-request (only GET is valid)
120 #if WHEN_RFC_COMPLIANT
122 input
.append("POST /\r\n", 7);
123 struct resultSet expect
= {
126 .parserState
= Http1::HTTP_PARSE_DONE
,
127 .status
= Http::scOkay
,
129 .msgEnd
= (int)input
.length()-1,
133 .method
= HttpRequestMethod(Http::METHOD_POST
),
139 .version
= AnyP::ProtocolVersion()
142 testResults(__LINE__
, input
, output
, expect
);
146 // RFC 1945 and 2616 : HTTP/1.0 request
148 input
.append("GET / HTTP/1.0\r\n", 16);
149 struct resultSet expect
= {
152 .parserState
= Http1::HTTP_PARSE_MIME
,
153 .status
= Http::scOkay
,
155 .msgEnd
= (int)input
.length()-1,
159 .method
= HttpRequestMethod(Http::METHOD_GET
),
165 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,0)
168 testResults(__LINE__
, input
, output
, expect
);
172 // RFC 2616 : HTTP/1.1 request
174 input
.append("GET / HTTP/1.1\r\n", 16);
175 struct resultSet expect
= {
178 .parserState
= Http1::HTTP_PARSE_MIME
,
179 .status
= Http::scOkay
,
181 .msgEnd
= (int)input
.length()-1,
185 .method
= HttpRequestMethod(Http::METHOD_GET
),
191 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
194 testResults(__LINE__
, input
, output
, expect
);
198 // RFC 2616 : future version full-request
200 input
.append("GET / HTTP/1.2\r\n", 16);
201 struct resultSet expect
= {
204 .parserState
= Http1::HTTP_PARSE_MIME
,
205 .status
= Http::scOkay
,
207 .msgEnd
= (int)input
.length()-1,
211 .method
= HttpRequestMethod(Http::METHOD_GET
),
217 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,2)
220 testResults(__LINE__
, input
, output
, expect
);
224 // RFC 2616 : future version full-request
226 // IETF HTTPbis WG has made this two-digits format invalid.
227 // it gets treated same as HTTP/0.9 for now
228 input
.append("GET / HTTP/10.12\r\n", 18);
229 struct resultSet expect
= {
232 .parserState
= Http1::HTTP_PARSE_MIME
,
233 .status
= Http::scHttpVersionNotSupported
,
235 .msgEnd
= (int)input
.length()-1,
236 .suffixSz
= input
.length(),
239 .method
= HttpRequestMethod(Http::METHOD_GET
),
245 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,10,12)
248 testResults(__LINE__
, input
, output
, expect
);
252 // unknown non-HTTP protocol names
254 // XXX: violations mode treats them as HTTP/0.9 requests! which is wrong.
255 #if !USE_HTTP_VIOLATIONS
256 input
.append("GET / FOO/1.0\n", 14);
257 struct resultSet expect
= {
260 .parserState
= Http1::HTTP_PARSE_DONE
,
261 .status
= Http::scHttpVersionNotSupported
,
263 .msgEnd
= (int)input
.length()-1,
264 .suffixSz
= input
.length(),
267 .method
= HttpRequestMethod(Http::METHOD_GET
),
273 .version
= AnyP::ProtocolVersion()
276 testResults(__LINE__
, input
, output
, expect
);
283 input
.append("GET / HTTP/\n", 12);
284 struct resultSet expect
= {
287 .parserState
= Http1::HTTP_PARSE_DONE
,
288 .status
= Http::scHttpVersionNotSupported
,
290 .msgEnd
= (int)input
.length()-1,
291 .suffixSz
= input
.length(),
294 .method
= HttpRequestMethod(Http::METHOD_GET
),
300 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,0,0)
303 testResults(__LINE__
, input
, output
, expect
);
309 input
.append("GET / HTTP/.1\n", 14);
310 struct resultSet expect
= {
313 .parserState
= Http1::HTTP_PARSE_DONE
,
314 .status
= Http::scHttpVersionNotSupported
,
316 .msgEnd
= (int)input
.length()-1,
317 .suffixSz
= input
.length(),
320 .method
= HttpRequestMethod(Http::METHOD_GET
),
326 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,0,0)
329 testResults(__LINE__
, input
, output
, expect
);
335 input
.append("GET / HTTP/11\n", 14);
336 struct resultSet expect
= {
339 .parserState
= Http1::HTTP_PARSE_DONE
,
340 .status
= Http::scHttpVersionNotSupported
,
342 .msgEnd
= (int)input
.length()-1,
343 .suffixSz
= input
.length(),
346 .method
= HttpRequestMethod(Http::METHOD_GET
),
352 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,0,0)
355 testResults(__LINE__
, input
, output
, expect
);
359 // negative major version (bug 3062)
361 input
.append("GET / HTTP/-999999.1\n", 21);
362 struct resultSet expect
= {
365 .parserState
= Http1::HTTP_PARSE_DONE
,
366 .status
= Http::scHttpVersionNotSupported
,
368 .msgEnd
= (int)input
.length()-1,
369 .suffixSz
= input
.length(),
372 .method
= HttpRequestMethod(Http::METHOD_GET
),
378 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,0,0)
381 testResults(__LINE__
, input
, output
, expect
);
387 input
.append("GET / HTTP/1.\n", 14);
388 struct resultSet expect
= {
391 .parserState
= Http1::HTTP_PARSE_DONE
,
392 .status
= Http::scHttpVersionNotSupported
,
394 .msgEnd
= (int)input
.length()-1,
395 .suffixSz
= input
.length(),
398 .method
= HttpRequestMethod(Http::METHOD_GET
),
404 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,0)
407 testResults(__LINE__
, input
, output
, expect
);
411 // negative major version (bug 3062 corollary)
413 input
.append("GET / HTTP/1.-999999\n", 21);
414 struct resultSet expect
= {
417 .parserState
= Http1::HTTP_PARSE_DONE
,
418 .status
= Http::scHttpVersionNotSupported
,
420 .msgEnd
= (int)input
.length()-1,
421 .suffixSz
= input
.length(),
424 .method
= HttpRequestMethod(Http::METHOD_GET
),
430 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,0)
433 testResults(__LINE__
, input
, output
, expect
);
439 testHttp1Parser::testParseRequestLineStrange()
441 // ensure MemPools etc exist
445 Http1::RequestParser output
;
449 input
.append("GET / HTTP/1.1\r\n", 21);
450 struct resultSet expect
= {
453 .parserState
= Http1::HTTP_PARSE_MIME
,
454 .status
= Http::scOkay
,
456 .msgEnd
= (int)input
.length()-1,
460 .method
= HttpRequestMethod(Http::METHOD_GET
),
466 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
469 testResults(__LINE__
, input
, output
, expect
);
473 // whitespace inside URI. (nasty but happens)
474 // XXX: depends on tolerant parser...
476 input
.append("GET /fo o/ HTTP/1.1\n", 20);
477 struct resultSet expect
= {
480 .parserState
= Http1::HTTP_PARSE_MIME
,
481 .status
= Http::scOkay
,
483 .msgEnd
= (int)input
.length()-1,
487 .method
= HttpRequestMethod(Http::METHOD_GET
),
493 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
496 testResults(__LINE__
, input
, output
, expect
);
500 // additional data in buffer
502 input
.append("GET / HTTP/1.1\nboo!", 23);
503 struct resultSet expect
= {
506 .parserState
= Http1::HTTP_PARSE_MIME
,
507 .status
= Http::scOkay
,
509 .msgEnd
= (int)input
.length()-5,
510 .suffixSz
= 4, // strlen("boo!")
513 .method
= HttpRequestMethod(Http::METHOD_GET
),
519 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
522 testResults(__LINE__
, input
, output
, expect
);
528 testHttp1Parser::testParseRequestLineTerminators()
530 // ensure MemPools etc exist
534 Http1::RequestParser output
;
536 // alternative EOL sequence: NL-only
538 input
.append("GET / HTTP/1.1\n", 15);
539 struct resultSet expect
= {
542 .parserState
= Http1::HTTP_PARSE_MIME
,
543 .status
= Http::scOkay
,
545 .msgEnd
= (int)input
.length()-1,
549 .method
= HttpRequestMethod(Http::METHOD_GET
),
555 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
558 testResults(__LINE__
, input
, output
, expect
);
562 // alternative EOL sequence: double-NL-only
564 input
.append("GET / HTTP/1.1\n\n", 16);
565 struct resultSet expect
= {
568 .parserState
= Http1::HTTP_PARSE_DONE
,
569 .status
= Http::scOkay
,
571 .msgEnd
= (int)input
.length()-2,
575 .method
= HttpRequestMethod(Http::METHOD_GET
),
581 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
584 testResults(__LINE__
, input
, output
, expect
);
588 // alternative EOL sequence: multi-CR-NL
590 input
.append("GET / HTTP/1.1\r\r\r\n", 18);
591 // Being tolerant we can ignore and elide these apparently benign CR
592 Config
.onoff
.relaxed_header_parser
= 1;
593 struct resultSet expectRelaxed
= {
596 .parserState
= Http1::HTTP_PARSE_MIME
,
597 .status
= Http::scOkay
,
599 .msgEnd
= (int)input
.length()-1,
603 .method
= HttpRequestMethod(Http::METHOD_GET
),
609 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
612 testResults(__LINE__
, input
, output
, expectRelaxed
);
614 // strict mode treats these as several bare-CR in the request line which is explicitly invalid.
615 Config
.onoff
.relaxed_header_parser
= 0;
616 struct resultSet expectStrict
= {
619 .parserState
= Http1::HTTP_PARSE_MIME
,
620 .status
= Http::scBadRequest
,
623 .suffixSz
= input
.length(),
626 .method
= HttpRequestMethod(),
632 .version
= AnyP::ProtocolVersion()
635 testResults(__LINE__
, input
, output
, expectStrict
);
639 // space padded version
641 // RFC 1945 and 2616 specify version is followed by CRLF. No intermediary bytes.
642 // NP: the terminal whitespace is a special case: invalid for even HTTP/0.9 with no version tag
643 input
.append("GET / HTTP/1.1 \n", 16);
644 struct resultSet expect
= {
647 .parserState
= Http1::HTTP_PARSE_DONE
,
648 .status
= Http::scBadRequest
,
650 .msgEnd
= (int)input
.length()-1,
651 .suffixSz
= input
.length(),
654 .method
= HttpRequestMethod(Http::METHOD_GET
),
660 .version
= AnyP::ProtocolVersion()
663 testResults(__LINE__
, input
, output
, expect
);
669 testHttp1Parser::testParseRequestLineMethods()
671 // ensure MemPools etc exist
675 Http1::RequestParser output
;
677 // RFC 2616 : . method
679 input
.append(". / HTTP/1.1\n", 13);
680 struct resultSet expect
= {
683 .parserState
= Http1::HTTP_PARSE_MIME
,
684 .status
= Http::scOkay
,
686 .msgEnd
= (int)input
.length()-1,
690 .method
= HttpRequestMethod("."),
696 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
699 testResults(__LINE__
, input
, output
, expect
);
703 // OPTIONS with * URL
705 input
.append("OPTIONS * HTTP/1.1\n", 19);
706 struct resultSet expect
= {
709 .parserState
= Http1::HTTP_PARSE_MIME
,
710 .status
= Http::scOkay
,
712 .msgEnd
= (int)input
.length()-1,
716 .method
= HttpRequestMethod(Http::METHOD_OPTIONS
),
722 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
725 testResults(__LINE__
, input
, output
, expect
);
731 input
.append("HELLOWORLD / HTTP/1.1\n", 22);
732 struct resultSet expect
= {
735 .parserState
= Http1::HTTP_PARSE_MIME
,
736 .status
= Http::scOkay
,
738 .msgEnd
= (int)input
.length()-1,
742 .method
= HttpRequestMethod("HELLOWORLD"),
748 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
751 testResults(__LINE__
, input
, output
, expect
);
757 input
.append("A\n", 2);
758 struct resultSet expect
= {
761 .parserState
= Http1::HTTP_PARSE_DONE
,
762 .status
= Http::scBadRequest
,
764 .msgEnd
= (int)input
.length()-1,
765 .suffixSz
= input
.length(),
768 .method
= HttpRequestMethod(),
774 .version
= AnyP::ProtocolVersion()
777 testResults(__LINE__
, input
, output
, expect
);
782 input
.append("GET\n", 4);
783 struct resultSet expect
= {
786 .parserState
= Http1::HTTP_PARSE_DONE
,
787 .status
= Http::scBadRequest
,
789 .msgEnd
= (int)input
.length()-1,
790 .suffixSz
= input
.length(),
793 .method
= HttpRequestMethod(),
799 .version
= AnyP::ProtocolVersion()
802 testResults(__LINE__
, input
, output
, expect
);
806 // space padded method (in strict mode SP is reserved so invalid as a method byte)
808 input
.append(" GET / HTTP/1.1\n", 16);
809 // RELAXED mode Squid custom tolerance ignores SP
810 #if USE_HTTP_VIOLATIONS
811 Config
.onoff
.relaxed_header_parser
= 1;
812 struct resultSet expectRelaxed
= {
815 .parserState
= Http1::HTTP_PARSE_MIME
,
816 .status
= Http::scOkay
,
817 .msgStart
= 0, // garbage collection consumes the SP
818 .msgEnd
= (int)input
.length()-2,
822 .method
= HttpRequestMethod(Http::METHOD_GET
),
828 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
831 testResults(__LINE__
, input
, output
, expectRelaxed
);
834 // STRICT mode obeys RFC syntax
835 Config
.onoff
.relaxed_header_parser
= 0;
836 struct resultSet expectStrict
= {
839 .parserState
= Http1::HTTP_PARSE_DONE
,
840 .status
= Http::scBadRequest
,
842 .msgEnd
= (int)input
.length()-1,
843 .suffixSz
= input
.length(),
846 .method
= HttpRequestMethod(),
852 .version
= AnyP::ProtocolVersion()
855 testResults(__LINE__
, input
, output
, expectStrict
);
859 // RFC 2616 defined tolerance: ignore empty line(s) prefix on messages
860 #if WHEN_RFC_COMPLIANT
862 input
.append("\r\n\r\n\nGET / HTTP/1.1\r\n", 21);
863 struct resultSet expect
= {
866 .parserState
= Http1::HTTP_PARSE_MIME
,
867 .status
= Http::scOkay
,
869 .msgEnd
= (int)input
.length()-1,
873 .method
= HttpRequestMethod(Http::METHOD_GET
),
879 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
882 testResults(__LINE__
, input
, output
, expect
);
887 // tab padded method (NP: tab is not SP so treated as any other binary)
889 input
.append("\tGET / HTTP/1.1\n", 16);
890 #if WHEN_RFC_COMPLIANT
891 struct resultSet expect
= {
894 .parserState
= Http1::HTTP_PARSE_DONE
,
895 .status
= Http::scBadRequest
,
897 .msgEnd
= (int)input
.length()-1,
898 .suffixSz
= input
.length(),
901 .method
= HttpRequestMethod(),
907 .version
= AnyP::ProtocolVersion()
909 #else // XXX: currently broken
910 struct resultSet expect
= {
913 .parserState
= Http1::HTTP_PARSE_MIME
,
914 .status
= Http::scOkay
,
915 .msgStart
= 0, // garbage collection consumes the SP
916 .msgEnd
= (int)input
.length()-1,
920 .method
= HttpRequestMethod(SBuf("\tGET")),
926 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
930 testResults(__LINE__
, input
, output
, expect
);
936 testHttp1Parser::testParseRequestLineInvalid()
938 // ensure MemPools etc exist
942 Http1::RequestParser output
;
944 // no method (but in a form which is ambiguous with HTTP/0.9 simple-request)
946 // XXX: HTTP/0.9 requires method to be "GET"
947 input
.append("/ HTTP/1.0\n", 11);
948 struct resultSet expect
= {
951 .parserState
= Http1::HTTP_PARSE_MIME
,
952 .status
= Http::scOkay
,
954 .msgEnd
= (int)input
.length()-1,
958 .method
= HttpRequestMethod("/"),
964 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,0,9)
967 testResults(__LINE__
, input
, output
, expect
);
971 // no method (an invalid format)
973 input
.append(" / HTTP/1.0\n", 12);
975 // XXX: squid custom tolerance consumes initial SP.
976 Config
.onoff
.relaxed_header_parser
= 1;
977 struct resultSet expectRelaxed
= {
980 .parserState
= Http1::HTTP_PARSE_MIME
,
981 .status
= Http::scOkay
,
983 .msgEnd
= (int)input
.length()-2,
987 .method
= HttpRequestMethod("/"),
993 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,0,9)
996 testResults(__LINE__
, input
, output
, expectRelaxed
);
998 // STRICT detect as invalid
999 Config
.onoff
.relaxed_header_parser
= 0;
1000 #if WHEN_RFC_COMPLIANT
1001 // XXX: except Squid does not
1002 struct resultSet expectStrict
= {
1005 .parserState
= Http1::HTTP_PARSE_DONE
,
1006 .status
= Http::scBadRequest
,
1008 .msgEnd
= (int)input
.length()-1,
1012 .method
= HttpRequestMethod(),
1018 .version
= AnyP::ProtocolVersion()
1021 struct resultSet expectStrict
= {
1024 .parserState
= Http1::HTTP_PARSE_DONE
,
1025 .status
= Http::scBadRequest
,
1027 .msgEnd
= (int)input
.length()-1,
1028 .suffixSz
= input
.length(),
1031 .method
= HttpRequestMethod(),
1037 .version
= AnyP::ProtocolVersion()
1040 output
.reset(input
);
1041 testResults(__LINE__
, input
, output
, expectStrict
);
1045 // binary code in method (invalid)
1047 input
.append("GET\x0B / HTTP/1.1\n", 16);
1048 #if WHEN_RFC_COMPLIANT
1049 struct resultSet expect
= {
1052 .parserState
= Http1::HTTP_PARSE_DONE
,
1053 .status
= Http::scBadRequest
,
1055 .msgEnd
= (int)input
.length()-1,
1056 .suffixSz
= input
.length(),
1059 .method
= HttpRequestMethod(),
1065 .version
= AnyP::ProtocolVersion()
1068 struct resultSet expect
= {
1071 .parserState
= Http1::HTTP_PARSE_MIME
,
1072 .status
= Http::scOkay
,
1073 .msgStart
= 0, // garbage collection consumes the SP
1074 .msgEnd
= (int)input
.length()-1,
1078 .method
= HttpRequestMethod(SBuf("GET\x0B")),
1084 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
1087 output
.reset(input
);
1088 testResults(__LINE__
, input
, output
, expect
);
1094 // RFC 2616 sec 5.1 prohibits CR other than in terminator.
1095 input
.append("GET\r / HTTP/1.1\r\n", 16);
1096 struct resultSet expect
= {
1099 .parserState
= Http1::HTTP_PARSE_DONE
,
1100 .status
= Http::scBadRequest
,
1102 .msgEnd
= -1, // halt at the first \r
1103 .suffixSz
= input
.length(),
1106 .method
= HttpRequestMethod(),
1112 .version
= AnyP::ProtocolVersion()
1114 output
.reset(input
);
1115 testResults(__LINE__
, input
, output
, expect
);
1119 // binary code NUL! in method (strange but ...)
1121 input
.append("GET\0 / HTTP/1.1\n", 16);
1122 #if WHEN_RFC_COMPLIANT
1123 struct resultSet expect
= {
1126 .parserState
= Http1::HTTP_PARSE_DONE
,
1127 .status
= Http::scBadRequest
,
1129 .msgEnd
= -1, // halt at the \0
1130 .suffixSz
= input
.length(),
1133 .method
= HttpRequestMethod(),
1139 .version
= AnyP::ProtocolVersion()
1142 struct resultSet expect
= {
1145 .parserState
= Http1::HTTP_PARSE_MIME
,
1146 .status
= Http::scOkay
,
1148 .msgEnd
= (int)input
.length()-1,
1152 .method
= HttpRequestMethod(SBuf("GET\0",4)),
1158 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1)
1161 output
.reset(input
);
1162 testResults(__LINE__
, input
, output
, expect
);
1166 // no URL (grammer invalid, ambiguous with RFC 1945 HTTP/0.9 simple-request)
1168 input
.append("GET HTTP/1.1\n", 14);
1169 struct resultSet expect
= {
1172 .parserState
= Http1::HTTP_PARSE_DONE
,
1173 .status
= Http::scOkay
,
1175 .msgEnd
= (int)input
.length()-1,
1179 .method
= HttpRequestMethod(Http::METHOD_GET
),
1185 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,0,9)
1187 output
.reset(input
);
1188 testResults(__LINE__
, input
, output
, expect
);
1192 // no URL (grammer invalid, ambiguous with RFC 1945 HTTP/0.9 simple-request)
1194 input
.append("GET HTTP/1.1\n", 13);
1195 struct resultSet expect
= {
1198 .parserState
= Http1::HTTP_PARSE_DONE
,
1199 .status
= Http::scOkay
,
1201 .msgEnd
= (int)input
.length()-1,
1205 .method
= HttpRequestMethod(Http::METHOD_GET
),
1211 .version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,0,9)
1213 output
.reset(input
);
1214 testResults(__LINE__
, input
, output
, expect
);
1220 input
.append("\xB\xC\xE\xF\n", 5);
1221 struct resultSet expect
= {
1224 .parserState
= Http1::HTTP_PARSE_DONE
,
1225 .status
= Http::scBadRequest
,
1227 .msgEnd
= (int)input
.length()-1,
1228 .suffixSz
= input
.length(),
1231 .method
= HttpRequestMethod(),
1237 .version
= AnyP::ProtocolVersion()
1239 output
.reset(input
);
1240 testResults(__LINE__
, input
, output
, expect
);
1244 // mixed whitespace line
1246 // We accept non-space binary bytes for method so first \t shows up as that
1247 // but remaining space and tabs are skipped searching for URI-start
1248 input
.append("\t \t \t\n", 6);
1249 struct resultSet expect
= {
1252 .parserState
= Http1::HTTP_PARSE_DONE
,
1253 .status
= Http::scBadRequest
,
1255 .msgEnd
= (int)input
.length()-1,
1256 .suffixSz
= input
.length(),
1259 .method
= HttpRequestMethod(SBuf("\t")),
1265 .version
= AnyP::ProtocolVersion()
1267 output
.reset(input
);
1268 testResults(__LINE__
, input
, output
, expect
);
1272 // mixed whitespace line with CR middle
1274 // CR aborts on sight, so even initial \t method is not marked as above
1275 // (not when parsing clean with whole line available anyway)
1276 input
.append("\t \r \n", 6);
1277 struct resultSet expect
= {
1280 .parserState
= Http1::HTTP_PARSE_DONE
,
1281 .status
= Http::scBadRequest
,
1283 .msgEnd
= -1, // halt on the \r
1284 .suffixSz
= input
.length(),
1287 .method
= HttpRequestMethod(),
1293 .version
= AnyP::ProtocolVersion()
1295 output
.reset(input
);
1296 testResults(__LINE__
, input
, output
, expect
);
1302 testHttp1Parser::testDripFeed()
1304 // Simulate a client drip-feeding Squid a few bytes at a time.
1305 // extend the size of the buffer from 0 bytes to full request length
1306 // calling the parser repeatedly as visible data grows.
1309 data
.append(" ", 12);
1310 SBuf::size_type garbageEnd
= data
.length();
1311 data
.append("GET http://example.com/ HTTP/1.1\r\n", 34);
1312 SBuf::size_type reqLineEnd
= data
.length() - 1;
1313 data
.append("Host: example.com\r\n\r\n", 21);
1314 SBuf::size_type mimeEnd
= data
.length() - 1;
1315 data
.append("...", 3); // trailer to catch mime EOS errors.
1317 SBuf ioBuf
; // begins empty
1318 Http1::RequestParser
hp(ioBuf
);
1320 // only relaxed parser accepts the garbage whitespace
1321 Config
.onoff
.relaxed_header_parser
= 1;
1323 // state of things we expect right now
1324 struct resultSet expect
= {
1327 .parserState
= Http1::HTTP_PARSE_NEW
,
1328 .status
= Http::scBadRequest
,
1334 .method
= HttpRequestMethod(),
1340 .version
= AnyP::ProtocolVersion()
1343 Config
.maxRequestHeaderSize
= 1024; // large enough to hold the test data.
1345 for (SBuf::size_type pos
= 0; pos
<= data
.length(); ++pos
) {
1347 // simulate reading one more byte
1348 ioBuf
.append(data
.substr(pos
,1));
1350 // sync the buffers like Squid does
1353 // when the garbage is passed we expect to start seeing first-line bytes
1354 if (pos
== garbageEnd
) {
1355 expect
.parserState
= Http1::HTTP_PARSE_FIRST
;
1356 expect
.msgStart
= 0;
1359 // all points after garbage start to see accumulated bytes looking for end of current section
1360 if (pos
>= garbageEnd
)
1361 expect
.suffixSz
= ioBuf
.length();
1363 // at end of request line expect to see method, URI, version details
1364 // and switch to seeking Mime header section
1365 if (pos
== reqLineEnd
) {
1366 expect
.parserState
= Http1::HTTP_PARSE_MIME
;
1367 expect
.suffixSz
= 0;
1368 expect
.msgEnd
= reqLineEnd
-garbageEnd
;
1369 expect
.status
= Http::scOkay
;
1370 expect
.methodStart
= 0;
1371 expect
.methodEnd
= 2;
1372 expect
.method
= HttpRequestMethod(Http::METHOD_GET
);
1373 expect
.uriStart
= 4;
1375 expect
.uri
= "http://example.com/";
1376 expect
.versionStart
= 24;
1377 expect
.versionEnd
= 31;
1378 expect
.version
= AnyP::ProtocolVersion(AnyP::PROTO_HTTP
,1,1);
1381 // one mime header is done we are expectign a new request
1382 // parse results say true and initial data is all gone from the buffer
1383 if (pos
== mimeEnd
) {
1384 expect
.parsed
= true;
1386 expect
.suffixSz
= 0;
1389 testResults(__LINE__
, ioBuf
, hp
, expect
);
1391 // sync the buffers like Squid does
1394 // Squid stops using the parser once it reports done.