testHttp1Parser.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2017 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 #endif
87 
88  const bool parsed = output.parse(input);
89 
90 #ifdef SQUID_DEBUG_TESTS
91  if (expect.parsed != parsed)
92  std::cerr << "\tparse-FAILED: " << expect.parsed << "!=" << parsed << "\n";
93  else if (parsed && expect.method != output.method_)
94  std::cerr << "\tmethod-FAILED: " << expect.method << "!=" << output.method_ << "\n";
95  if (expect.status != output.parseStatusCode)
96  std::cerr << "\tscode-FAILED: " << expect.status << "!=" << output.parseStatusCode << "\n";
97  if (expect.suffixSz != output.buf_.length())
98  std::cerr << "\tsuffixSz-FAILED: " << expect.suffixSz << "!=" << output.buf_.length() << "\n";
99 #endif
100 
101  // runs the parse
102  CPPUNIT_ASSERT_EQUAL(expect.parsed, parsed);
103 
104  // if parsing was successful, check easily visible field outputs
105  if (parsed) {
106  CPPUNIT_ASSERT_EQUAL(expect.method, output.method_);
107  if (expect.uri != NULL)
108  CPPUNIT_ASSERT_EQUAL(0, output.uri_.cmp(expect.uri));
109  CPPUNIT_ASSERT_EQUAL(expect.version, output.msgProtocol_);
110  }
111 
112  CPPUNIT_ASSERT_EQUAL(expect.status, output.parseStatusCode);
113 
114  // check more obscure states
115  CPPUNIT_ASSERT_EQUAL(expect.needsMore, output.needsMoreData());
116  if (output.needsMoreData())
117  CPPUNIT_ASSERT_EQUAL(expect.parserState, output.parsingStage_);
118  CPPUNIT_ASSERT_EQUAL(expect.suffixSz, output.buf_.length());
119 }
120 
121 void
123 {
124  // whether the constructor works
125  {
126  Http1::RequestParser output;
127  CPPUNIT_ASSERT_EQUAL(true, output.needsMoreData());
128  CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_NONE, output.parsingStage_);
129  CPPUNIT_ASSERT_EQUAL(Http::scNone, output.parseStatusCode); // XXX: clear() not being called.
130  CPPUNIT_ASSERT(output.buf_.isEmpty());
131  CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_NONE), output.method_);
132  CPPUNIT_ASSERT(output.uri_.isEmpty());
133  CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_);
134  }
135 
136  // whether new() works
137  {
139  CPPUNIT_ASSERT_EQUAL(true, output->needsMoreData());
140  CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_NONE, output->parsingStage_);
141  CPPUNIT_ASSERT_EQUAL(Http::scNone, output->parseStatusCode);
142  CPPUNIT_ASSERT(output->buf_.isEmpty());
143  CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_NONE), output->method_);
144  CPPUNIT_ASSERT(output->uri_.isEmpty());
145  CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output->msgProtocol_);
146  delete output;
147  }
148 }
149 
150 void
152 {
153  // ensure MemPools etc exist
154  globalSetup();
155 
156  SBuf input;
157  Http1::RequestParser output;
158 
159  // TEST: Do we comply with RFC 1945 section 5.1 ?
160  // TEST: Do we comply with RFC 7230 sections 2.6, 3.1.1 and 3.5 ?
161 
162  // RFC 1945 : HTTP/0.9 simple-request
163  {
164  input.append("GET /\r\n", 7);
165  struct resultSet expect = {
166  .parsed = true,
167  .needsMore = false,
168  .parserState = Http1::HTTP_PARSE_DONE,
169  .status = Http::scOkay,
170  .suffixSz = 0,
172  .uri = "/",
174  };
175  output.clear();
176  testResults(__LINE__, input, output, expect);
177  input.clear();
178  }
179 
180  // RFC 1945 : invalid HTTP/0.9 simple-request (only GET is valid)
181  {
182  input.append("POST /\r\n", 8);
183  struct resultSet expect = {
184  .parsed = false,
185  .needsMore = false,
186  .parserState = Http1::HTTP_PARSE_DONE,
187  .status = Http::scBadRequest,
188  .suffixSz = input.length(),
190  .uri = NULL,
191  .version = AnyP::ProtocolVersion()
192  };
193  output.clear();
194  testResults(__LINE__, input, output, expect);
195  input.clear();
196  }
197 
198  // RFC 1945 and 7230 : HTTP/1.0 request
199  {
200  input.append("GET / HTTP/1.0\r\n", 16);
201  struct resultSet expect = {
202  .parsed = false,
203  .needsMore = true,
204  .parserState = Http1::HTTP_PARSE_MIME,
205  .status = Http::scOkay,
206  .suffixSz = 0,
208  .uri = "/",
210  };
211  output.clear();
212  testResults(__LINE__, input, output, expect);
213  input.clear();
214  }
215 
216  // RFC 7230 : HTTP/1.1 request
217  {
218  input.append("GET / HTTP/1.1\r\n", 16);
219  struct resultSet expect = {
220  .parsed = false,
221  .needsMore = true,
222  .parserState = Http1::HTTP_PARSE_MIME,
223  .status = Http::scOkay,
224  .suffixSz = 0,
226  .uri = "/",
228  };
229  output.clear();
230  testResults(__LINE__, input, output, expect);
231  input.clear();
232  }
233 
234  // RFC 7230 : future 1.x version full-request
235  {
236  input.append("GET / HTTP/1.2\r\n", 16);
237  struct resultSet expect = {
238  .parsed = false,
239  .needsMore = true,
240  .parserState = Http1::HTTP_PARSE_MIME,
241  .status = Http::scOkay,
242  .suffixSz = 0,
244  .uri = "/",
246  };
247  output.clear();
248  testResults(__LINE__, input, output, expect);
249  input.clear();
250  }
251 
252  // RFC 7230 : future versions do not use 1.x message syntax.
253  // However, it is still valid syntax for the single-digit forms
254  // to appear. The parser we are testing should accept them.
255  {
256  input.append("GET / HTTP/2.0\r\n", 16);
257  struct resultSet expectA = {
258  .parsed = true,
259  .needsMore = false,
260  .parserState = Http1::HTTP_PARSE_DONE,
261  .status = Http::scOkay,
262  .suffixSz = 0,
264  .uri = "/",
266  };
267  output.clear();
268  testResults(__LINE__, input, output, expectA);
269  input.clear();
270 
271  input.append("GET / HTTP/9.9\r\n", 16);
272  struct resultSet expectB = {
273  .parsed = true,
274  .needsMore = false,
275  .parserState = Http1::HTTP_PARSE_DONE,
276  .status = Http::scOkay,
277  .suffixSz = 0,
279  .uri = "/",
281  };
282  output.clear();
283  testResults(__LINE__, input, output, expectB);
284  input.clear();
285  }
286 
287  // RFC 7230 : future versions >= 10.0 are invalid syntax
288  {
289  input.append("GET / HTTP/10.12\r\n", 18);
290  struct resultSet expect = {
291  .parsed = false,
292  .needsMore = false,
293  .parserState = Http1::HTTP_PARSE_MIME,
294  .status = Http::scBadRequest,
295  .suffixSz = input.length(),
297  .uri = "/",
298  .version = AnyP::ProtocolVersion()
299  };
300  output.clear();
301  testResults(__LINE__, input, output, expect);
302  input.clear();
303  }
304 
305  // unknown non-HTTP protocol names
306  {
307  input.append("GET / FOO/1.0\r\n", 15);
308  struct resultSet expect = {
309  .parsed = false,
310  .needsMore = false,
311  .parserState = Http1::HTTP_PARSE_DONE,
312  .status = Http::scBadRequest,
313  .suffixSz = input.length(),
315  .uri = "/",
316  .version = AnyP::ProtocolVersion()
317  };
318  output.clear();
319  testResults(__LINE__, input, output, expect);
320  input.clear();
321  }
322 
323  // no version digits
324  {
325  input.append("GET / HTTP/\r\n", 13);
326  struct resultSet expect = {
327  .parsed = false,
328  .needsMore = false,
329  .parserState = Http1::HTTP_PARSE_DONE,
330  .status = Http::scBadRequest,
331  .suffixSz = input.length(),
333  .uri = "/",
334  .version = AnyP::ProtocolVersion()
335  };
336  output.clear();
337  testResults(__LINE__, input, output, expect);
338  input.clear();
339  }
340 
341  // no major version
342  {
343  input.append("GET / HTTP/.1\r\n", 15);
344  struct resultSet expect = {
345  .parsed = false,
346  .needsMore = false,
347  .parserState = Http1::HTTP_PARSE_DONE,
348  .status = Http::scBadRequest,
349  .suffixSz = input.length(),
351  .uri = "/",
352  .version = AnyP::ProtocolVersion()
353  };
354  output.clear();
355  testResults(__LINE__, input, output, expect);
356  input.clear();
357  }
358 
359  // no version dot
360  {
361  input.append("GET / HTTP/11\r\n", 15);
362  struct resultSet expect = {
363  .parsed = false,
364  .needsMore = false,
365  .parserState = Http1::HTTP_PARSE_DONE,
366  .status = Http::scBadRequest,
367  .suffixSz = input.length(),
369  .uri = "/",
370  .version = AnyP::ProtocolVersion()
371  };
372  output.clear();
373  testResults(__LINE__, input, output, expect);
374  input.clear();
375  }
376 
377  // negative major version (bug 3062)
378  {
379  input.append("GET / HTTP/-999999.1\r\n", 22);
380  struct resultSet expect = {
381  .parsed = false,
382  .needsMore = false,
383  .parserState = Http1::HTTP_PARSE_DONE,
384  .status = Http::scBadRequest,
385  .suffixSz = input.length(),
387  .uri = "/",
388  .version = AnyP::ProtocolVersion()
389  };
390  output.clear();
391  testResults(__LINE__, input, output, expect);
392  input.clear();
393  }
394 
395  // no minor version
396  {
397  input.append("GET / HTTP/1.\r\n", 15);
398  struct resultSet expect = {
399  .parsed = false,
400  .needsMore = false,
401  .parserState = Http1::HTTP_PARSE_DONE,
402  .status = Http::scBadRequest,
403  .suffixSz = input.length(),
405  .uri = "/",
406  .version = AnyP::ProtocolVersion()
407  };
408  output.clear();
409  testResults(__LINE__, input, output, expect);
410  input.clear();
411  }
412 
413  // negative major version (bug 3062 corollary)
414  {
415  input.append("GET / HTTP/1.-999999\r\n", 22);
416  struct resultSet expect = {
417  .parsed = false,
418  .needsMore = false,
419  .parserState = Http1::HTTP_PARSE_DONE,
420  .status = Http::scBadRequest,
421  .suffixSz = input.length(),
423  .uri = "/",
424  .version = AnyP::ProtocolVersion()
425  };
426  output.clear();
427  testResults(__LINE__, input, output, expect);
428  input.clear();
429  }
430 }
431 
432 void
434 {
435  // ensure MemPools etc exist
436  globalSetup();
437 
438  SBuf input;
439  Http1::RequestParser output;
440 
441  // space padded URL
442  {
443  input.append("GET / HTTP/1.1\r\n", 21);
444  // when being tolerant extra (sequential) SP delimiters are acceptable
446  struct resultSet expect = {
447  .parsed = false,
448  .needsMore = true,
449  .parserState = Http1::HTTP_PARSE_MIME,
450  .status = Http::scOkay,
451  .suffixSz = 0,
453  .uri = "/",
455  };
456  output.clear();
457  testResults(__LINE__, input, output, expect);
458 
460  struct resultSet expectStrict = {
461  .parsed = false,
462  .needsMore = false,
463  .parserState = Http1::HTTP_PARSE_DONE,
464  .status = Http::scBadRequest,
465  .suffixSz = input.length(),
466  .method = HttpRequestMethod(),
467  .uri = NULL,
468  .version = AnyP::ProtocolVersion()
469  };
470  output.clear();
471  testResults(__LINE__, input, output, expectStrict);
472  input.clear();
473  }
474 
475  // whitespace inside URI. (nasty but happens)
476  {
477  input.append("GET /fo o/ HTTP/1.1\r\n", 21);
479  struct resultSet expect = {
480  .parsed = false,
481  .needsMore = true,
482  .parserState = Http1::HTTP_PARSE_MIME,
483  .status = Http::scOkay,
484  .suffixSz = 0,
486  .uri = "/fo o/",
488  };
489  output.clear();
490  testResults(__LINE__, input, output, expect);
491 
493  struct resultSet expectStrict = {
494  .parsed = false,
495  .needsMore = false,
496  .parserState = Http1::HTTP_PARSE_DONE,
497  .status = Http::scBadRequest,
498  .suffixSz = input.length(),
499  .method = HttpRequestMethod(),
500  .uri = NULL,
501  .version = AnyP::ProtocolVersion()
502  };
503  output.clear();
504  testResults(__LINE__, input, output, expectStrict);
505  input.clear();
506  }
507 
508  // additional data in buffer
509  {
510  input.append("GET / HTTP/1.1\r\nboo!", 20);
511  struct resultSet expect = {
512  .parsed = false,
513  .needsMore = true,
514  .parserState = Http1::HTTP_PARSE_MIME,
515  .status = Http::scOkay,
516  .suffixSz = 4, // strlen("boo!")
518  .uri = "/",
520  };
521  output.clear();
522  testResults(__LINE__, input, output, expect);
523  input.clear();
525  }
526 }
527 
528 void
530 {
531  // ensure MemPools etc exist
532  globalSetup();
533 
534  SBuf input;
535  Http1::RequestParser output;
536 
537  // alternative EOL sequence: NL-only
538  // RFC 7230 tolerance permits omitted CR
539  {
540  input.append("GET / HTTP/1.1\n", 15);
542  struct resultSet expect = {
543  .parsed = false,
544  .needsMore = true,
545  .parserState = Http1::HTTP_PARSE_MIME,
546  .status = Http::scOkay,
547  .suffixSz = 0,
549  .uri = "/",
551  };
552  output.clear();
553  testResults(__LINE__, input, output, expect);
554 
556  struct resultSet expectStrict = {
557  .parsed = false,
558  .needsMore = false,
559  .parserState = Http1::HTTP_PARSE_DONE,
560  .status = Http::scBadRequest,
561  .suffixSz = input.length(),
562  .method = HttpRequestMethod(),
563  .uri = NULL,
564  .version = AnyP::ProtocolVersion()
565  };
566  output.clear();
567  testResults(__LINE__, input, output, expectStrict);
568  input.clear();
569  }
570 
571  // alternative EOL sequence: double-NL-only
572  // RFC 7230 tolerance permits omitted CR
573  // NP: represents a request with no mime headers
574  {
575  input.append("GET / HTTP/1.1\n\n", 16);
577  struct resultSet expect = {
578  .parsed = true,
579  .needsMore = false,
580  .parserState = Http1::HTTP_PARSE_DONE,
581  .status = Http::scOkay,
582  .suffixSz = 0,
584  .uri = "/",
586  };
587  output.clear();
588  testResults(__LINE__, input, output, expect);
589 
591  struct resultSet expectStrict = {
592  .parsed = false,
593  .needsMore = false,
594  .parserState = Http1::HTTP_PARSE_DONE,
595  .status = Http::scBadRequest,
596  .suffixSz = input.length(),
597  .method = HttpRequestMethod(),
598  .uri = NULL,
599  .version = AnyP::ProtocolVersion()
600  };
601  output.clear();
602  testResults(__LINE__, input, output, expectStrict);
603  input.clear();
604  }
605 
606  // space padded version
607  {
608  // RFC 7230 specifies version is followed by CRLF. No intermediary bytes.
609  input.append("GET / HTTP/1.1 \r\n", 17);
610  struct resultSet expect = {
611  .parsed = false,
612  .needsMore = false,
613  .parserState = Http1::HTTP_PARSE_DONE,
614  .status = Http::scBadRequest,
615  .suffixSz = input.length(),
616  .method = HttpRequestMethod(),
617  .uri = NULL,
618  .version = AnyP::ProtocolVersion()
619  };
620  output.clear();
621  testResults(__LINE__, input, output, expect);
622  input.clear();
623  }
624 }
625 
626 void
628 {
629  // ensure MemPools etc exist
630  globalSetup();
631 
632  SBuf input;
633  Http1::RequestParser output;
634 
635  // RFC 7230 : dot method
636  {
637  input.append(". / HTTP/1.1\r\n", 14);
638  struct resultSet expect = {
639  .parsed = false,
640  .needsMore = true,
641  .parserState = Http1::HTTP_PARSE_MIME,
642  .status = Http::scOkay,
643  .suffixSz = 0,
644  .method = HttpRequestMethod(SBuf(".")),
645  .uri = "/",
647  };
648  output.clear();
649  testResults(__LINE__, input, output, expect);
650  input.clear();
651  }
652 
653  // RFC 7230 : special TCHAR method chars
654  {
655  input.append("!#$%&'*+-.^_`|~ / HTTP/1.1\r\n", 28);
656  struct resultSet expect = {
657  .parsed = false,
658  .needsMore = true,
659  .parserState = Http1::HTTP_PARSE_MIME,
660  .status = Http::scOkay,
661  .suffixSz = 0,
662  .method = HttpRequestMethod(SBuf("!#$%&'*+-.^_`|~")),
663  .uri = "/",
665  };
666  output.clear();
667  testResults(__LINE__, input, output, expect);
668  input.clear();
669  }
670 
671  // OPTIONS with * URL
672  {
673  input.append("OPTIONS * HTTP/1.1\r\n", 20);
674  struct resultSet expect = {
675  .parsed = false,
676  .needsMore = true,
677  .parserState = Http1::HTTP_PARSE_MIME,
678  .status = Http::scOkay,
679  .suffixSz = 0,
681  .uri = "*",
683  };
684  output.clear();
685  testResults(__LINE__, input, output, expect);
686  input.clear();
687  }
688 
689  // unknown method
690  {
691  input.append("HELLOWORLD / HTTP/1.1\r\n", 23);
692  struct resultSet expect = {
693  .parsed = false,
694  .needsMore = true,
695  .parserState = Http1::HTTP_PARSE_MIME,
696  .status = Http::scOkay,
697  .suffixSz = 0,
698  .method = HttpRequestMethod(SBuf("HELLOWORLD")),
699  .uri = "/",
701  };
702  output.clear();
703  testResults(__LINE__, input, output, expect);
704  input.clear();
705  }
706 
707 #if 0
708  // too-long method (over 16 bytes)
709  {
710  input.append("HELLOSTRANGEWORLD / HTTP/1.1\r\n", 31);
711  struct resultSet expect = {
712  .parsed = false,
713  .needsMore = false,
714  .parserState = Http1::HTTP_PARSE_DONE,
715  .status = Http::scNotImplemented,
716  .suffixSz = input.length(),
717  .method = HttpRequestMethod(),
718  .uri = NULL,
719  .version = AnyP::ProtocolVersion()
720  };
721  output.clear();
722  testResults(__LINE__, input, output, expect);
723  input.clear();
724  }
725 #endif
726 
727  // method-only
728  {
729  input.append("A\n", 2);
730  struct resultSet expect = {
731  .parsed = false,
732  .needsMore = false,
733  .parserState = Http1::HTTP_PARSE_DONE,
734  .status = Http::scBadRequest,
735  .suffixSz = input.length(),
736  .method = HttpRequestMethod(),
737  .uri = NULL,
738  .version = AnyP::ProtocolVersion()
739  };
740  output.clear();
741  testResults(__LINE__, input, output, expect);
742  input.clear();
743  }
744 
745  {
746  input.append("GET\n", 4);
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  // space padded method (SP is reserved so invalid as a method byte)
763  {
764  input.append(" GET / HTTP/1.1\r\n", 17);
765  struct resultSet expect = {
766  .parsed = false,
767  .needsMore = false,
768  .parserState = Http1::HTTP_PARSE_DONE,
769  .status = Http::scBadRequest,
770  .suffixSz = input.length(),
771  .method = HttpRequestMethod(),
772  .uri = NULL,
773  .version = AnyP::ProtocolVersion()
774  };
775  output.clear();
776  testResults(__LINE__, input, output, expect);
777  input.clear();
778  }
779 
780  // RFC 7230 defined tolerance: ignore empty line(s) prefix on messages
781  {
782  input.append("\r\n\r\n\nGET / HTTP/1.1\r\n", 21);
784  struct resultSet expect = {
785  .parsed = false,
786  .needsMore = true,
787  .parserState = Http1::HTTP_PARSE_MIME,
788  .status = Http::scOkay,
789  .suffixSz = 0,
791  .uri = "/",
793  };
794  output.clear();
795  testResults(__LINE__, input, output, expect);
796 
798  struct resultSet expectStrict = {
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, expectStrict);
810  input.clear();
811  }
812 
813  // forbidden character in method
814  {
815  input.append("\tGET / HTTP/1.1\r\n", 17);
816  struct resultSet expect = {
817  .parsed = false,
818  .needsMore = false,
819  .parserState = Http1::HTTP_PARSE_DONE,
820  .status = Http::scBadRequest,
821  .suffixSz = input.length(),
822  .method = HttpRequestMethod(),
823  .uri = NULL,
824  .version = AnyP::ProtocolVersion()
825  };
826  output.clear();
827  testResults(__LINE__, input, output, expect);
828  input.clear();
829  }
830 
831  // CR in method delimiters
832  {
833  // RFC 7230 section 3.5 permits CR in whitespace but only for tolerant parsers
834  input.append("GET\r / HTTP/1.1\r\n", 17);
836  struct resultSet expect = {
837  .parsed = false,
838  .needsMore = true,
839  .parserState = Http1::HTTP_PARSE_MIME,
840  .status = Http::scOkay,
841  .suffixSz = 0,
843  .uri = "/",
845  };
846  output.clear();
847  testResults(__LINE__, input, output, expect);
848 
850  struct resultSet expectStrict = {
851  .parsed = false,
852  .needsMore = false,
853  .parserState = Http1::HTTP_PARSE_DONE,
854  .status = Http::scBadRequest,
855  .suffixSz = input.length(),
856  .method = HttpRequestMethod(),
857  .uri = NULL,
858  .version = AnyP::ProtocolVersion()
859  };
860  output.clear();
861  testResults(__LINE__, input, output, expectStrict);
862  input.clear();
863  }
864 
865  // tolerant parser delimiters
866  {
867  // RFC 7230 section 3.5 permits certain binary characters as whitespace delimiters
868  input.append("GET\r\t\x0B\x0C / HTTP/1.1\r\n", 20);
870  struct resultSet expect = {
871  .parsed = false,
872  .needsMore = true,
873  .parserState = Http1::HTTP_PARSE_MIME,
874  .status = Http::scOkay,
875  .suffixSz = 0,
877  .uri = "/",
879  };
880  output.clear();
881  testResults(__LINE__, input, output, expect);
882 
884  struct resultSet expectStrict = {
885  .parsed = false,
886  .needsMore = false,
887  .parserState = Http1::HTTP_PARSE_DONE,
888  .status = Http::scBadRequest,
889  .suffixSz = input.length(),
890  .method = HttpRequestMethod(),
891  .uri = NULL,
892  .version = AnyP::ProtocolVersion()
893  };
894  output.clear();
895  testResults(__LINE__, input, output, expectStrict);
896  input.clear();
897  }
898 }
899 
900 void
902 {
903  // ensure MemPools etc exist
904  globalSetup();
905 
906  SBuf input;
907  Http1::RequestParser output;
908 
909  // no method (or method delimiter)
910  {
911  // HTTP/0.9 requires method to be "GET"
912  input.append("/ HTTP/1.0\n", 11);
913  struct resultSet expect = {
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, expect);
925  input.clear();
926  }
927 
928  // no method (with method delimiter)
929  {
930  input.append(" / HTTP/1.0\n", 12);
931  struct resultSet expectStrict = {
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, expectStrict);
943  input.clear();
944  }
945 
946  // binary code after method (invalid)
947  {
948  input.append("GET\x16 / 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  // binary code NUL! after method (always invalid)
965  {
966  input.append("GET\0 / HTTP/1.1\r\n", 17);
967  struct resultSet expect = {
968  .parsed = false,
969  .needsMore = false,
970  .parserState = Http1::HTTP_PARSE_DONE,
971  .status = Http::scBadRequest,
972  .suffixSz = input.length(),
973  .method = HttpRequestMethod(),
974  .uri = NULL,
975  .version = AnyP::ProtocolVersion()
976  };
977  output.clear();
978  testResults(__LINE__, input, output, expect);
979  input.clear();
980  }
981 
982  // Either an RFC 1945 HTTP/0.9 simple-request for an "HTTP/1.1" URI or
983  // an invalid (no URI) HTTP/1.1 request. We treat this as latter, naturally.
984  {
985  input.append("GET HTTP/1.1\r\n", 15);
987  struct resultSet expect = {
988  .parsed = false,
989  .needsMore = false,
990  .parserState = Http1::HTTP_PARSE_DONE,
991  .status = Http::scBadRequest,
992  .suffixSz = input.length(),
993  .method = HttpRequestMethod(),
994  .uri = NULL,
995  .version = AnyP::ProtocolVersion()
996  };
997  output.clear();
998  testResults(__LINE__, input, output, expect);
999 
1001  struct resultSet expectStrict = {
1002  .parsed = false,
1003  .needsMore = false,
1004  .parserState = Http1::HTTP_PARSE_DONE,
1005  .status = Http::scBadRequest,
1006  .suffixSz = input.length(),
1007  .method = HttpRequestMethod(),
1008  .uri = NULL,
1009  .version = AnyP::ProtocolVersion()
1010  };
1011  output.clear();
1012  testResults(__LINE__, input, output, expectStrict);
1013  input.clear();
1014  }
1015 
1016  // Either an RFC 1945 HTTP/0.9 simple-request for an "HTTP/1.1" URI or
1017  // an invalid (no URI) HTTP/1.1 request. We treat this as latter, naturally.
1018  {
1019  input.append("GET HTTP/1.1\r\n", 14);
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  // binary line
1036  {
1037  input.append("\xB\xC\xE\xF\n", 5);
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
1054  {
1055  input.append("\t \t \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  // mixed whitespace line with CR
1072  {
1073  input.append("\r \t \n", 6);
1074  struct resultSet expect = {
1075  .parsed = false,
1076  .needsMore = false,
1077  .parserState = Http1::HTTP_PARSE_DONE,
1078  .status = Http::scBadRequest,
1079  .suffixSz = input.length(),
1080  .method = HttpRequestMethod(),
1081  .uri = NULL,
1082  .version = AnyP::ProtocolVersion()
1083  };
1084  output.clear();
1085  testResults(__LINE__, input, output, expect);
1086  input.clear();
1087  }
1088 }
1089 
1090 void
1092 {
1093  // Simulate a client drip-feeding Squid a few bytes at a time.
1094  // extend the size of the buffer from 0 bytes to full request length
1095  // calling the parser repeatedly as visible data grows.
1096 
1097  SBuf data;
1098  data.append("\n\n\n\n\n\n\n\n\n\n\n\n", 12);
1099  SBuf::size_type garbageEnd = data.length();
1100  data.append("GET ", 4);
1101  data.append("http://example.com/ ", 20);
1102  data.append("HTTP/1.1\r\n", 10);
1103  SBuf::size_type reqLineEnd = data.length() - 1;
1104  data.append("Host: example.com\r\n\r\n", 21);
1105  SBuf::size_type mimeEnd = data.length() - 1;
1106  data.append("...", 3); // trailer to catch mime EOS errors.
1107 
1108  SBuf ioBuf;
1110 
1111  // start with strict and move on to relaxed
1113 
1114  Config.maxRequestHeaderSize = 1024; // large enough to hold the test data.
1115 
1116  do {
1117 
1118  // state of things we expect right now
1119  struct resultSet expect = {
1120  .parsed = false,
1121  .needsMore = true,
1122  .parserState = Http1::HTTP_PARSE_NONE,
1123  .status = Http::scNone,
1124  .suffixSz = 0,
1125  .method = HttpRequestMethod(),
1126  .uri = NULL,
1127  .version = AnyP::ProtocolVersion()
1128  };
1129 
1130  ioBuf.clear(); // begins empty for each parser type
1131  hp.clear();
1132 
1134 
1135  for (SBuf::size_type pos = 0; pos <= data.length(); ++pos) {
1136 
1137  // simulate reading one more byte
1138  ioBuf.append(data.substr(pos,1));
1139 
1140  // strict does not permit the garbage prefix
1141  if (pos < garbageEnd && !Config.onoff.relaxed_header_parser) {
1142  ioBuf.clear();
1143  continue;
1144  }
1145 
1146  // when the garbage is passed we expect to start seeing first-line bytes
1147  if (pos == garbageEnd)
1149 
1150  // all points after garbage start to see accumulated bytes looking for end of current section
1151  if (pos >= garbageEnd)
1152  expect.suffixSz = ioBuf.length();
1153 
1154  // at end of request line expect to see method, URI, version details
1155  // and switch to seeking Mime header section
1156  if (pos == reqLineEnd) {
1158  expect.suffixSz = 0; // and a checkpoint buffer reset
1159  expect.status = Http::scOkay;
1161  expect.uri = "http://example.com/";
1163  }
1164 
1165  // one mime header is done we are expecting a new request
1166  // parse results say true and initial data is all gone from the buffer
1167  if (pos == mimeEnd) {
1168  expect.parsed = true;
1169  expect.needsMore = false;
1170  expect.suffixSz = 0; // and a checkpoint buffer reset
1171  }
1172 
1173  testResults(__LINE__, ioBuf, hp, expect);
1174 
1175  // sync the buffers like Squid does
1176  ioBuf = hp.remaining();
1177 
1178  // Squid stops using the parser once it has parsed the first message.
1179  if (!hp.needsMoreData())
1180  break;
1181  }
1182 
1184 
1185 }
1186 
AnyP::ProtocolVersion msgProtocol_
what protocol label has been found in the first line (if any)
Definition: Parser.h:152
Http::StatusCode status
void testParseRequestLineProtocols()
CPPUNIT_TEST_SUITE_REGISTRATION(testHttp1Parser)
ParseState
Definition: Parser.h:21
AnyP::ProtocolVersion ProtocolVersion()
Protocol version to use in Http::Message structures wrapping FTP messages.
Definition: Elements.cc:24
Definition: SBuf.h:87
AnyP::ProtocolVersion version
SBuf & append(const SBuf &S)
Definition: SBuf.cc:207
void clear()
Definition: SBuf.cc:190
Http::StatusCode parseStatusCode
Definition: Parser.h:106
bool isEmpty() const
Definition: SBuf.h:422
const char * uri
HTTP/1 mime-header block.
Definition: Parser.h:27
void testParseRequestLineTerminators()
virtual bool parse(const SBuf &aBuf)
void testParseRequestLineInvalid()
HttpRequestMethod method
void const char HLPCB void * data
Definition: stub_helper.cc:16
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:405
StatusCode
Definition: StatusCode.h:20
int relaxed_header_parser
Definition: SquidConfig.h:315
SBuf::size_type suffixSz
void Init()
Definition: old_api.cc:412
initialized, but nothing usefully parsed yet
Definition: Parser.h:22
void testParseRequestLineMethods()
parsed a message header, or reached a terminal syntax error
Definition: Parser.h:28
HTTP/1 message first-line.
Definition: Parser.h:23
static void testResults(int line, const SBuf &input, Http1::RequestParser &output, struct resultSet &expect)
void const char * buf
Definition: stub_helper.cc:16
HttpRequestMethod method_
what request method has been found on the first line
Definition: RequestParser.h:72
struct SquidConfig::@112 onoff
Http1::ParseState parserState
SBuf buf_
bytes remaining to be parsed
Definition: Parser.h:146
int cmp(const SBuf &S, const size_type n) const
shorthand version for compare()
Definition: SBuf.h:265
static const size_type npos
Definition: SBuf.h:93
bool needsMoreData() const
Definition: Parser.h:64
void testParseRequestLineStrange()
size_type find(char c, size_type startPos=0) const
Definition: SBuf.cc:614
const SBuf & remaining() const
the remaining unprocessed section of buffer
Definition: Parser.h:96
SBuf uri_
raw copy of the original client request-line URI field
Definition: RequestParser.h:75
MemBlob::size_type size_type
Definition: SBuf.h:90
SBuf substr(size_type pos, size_type n=npos) const
Definition: SBuf.cc:606
class SquidConfig Config
Definition: SquidConfig.cc:12
#define NULL
Definition: types.h:166
size_t maxRequestHeaderSize
Definition: SquidConfig.h:124
ParseState parsingStage_
what stage the parser is currently up to
Definition: Parser.h:149

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors