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