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

 

Introduction

Documentation

Support

Miscellaneous