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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors