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