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