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