]>
Commit | Line | Data |
---|---|---|
48a37aee | 1 | /* |
f70aedc4 | 2 | * Copyright (C) 1996-2021 The Squid Software Foundation and contributors |
48a37aee AJ |
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 | ||
f7f3304a | 9 | #include "squid.h" |
4c14658e AJ |
10 | |
11 | #include <cppunit/TestAssert.h> | |
12 | ||
b6a7fc85 AJ |
13 | #define private public |
14 | #define protected public | |
15 | ||
947ca0c6 | 16 | #include "Debug.h" |
c99510dd | 17 | #include "http/one/RequestParser.h" |
274bd5ad | 18 | #include "http/RequestMethod.h" |
4c14658e | 19 | #include "MemBuf.h" |
4d5904f7 | 20 | #include "SquidConfig.h" |
bb86dcd4 | 21 | #include "testHttp1Parser.h" |
7f861c77 | 22 | #include "unitTestMain.h" |
4c14658e | 23 | |
bb86dcd4 | 24 | CPPUNIT_TEST_SUITE_REGISTRATION( testHttp1Parser ); |
4c14658e AJ |
25 | |
26 | void | |
bb86dcd4 | 27 | testHttp1Parser::globalSetup() |
4c14658e AJ |
28 | { |
29 | static bool setup_done = false; | |
30 | if (setup_done) | |
31 | return; | |
26c66627 | 32 | |
4c14658e AJ |
33 | Mem::Init(); |
34 | setup_done = true; | |
016a316b AJ |
35 | |
36 | // default to strict parser. set for loose parsing specifically where behaviour differs. | |
37 | Config.onoff.relaxed_header_parser = 0; | |
38 | ||
39 | Config.maxRequestHeaderSize = 1024; // XXX: unit test the RequestParser handling of this limit | |
4c14658e AJ |
40 | } |
41 | ||
7a4fa6a0 AJ |
42 | struct resultSet { |
43 | bool parsed; | |
36a9c964 | 44 | bool needsMore; |
7a4fa6a0 AJ |
45 | Http1::ParseState parserState; |
46 | Http::StatusCode status; | |
7a4fa6a0 | 47 | SBuf::size_type suffixSz; |
7a4fa6a0 | 48 | HttpRequestMethod method; |
7a4fa6a0 | 49 | const char *uri; |
7a4fa6a0 AJ |
50 | AnyP::ProtocolVersion version; |
51 | }; | |
52 | ||
e02f963c AR |
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 | ||
7a4fa6a0 AJ |
81 | static void |
82 | testResults(int line, const SBuf &input, Http1::RequestParser &output, struct resultSet &expect) | |
83 | { | |
e02f963c AR |
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"; | |
7a4fa6a0 AJ |
99 | #endif |
100 | ||
947ca0c6 | 101 | // runs the parse |
e02f963c AR |
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 | } | |
947ca0c6 | 111 | |
de158bf5 | 112 | CPPUNIT_ASSERT_EQUAL(expect.status, output.parseStatusCode); |
947ca0c6 AJ |
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()); | |
7a4fa6a0 AJ |
119 | } |
120 | ||
10c0d360 AJ |
121 | void |
122 | testHttp1Parser::testParserConstruct() | |
123 | { | |
124 | // whether the constructor works | |
125 | { | |
126 | Http1::RequestParser output; | |
127 | CPPUNIT_ASSERT_EQUAL(true, output.needsMoreData()); | |
128 | CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_NONE, output.parsingStage_); | |
f1d5359e | 129 | CPPUNIT_ASSERT_EQUAL(Http::scNone, output.parseStatusCode); // XXX: clear() not being called. |
10c0d360 | 130 | CPPUNIT_ASSERT(output.buf_.isEmpty()); |
10c0d360 | 131 | CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_NONE), output.method_); |
10c0d360 | 132 | CPPUNIT_ASSERT(output.uri_.isEmpty()); |
10c0d360 AJ |
133 | CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); |
134 | } | |
135 | ||
136 | // whether new() works | |
137 | { | |
138 | Http1::RequestParser *output = new Http1::RequestParser; | |
139 | CPPUNIT_ASSERT_EQUAL(true, output->needsMoreData()); | |
140 | CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_NONE, output->parsingStage_); | |
f1d5359e | 141 | CPPUNIT_ASSERT_EQUAL(Http::scNone, output->parseStatusCode); |
10c0d360 | 142 | CPPUNIT_ASSERT(output->buf_.isEmpty()); |
10c0d360 | 143 | CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_NONE), output->method_); |
10c0d360 | 144 | CPPUNIT_ASSERT(output->uri_.isEmpty()); |
10c0d360 AJ |
145 | CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output->msgProtocol_); |
146 | delete output; | |
147 | } | |
148 | } | |
149 | ||
4c14658e | 150 | void |
bb86dcd4 | 151 | testHttp1Parser::testParseRequestLineProtocols() |
4c14658e AJ |
152 | { |
153 | // ensure MemPools etc exist | |
154 | globalSetup(); | |
155 | ||
7a4fa6a0 | 156 | SBuf input; |
1b51ee7b | 157 | Http1::RequestParser output; |
4c14658e AJ |
158 | |
159 | // TEST: Do we comply with RFC 1945 section 5.1 ? | |
947ca0c6 | 160 | // TEST: Do we comply with RFC 7230 sections 2.6, 3.1.1 and 3.5 ? |
4c14658e AJ |
161 | |
162 | // RFC 1945 : HTTP/0.9 simple-request | |
4245f3b9 AJ |
163 | { |
164 | input.append("GET /\r\n", 7); | |
7a4fa6a0 AJ |
165 | struct resultSet expect = { |
166 | .parsed = true, | |
36a9c964 | 167 | .needsMore = false, |
7a4fa6a0 AJ |
168 | .parserState = Http1::HTTP_PARSE_DONE, |
169 | .status = Http::scOkay, | |
7a4fa6a0 | 170 | .suffixSz = 0, |
7a4fa6a0 | 171 | .method = HttpRequestMethod(Http::METHOD_GET), |
7a4fa6a0 | 172 | .uri = "/", |
7a4fa6a0 AJ |
173 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9) |
174 | }; | |
36a9c964 | 175 | output.clear(); |
7a4fa6a0 AJ |
176 | testResults(__LINE__, input, output, expect); |
177 | input.clear(); | |
4245f3b9 | 178 | } |
4c14658e | 179 | |
8c730cae | 180 | // RFC 1945 : invalid HTTP/0.9 simple-request (only GET is valid) |
4245f3b9 | 181 | { |
947ca0c6 | 182 | input.append("POST /\r\n", 8); |
7a4fa6a0 | 183 | struct resultSet expect = { |
947ca0c6 | 184 | .parsed = false, |
36a9c964 | 185 | .needsMore = false, |
7a4fa6a0 | 186 | .parserState = Http1::HTTP_PARSE_DONE, |
947ca0c6 | 187 | .status = Http::scBadRequest, |
e02f963c | 188 | .suffixSz = input.length(), |
7a4fa6a0 | 189 | .method = HttpRequestMethod(Http::METHOD_POST), |
947ca0c6 | 190 | .uri = NULL, |
7a4fa6a0 AJ |
191 | .version = AnyP::ProtocolVersion() |
192 | }; | |
36a9c964 | 193 | output.clear(); |
7a4fa6a0 AJ |
194 | testResults(__LINE__, input, output, expect); |
195 | input.clear(); | |
4245f3b9 | 196 | } |
947ca0c6 AJ |
197 | |
198 | // RFC 1945 and 7230 : HTTP/1.0 request | |
4245f3b9 AJ |
199 | { |
200 | input.append("GET / HTTP/1.0\r\n", 16); | |
7a4fa6a0 AJ |
201 | struct resultSet expect = { |
202 | .parsed = false, | |
36a9c964 | 203 | .needsMore = true, |
7a4fa6a0 AJ |
204 | .parserState = Http1::HTTP_PARSE_MIME, |
205 | .status = Http::scOkay, | |
7a4fa6a0 | 206 | .suffixSz = 0, |
7a4fa6a0 | 207 | .method = HttpRequestMethod(Http::METHOD_GET), |
7a4fa6a0 | 208 | .uri = "/", |
7a4fa6a0 AJ |
209 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,0) |
210 | }; | |
36a9c964 | 211 | output.clear(); |
7a4fa6a0 AJ |
212 | testResults(__LINE__, input, output, expect); |
213 | input.clear(); | |
4245f3b9 | 214 | } |
4c14658e | 215 | |
947ca0c6 | 216 | // RFC 7230 : HTTP/1.1 request |
4245f3b9 AJ |
217 | { |
218 | input.append("GET / HTTP/1.1\r\n", 16); | |
7a4fa6a0 AJ |
219 | struct resultSet expect = { |
220 | .parsed = false, | |
36a9c964 | 221 | .needsMore = true, |
7a4fa6a0 AJ |
222 | .parserState = Http1::HTTP_PARSE_MIME, |
223 | .status = Http::scOkay, | |
7a4fa6a0 | 224 | .suffixSz = 0, |
7a4fa6a0 | 225 | .method = HttpRequestMethod(Http::METHOD_GET), |
7a4fa6a0 | 226 | .uri = "/", |
7a4fa6a0 AJ |
227 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) |
228 | }; | |
36a9c964 | 229 | output.clear(); |
7a4fa6a0 AJ |
230 | testResults(__LINE__, input, output, expect); |
231 | input.clear(); | |
4245f3b9 | 232 | } |
4c14658e | 233 | |
f9f79936 | 234 | // RFC 7230 : future 1.x version full-request |
f4880526 AJ |
235 | { |
236 | input.append("GET / HTTP/1.2\r\n", 16); | |
7a4fa6a0 AJ |
237 | struct resultSet expect = { |
238 | .parsed = false, | |
36a9c964 | 239 | .needsMore = true, |
7a4fa6a0 AJ |
240 | .parserState = Http1::HTTP_PARSE_MIME, |
241 | .status = Http::scOkay, | |
7a4fa6a0 | 242 | .suffixSz = 0, |
7a4fa6a0 | 243 | .method = HttpRequestMethod(Http::METHOD_GET), |
7a4fa6a0 | 244 | .uri = "/", |
7a4fa6a0 AJ |
245 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,2) |
246 | }; | |
36a9c964 | 247 | output.clear(); |
7a4fa6a0 AJ |
248 | testResults(__LINE__, input, output, expect); |
249 | input.clear(); | |
4245f3b9 | 250 | } |
8c730cae | 251 | |
f9f79936 AJ |
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. | |
4245f3b9 | 255 | { |
947ca0c6 AJ |
256 | input.append("GET / HTTP/2.0\r\n", 16); |
257 | struct resultSet expectA = { | |
f9f79936 | 258 | .parsed = true, |
947ca0c6 | 259 | .needsMore = false, |
f9f79936 AJ |
260 | .parserState = Http1::HTTP_PARSE_DONE, |
261 | .status = Http::scOkay, | |
262 | .suffixSz = 0, | |
263 | .method = HttpRequestMethod(Http::METHOD_GET), | |
947ca0c6 | 264 | .uri = "/", |
f9f79936 | 265 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,2,0) |
947ca0c6 AJ |
266 | }; |
267 | output.clear(); | |
268 | testResults(__LINE__, input, output, expectA); | |
269 | input.clear(); | |
270 | ||
f9f79936 | 271 | input.append("GET / HTTP/9.9\r\n", 16); |
947ca0c6 | 272 | struct resultSet expectB = { |
f9f79936 AJ |
273 | .parsed = true, |
274 | .needsMore = false, | |
275 | .parserState = Http1::HTTP_PARSE_DONE, | |
276 | .status = Http::scOkay, | |
277 | .suffixSz = 0, | |
278 | .method = HttpRequestMethod(Http::METHOD_GET), | |
279 | .uri = "/", | |
280 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,9,9) | |
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 = { | |
7a4fa6a0 | 291 | .parsed = false, |
36a9c964 | 292 | .needsMore = false, |
7a4fa6a0 | 293 | .parserState = Http1::HTTP_PARSE_MIME, |
e02f963c AR |
294 | .status = Http::scBadRequest, |
295 | .suffixSz = input.length(), | |
7a4fa6a0 | 296 | .method = HttpRequestMethod(Http::METHOD_GET), |
7a4fa6a0 | 297 | .uri = "/", |
9651320a | 298 | .version = AnyP::ProtocolVersion() |
7a4fa6a0 | 299 | }; |
36a9c964 | 300 | output.clear(); |
f9f79936 | 301 | testResults(__LINE__, input, output, expect); |
7a4fa6a0 | 302 | input.clear(); |
4245f3b9 | 303 | } |
4c14658e | 304 | |
7a4fa6a0 | 305 | // unknown non-HTTP protocol names |
4245f3b9 | 306 | { |
947ca0c6 | 307 | input.append("GET / FOO/1.0\r\n", 15); |
7a4fa6a0 AJ |
308 | struct resultSet expect = { |
309 | .parsed = false, | |
36a9c964 | 310 | .needsMore = false, |
7a4fa6a0 | 311 | .parserState = Http1::HTTP_PARSE_DONE, |
e02f963c AR |
312 | .status = Http::scBadRequest, |
313 | .suffixSz = input.length(), | |
7a4fa6a0 | 314 | .method = HttpRequestMethod(Http::METHOD_GET), |
7a4fa6a0 | 315 | .uri = "/", |
7a4fa6a0 AJ |
316 | .version = AnyP::ProtocolVersion() |
317 | }; | |
36a9c964 | 318 | output.clear(); |
7a4fa6a0 AJ |
319 | testResults(__LINE__, input, output, expect); |
320 | input.clear(); | |
4245f3b9 | 321 | } |
8c730cae | 322 | |
947ca0c6 | 323 | // no version digits |
4245f3b9 | 324 | { |
947ca0c6 | 325 | input.append("GET / HTTP/\r\n", 13); |
7a4fa6a0 AJ |
326 | struct resultSet expect = { |
327 | .parsed = false, | |
36a9c964 | 328 | .needsMore = false, |
7a4fa6a0 | 329 | .parserState = Http1::HTTP_PARSE_DONE, |
e02f963c AR |
330 | .status = Http::scBadRequest, |
331 | .suffixSz = input.length(), | |
7a4fa6a0 | 332 | .method = HttpRequestMethod(Http::METHOD_GET), |
7a4fa6a0 | 333 | .uri = "/", |
9651320a | 334 | .version = AnyP::ProtocolVersion() |
7a4fa6a0 | 335 | }; |
36a9c964 | 336 | output.clear(); |
7a4fa6a0 AJ |
337 | testResults(__LINE__, input, output, expect); |
338 | input.clear(); | |
4245f3b9 | 339 | } |
4c14658e | 340 | |
8c730cae | 341 | // no major version |
4245f3b9 | 342 | { |
947ca0c6 | 343 | input.append("GET / HTTP/.1\r\n", 15); |
7a4fa6a0 AJ |
344 | struct resultSet expect = { |
345 | .parsed = false, | |
36a9c964 | 346 | .needsMore = false, |
7a4fa6a0 | 347 | .parserState = Http1::HTTP_PARSE_DONE, |
e02f963c AR |
348 | .status = Http::scBadRequest, |
349 | .suffixSz = input.length(), | |
7a4fa6a0 | 350 | .method = HttpRequestMethod(Http::METHOD_GET), |
7a4fa6a0 | 351 | .uri = "/", |
9651320a | 352 | .version = AnyP::ProtocolVersion() |
7a4fa6a0 | 353 | }; |
36a9c964 | 354 | output.clear(); |
7a4fa6a0 AJ |
355 | testResults(__LINE__, input, output, expect); |
356 | input.clear(); | |
4245f3b9 | 357 | } |
8c730cae AJ |
358 | |
359 | // no version dot | |
4245f3b9 | 360 | { |
947ca0c6 | 361 | input.append("GET / HTTP/11\r\n", 15); |
7a4fa6a0 AJ |
362 | struct resultSet expect = { |
363 | .parsed = false, | |
36a9c964 | 364 | .needsMore = false, |
7a4fa6a0 | 365 | .parserState = Http1::HTTP_PARSE_DONE, |
e02f963c AR |
366 | .status = Http::scBadRequest, |
367 | .suffixSz = input.length(), | |
7a4fa6a0 | 368 | .method = HttpRequestMethod(Http::METHOD_GET), |
7a4fa6a0 | 369 | .uri = "/", |
9651320a | 370 | .version = AnyP::ProtocolVersion() |
7a4fa6a0 | 371 | }; |
36a9c964 | 372 | output.clear(); |
7a4fa6a0 AJ |
373 | testResults(__LINE__, input, output, expect); |
374 | input.clear(); | |
4245f3b9 | 375 | } |
8c730cae AJ |
376 | |
377 | // negative major version (bug 3062) | |
4245f3b9 | 378 | { |
947ca0c6 | 379 | input.append("GET / HTTP/-999999.1\r\n", 22); |
7a4fa6a0 AJ |
380 | struct resultSet expect = { |
381 | .parsed = false, | |
36a9c964 | 382 | .needsMore = false, |
7a4fa6a0 | 383 | .parserState = Http1::HTTP_PARSE_DONE, |
e02f963c AR |
384 | .status = Http::scBadRequest, |
385 | .suffixSz = input.length(), | |
7a4fa6a0 | 386 | .method = HttpRequestMethod(Http::METHOD_GET), |
7a4fa6a0 | 387 | .uri = "/", |
9651320a | 388 | .version = AnyP::ProtocolVersion() |
7a4fa6a0 | 389 | }; |
36a9c964 | 390 | output.clear(); |
7a4fa6a0 AJ |
391 | testResults(__LINE__, input, output, expect); |
392 | input.clear(); | |
4245f3b9 | 393 | } |
4c14658e | 394 | |
8c730cae | 395 | // no minor version |
4245f3b9 | 396 | { |
947ca0c6 | 397 | input.append("GET / HTTP/1.\r\n", 15); |
7a4fa6a0 AJ |
398 | struct resultSet expect = { |
399 | .parsed = false, | |
36a9c964 | 400 | .needsMore = false, |
7a4fa6a0 | 401 | .parserState = Http1::HTTP_PARSE_DONE, |
e02f963c AR |
402 | .status = Http::scBadRequest, |
403 | .suffixSz = input.length(), | |
7a4fa6a0 | 404 | .method = HttpRequestMethod(Http::METHOD_GET), |
7a4fa6a0 | 405 | .uri = "/", |
947ca0c6 | 406 | .version = AnyP::ProtocolVersion() |
7a4fa6a0 | 407 | }; |
36a9c964 | 408 | output.clear(); |
7a4fa6a0 AJ |
409 | testResults(__LINE__, input, output, expect); |
410 | input.clear(); | |
4245f3b9 | 411 | } |
8c730cae AJ |
412 | |
413 | // negative major version (bug 3062 corollary) | |
4245f3b9 | 414 | { |
947ca0c6 | 415 | input.append("GET / HTTP/1.-999999\r\n", 22); |
7a4fa6a0 AJ |
416 | struct resultSet expect = { |
417 | .parsed = false, | |
36a9c964 | 418 | .needsMore = false, |
7a4fa6a0 | 419 | .parserState = Http1::HTTP_PARSE_DONE, |
e02f963c AR |
420 | .status = Http::scBadRequest, |
421 | .suffixSz = input.length(), | |
7a4fa6a0 | 422 | .method = HttpRequestMethod(Http::METHOD_GET), |
7a4fa6a0 | 423 | .uri = "/", |
947ca0c6 | 424 | .version = AnyP::ProtocolVersion() |
7a4fa6a0 | 425 | }; |
36a9c964 | 426 | output.clear(); |
7a4fa6a0 AJ |
427 | testResults(__LINE__, input, output, expect); |
428 | input.clear(); | |
4245f3b9 | 429 | } |
8c730cae AJ |
430 | } |
431 | ||
432 | void | |
bb86dcd4 | 433 | testHttp1Parser::testParseRequestLineStrange() |
8c730cae AJ |
434 | { |
435 | // ensure MemPools etc exist | |
436 | globalSetup(); | |
437 | ||
7a4fa6a0 | 438 | SBuf input; |
1b51ee7b | 439 | Http1::RequestParser output; |
8c730cae AJ |
440 | |
441 | // space padded URL | |
4245f3b9 AJ |
442 | { |
443 | input.append("GET / HTTP/1.1\r\n", 21); | |
947ca0c6 AJ |
444 | // when being tolerant extra (sequential) SP delimiters are acceptable |
445 | Config.onoff.relaxed_header_parser = 1; | |
7a4fa6a0 AJ |
446 | struct resultSet expect = { |
447 | .parsed = false, | |
36a9c964 | 448 | .needsMore = true, |
7a4fa6a0 AJ |
449 | .parserState = Http1::HTTP_PARSE_MIME, |
450 | .status = Http::scOkay, | |
7a4fa6a0 | 451 | .suffixSz = 0, |
7a4fa6a0 | 452 | .method = HttpRequestMethod(Http::METHOD_GET), |
7a4fa6a0 | 453 | .uri = "/", |
7a4fa6a0 AJ |
454 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) |
455 | }; | |
36a9c964 | 456 | output.clear(); |
7a4fa6a0 | 457 | testResults(__LINE__, input, output, expect); |
947ca0c6 AJ |
458 | |
459 | Config.onoff.relaxed_header_parser = 0; | |
460 | struct resultSet expectStrict = { | |
461 | .parsed = false, | |
462 | .needsMore = false, | |
463 | .parserState = Http1::HTTP_PARSE_DONE, | |
464 | .status = Http::scBadRequest, | |
e02f963c AR |
465 | .suffixSz = input.length(), |
466 | .method = HttpRequestMethod(), | |
947ca0c6 AJ |
467 | .uri = NULL, |
468 | .version = AnyP::ProtocolVersion() | |
469 | }; | |
470 | output.clear(); | |
471 | testResults(__LINE__, input, output, expectStrict); | |
7a4fa6a0 | 472 | input.clear(); |
4245f3b9 | 473 | } |
8c730cae | 474 | |
4c14658e | 475 | // whitespace inside URI. (nasty but happens) |
4245f3b9 | 476 | { |
947ca0c6 AJ |
477 | input.append("GET /fo o/ HTTP/1.1\r\n", 21); |
478 | Config.onoff.relaxed_header_parser = 1; | |
7a4fa6a0 AJ |
479 | struct resultSet expect = { |
480 | .parsed = false, | |
36a9c964 | 481 | .needsMore = true, |
7a4fa6a0 AJ |
482 | .parserState = Http1::HTTP_PARSE_MIME, |
483 | .status = Http::scOkay, | |
7a4fa6a0 | 484 | .suffixSz = 0, |
7a4fa6a0 | 485 | .method = HttpRequestMethod(Http::METHOD_GET), |
7a4fa6a0 | 486 | .uri = "/fo o/", |
7a4fa6a0 AJ |
487 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) |
488 | }; | |
36a9c964 | 489 | output.clear(); |
7a4fa6a0 | 490 | testResults(__LINE__, input, output, expect); |
947ca0c6 AJ |
491 | |
492 | Config.onoff.relaxed_header_parser = 0; | |
493 | struct resultSet expectStrict = { | |
494 | .parsed = false, | |
495 | .needsMore = false, | |
496 | .parserState = Http1::HTTP_PARSE_DONE, | |
e02f963c AR |
497 | .status = Http::scBadRequest, |
498 | .suffixSz = input.length(), | |
499 | .method = HttpRequestMethod(), | |
947ca0c6 AJ |
500 | .uri = NULL, |
501 | .version = AnyP::ProtocolVersion() | |
502 | }; | |
503 | output.clear(); | |
504 | testResults(__LINE__, input, output, expectStrict); | |
7a4fa6a0 | 505 | input.clear(); |
4245f3b9 | 506 | } |
4c14658e AJ |
507 | |
508 | // additional data in buffer | |
4245f3b9 | 509 | { |
947ca0c6 | 510 | input.append("GET / HTTP/1.1\r\nboo!", 20); |
7a4fa6a0 AJ |
511 | struct resultSet expect = { |
512 | .parsed = false, | |
36a9c964 | 513 | .needsMore = true, |
7a4fa6a0 AJ |
514 | .parserState = Http1::HTTP_PARSE_MIME, |
515 | .status = Http::scOkay, | |
7a4fa6a0 | 516 | .suffixSz = 4, // strlen("boo!") |
7a4fa6a0 | 517 | .method = HttpRequestMethod(Http::METHOD_GET), |
7a4fa6a0 | 518 | .uri = "/", |
7a4fa6a0 AJ |
519 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) |
520 | }; | |
36a9c964 | 521 | output.clear(); |
7a4fa6a0 AJ |
522 | testResults(__LINE__, input, output, expect); |
523 | input.clear(); | |
947ca0c6 | 524 | Config.onoff.relaxed_header_parser = 0; |
4245f3b9 | 525 | } |
8c730cae AJ |
526 | } |
527 | ||
528 | void | |
bb86dcd4 | 529 | testHttp1Parser::testParseRequestLineTerminators() |
8c730cae AJ |
530 | { |
531 | // ensure MemPools etc exist | |
532 | globalSetup(); | |
533 | ||
7a4fa6a0 | 534 | SBuf input; |
1b51ee7b | 535 | Http1::RequestParser output; |
4c14658e AJ |
536 | |
537 | // alternative EOL sequence: NL-only | |
947ca0c6 | 538 | // RFC 7230 tolerance permits omitted CR |
4245f3b9 AJ |
539 | { |
540 | input.append("GET / HTTP/1.1\n", 15); | |
947ca0c6 | 541 | Config.onoff.relaxed_header_parser = 1; |
7a4fa6a0 AJ |
542 | struct resultSet expect = { |
543 | .parsed = false, | |
36a9c964 | 544 | .needsMore = true, |
7a4fa6a0 AJ |
545 | .parserState = Http1::HTTP_PARSE_MIME, |
546 | .status = Http::scOkay, | |
7a4fa6a0 | 547 | .suffixSz = 0, |
7a4fa6a0 | 548 | .method = HttpRequestMethod(Http::METHOD_GET), |
7a4fa6a0 | 549 | .uri = "/", |
7a4fa6a0 AJ |
550 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) |
551 | }; | |
36a9c964 | 552 | output.clear(); |
7a4fa6a0 | 553 | testResults(__LINE__, input, output, expect); |
4c14658e | 554 | |
947ca0c6 AJ |
555 | Config.onoff.relaxed_header_parser = 0; |
556 | struct resultSet expectStrict = { | |
557 | .parsed = false, | |
36a9c964 | 558 | .needsMore = false, |
7a4fa6a0 | 559 | .parserState = Http1::HTTP_PARSE_DONE, |
e02f963c AR |
560 | .status = Http::scBadRequest, |
561 | .suffixSz = input.length(), | |
562 | .method = HttpRequestMethod(), | |
563 | .uri = NULL, | |
947ca0c6 | 564 | .version = AnyP::ProtocolVersion() |
7a4fa6a0 | 565 | }; |
36a9c964 | 566 | output.clear(); |
947ca0c6 | 567 | testResults(__LINE__, input, output, expectStrict); |
7a4fa6a0 | 568 | input.clear(); |
4245f3b9 | 569 | } |
4c14658e | 570 | |
947ca0c6 AJ |
571 | // alternative EOL sequence: double-NL-only |
572 | // RFC 7230 tolerance permits omitted CR | |
573 | // NP: represents a request with no mime headers | |
4245f3b9 | 574 | { |
947ca0c6 | 575 | input.append("GET / HTTP/1.1\n\n", 16); |
7a4fa6a0 | 576 | Config.onoff.relaxed_header_parser = 1; |
947ca0c6 AJ |
577 | struct resultSet expect = { |
578 | .parsed = true, | |
579 | .needsMore = false, | |
580 | .parserState = Http1::HTTP_PARSE_DONE, | |
7a4fa6a0 | 581 | .status = Http::scOkay, |
7a4fa6a0 | 582 | .suffixSz = 0, |
7a4fa6a0 | 583 | .method = HttpRequestMethod(Http::METHOD_GET), |
7a4fa6a0 | 584 | .uri = "/", |
7a4fa6a0 AJ |
585 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) |
586 | }; | |
36a9c964 | 587 | output.clear(); |
947ca0c6 | 588 | testResults(__LINE__, input, output, expect); |
4c14658e | 589 | |
4245f3b9 | 590 | Config.onoff.relaxed_header_parser = 0; |
7a4fa6a0 AJ |
591 | struct resultSet expectStrict = { |
592 | .parsed = false, | |
36a9c964 | 593 | .needsMore = false, |
947ca0c6 | 594 | .parserState = Http1::HTTP_PARSE_DONE, |
e02f963c AR |
595 | .status = Http::scBadRequest, |
596 | .suffixSz = input.length(), | |
597 | .method = HttpRequestMethod(), | |
598 | .uri = NULL, | |
7a4fa6a0 AJ |
599 | .version = AnyP::ProtocolVersion() |
600 | }; | |
36a9c964 | 601 | output.clear(); |
7a4fa6a0 AJ |
602 | testResults(__LINE__, input, output, expectStrict); |
603 | input.clear(); | |
4245f3b9 | 604 | } |
4c14658e | 605 | |
8c730cae | 606 | // space padded version |
4245f3b9 | 607 | { |
947ca0c6 AJ |
608 | // RFC 7230 specifies version is followed by CRLF. No intermediary bytes. |
609 | input.append("GET / HTTP/1.1 \r\n", 17); | |
7a4fa6a0 AJ |
610 | struct resultSet expect = { |
611 | .parsed = false, | |
36a9c964 | 612 | .needsMore = false, |
7a4fa6a0 | 613 | .parserState = Http1::HTTP_PARSE_DONE, |
e02f963c AR |
614 | .status = Http::scBadRequest, |
615 | .suffixSz = input.length(), | |
616 | .method = HttpRequestMethod(), | |
617 | .uri = NULL, | |
7a4fa6a0 AJ |
618 | .version = AnyP::ProtocolVersion() |
619 | }; | |
36a9c964 | 620 | output.clear(); |
7a4fa6a0 AJ |
621 | testResults(__LINE__, input, output, expect); |
622 | input.clear(); | |
4245f3b9 | 623 | } |
8c730cae AJ |
624 | } |
625 | ||
626 | void | |
bb86dcd4 | 627 | testHttp1Parser::testParseRequestLineMethods() |
8c730cae AJ |
628 | { |
629 | // ensure MemPools etc exist | |
630 | globalSetup(); | |
631 | ||
7a4fa6a0 | 632 | SBuf input; |
1b51ee7b | 633 | Http1::RequestParser output; |
8c730cae | 634 | |
947ca0c6 | 635 | // RFC 7230 : dot method |
4245f3b9 | 636 | { |
947ca0c6 | 637 | input.append(". / HTTP/1.1\r\n", 14); |
7a4fa6a0 AJ |
638 | struct resultSet expect = { |
639 | .parsed = false, | |
36a9c964 | 640 | .needsMore = true, |
7a4fa6a0 AJ |
641 | .parserState = Http1::HTTP_PARSE_MIME, |
642 | .status = Http::scOkay, | |
7a4fa6a0 | 643 | .suffixSz = 0, |
f9688132 | 644 | .method = HttpRequestMethod(SBuf(".")), |
7a4fa6a0 | 645 | .uri = "/", |
947ca0c6 AJ |
646 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) |
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 = "/", | |
7a4fa6a0 AJ |
664 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) |
665 | }; | |
36a9c964 | 666 | output.clear(); |
7a4fa6a0 AJ |
667 | testResults(__LINE__, input, output, expect); |
668 | input.clear(); | |
4245f3b9 | 669 | } |
4c14658e AJ |
670 | |
671 | // OPTIONS with * URL | |
4245f3b9 | 672 | { |
947ca0c6 | 673 | input.append("OPTIONS * HTTP/1.1\r\n", 20); |
7a4fa6a0 AJ |
674 | struct resultSet expect = { |
675 | .parsed = false, | |
36a9c964 | 676 | .needsMore = true, |
7a4fa6a0 AJ |
677 | .parserState = Http1::HTTP_PARSE_MIME, |
678 | .status = Http::scOkay, | |
7a4fa6a0 | 679 | .suffixSz = 0, |
7a4fa6a0 | 680 | .method = HttpRequestMethod(Http::METHOD_OPTIONS), |
7a4fa6a0 | 681 | .uri = "*", |
7a4fa6a0 AJ |
682 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) |
683 | }; | |
36a9c964 | 684 | output.clear(); |
7a4fa6a0 AJ |
685 | testResults(__LINE__, input, output, expect); |
686 | input.clear(); | |
4245f3b9 | 687 | } |
4c14658e AJ |
688 | |
689 | // unknown method | |
4245f3b9 | 690 | { |
947ca0c6 | 691 | input.append("HELLOWORLD / HTTP/1.1\r\n", 23); |
7a4fa6a0 AJ |
692 | struct resultSet expect = { |
693 | .parsed = false, | |
36a9c964 | 694 | .needsMore = true, |
7a4fa6a0 AJ |
695 | .parserState = Http1::HTTP_PARSE_MIME, |
696 | .status = Http::scOkay, | |
7a4fa6a0 | 697 | .suffixSz = 0, |
f9688132 | 698 | .method = HttpRequestMethod(SBuf("HELLOWORLD")), |
7a4fa6a0 | 699 | .uri = "/", |
7a4fa6a0 AJ |
700 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) |
701 | }; | |
36a9c964 | 702 | output.clear(); |
7a4fa6a0 AJ |
703 | testResults(__LINE__, input, output, expect); |
704 | input.clear(); | |
4245f3b9 | 705 | } |
4c14658e | 706 | |
e02f963c | 707 | #if 0 |
947ca0c6 AJ |
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 | } | |
e02f963c | 725 | #endif |
947ca0c6 | 726 | |
8c730cae | 727 | // method-only |
4245f3b9 AJ |
728 | { |
729 | input.append("A\n", 2); | |
7a4fa6a0 AJ |
730 | struct resultSet expect = { |
731 | .parsed = false, | |
36a9c964 | 732 | .needsMore = false, |
7a4fa6a0 AJ |
733 | .parserState = Http1::HTTP_PARSE_DONE, |
734 | .status = Http::scBadRequest, | |
7a4fa6a0 | 735 | .suffixSz = input.length(), |
7a4fa6a0 | 736 | .method = HttpRequestMethod(), |
7a4fa6a0 | 737 | .uri = NULL, |
7a4fa6a0 AJ |
738 | .version = AnyP::ProtocolVersion() |
739 | }; | |
36a9c964 | 740 | output.clear(); |
7a4fa6a0 AJ |
741 | testResults(__LINE__, input, output, expect); |
742 | input.clear(); | |
4245f3b9 | 743 | } |
8c730cae | 744 | |
4245f3b9 | 745 | { |
7a4fa6a0 AJ |
746 | input.append("GET\n", 4); |
747 | struct resultSet expect = { | |
748 | .parsed = false, | |
36a9c964 | 749 | .needsMore = false, |
7a4fa6a0 AJ |
750 | .parserState = Http1::HTTP_PARSE_DONE, |
751 | .status = Http::scBadRequest, | |
7a4fa6a0 | 752 | .suffixSz = input.length(), |
7a4fa6a0 | 753 | .method = HttpRequestMethod(), |
7a4fa6a0 | 754 | .uri = NULL, |
7a4fa6a0 AJ |
755 | .version = AnyP::ProtocolVersion() |
756 | }; | |
36a9c964 | 757 | output.clear(); |
7a4fa6a0 AJ |
758 | testResults(__LINE__, input, output, expect); |
759 | input.clear(); | |
4245f3b9 | 760 | } |
4c14658e | 761 | |
947ca0c6 | 762 | // space padded method (SP is reserved so invalid as a method byte) |
4245f3b9 | 763 | { |
947ca0c6 AJ |
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 | |
4245f3b9 | 781 | { |
947ca0c6 | 782 | input.append("\r\n\r\n\nGET / HTTP/1.1\r\n", 21); |
4245f3b9 | 783 | Config.onoff.relaxed_header_parser = 1; |
947ca0c6 | 784 | struct resultSet expect = { |
7a4fa6a0 | 785 | .parsed = false, |
36a9c964 | 786 | .needsMore = true, |
7a4fa6a0 AJ |
787 | .parserState = Http1::HTTP_PARSE_MIME, |
788 | .status = Http::scOkay, | |
7a4fa6a0 | 789 | .suffixSz = 0, |
7a4fa6a0 | 790 | .method = HttpRequestMethod(Http::METHOD_GET), |
7a4fa6a0 | 791 | .uri = "/", |
7a4fa6a0 AJ |
792 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) |
793 | }; | |
36a9c964 | 794 | output.clear(); |
947ca0c6 | 795 | testResults(__LINE__, input, output, expect); |
7a4fa6a0 | 796 | |
f4880526 | 797 | Config.onoff.relaxed_header_parser = 0; |
7a4fa6a0 AJ |
798 | struct resultSet expectStrict = { |
799 | .parsed = false, | |
36a9c964 | 800 | .needsMore = false, |
7a4fa6a0 AJ |
801 | .parserState = Http1::HTTP_PARSE_DONE, |
802 | .status = Http::scBadRequest, | |
7a4fa6a0 | 803 | .suffixSz = input.length(), |
7a4fa6a0 | 804 | .method = HttpRequestMethod(), |
7a4fa6a0 | 805 | .uri = NULL, |
7a4fa6a0 AJ |
806 | .version = AnyP::ProtocolVersion() |
807 | }; | |
36a9c964 | 808 | output.clear(); |
7a4fa6a0 AJ |
809 | testResults(__LINE__, input, output, expectStrict); |
810 | input.clear(); | |
4245f3b9 | 811 | } |
4c14658e | 812 | |
947ca0c6 | 813 | // forbidden character in method |
4245f3b9 | 814 | { |
947ca0c6 | 815 | input.append("\tGET / HTTP/1.1\r\n", 17); |
7a4fa6a0 AJ |
816 | struct resultSet expect = { |
817 | .parsed = false, | |
36a9c964 | 818 | .needsMore = false, |
947ca0c6 AJ |
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); | |
835 | Config.onoff.relaxed_header_parser = 1; | |
836 | struct resultSet expect = { | |
837 | .parsed = false, | |
838 | .needsMore = true, | |
7a4fa6a0 AJ |
839 | .parserState = Http1::HTTP_PARSE_MIME, |
840 | .status = Http::scOkay, | |
947ca0c6 | 841 | .suffixSz = 0, |
7a4fa6a0 | 842 | .method = HttpRequestMethod(Http::METHOD_GET), |
7a4fa6a0 | 843 | .uri = "/", |
7a4fa6a0 AJ |
844 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) |
845 | }; | |
36a9c964 | 846 | output.clear(); |
7a4fa6a0 | 847 | testResults(__LINE__, input, output, expect); |
947ca0c6 AJ |
848 | |
849 | Config.onoff.relaxed_header_parser = 0; | |
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); | |
7a4fa6a0 | 862 | input.clear(); |
4245f3b9 | 863 | } |
4c14658e | 864 | |
947ca0c6 | 865 | // tolerant parser delimiters |
4245f3b9 | 866 | { |
947ca0c6 AJ |
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); | |
869 | Config.onoff.relaxed_header_parser = 1; | |
7a4fa6a0 | 870 | struct resultSet expect = { |
947ca0c6 AJ |
871 | .parsed = false, |
872 | .needsMore = true, | |
873 | .parserState = Http1::HTTP_PARSE_MIME, | |
874 | .status = Http::scOkay, | |
875 | .suffixSz = 0, | |
876 | .method = HttpRequestMethod(Http::METHOD_GET), | |
877 | .uri = "/", | |
878 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) | |
879 | }; | |
880 | output.clear(); | |
881 | testResults(__LINE__, input, output, expect); | |
882 | ||
883 | Config.onoff.relaxed_header_parser = 0; | |
884 | struct resultSet expectStrict = { | |
7a4fa6a0 | 885 | .parsed = false, |
36a9c964 | 886 | .needsMore = false, |
7a4fa6a0 AJ |
887 | .parserState = Http1::HTTP_PARSE_DONE, |
888 | .status = Http::scBadRequest, | |
7a4fa6a0 | 889 | .suffixSz = input.length(), |
7a4fa6a0 | 890 | .method = HttpRequestMethod(), |
7a4fa6a0 | 891 | .uri = NULL, |
7a4fa6a0 AJ |
892 | .version = AnyP::ProtocolVersion() |
893 | }; | |
36a9c964 | 894 | output.clear(); |
947ca0c6 | 895 | testResults(__LINE__, input, output, expectStrict); |
7a4fa6a0 | 896 | input.clear(); |
4245f3b9 | 897 | } |
8c730cae | 898 | } |
4c14658e | 899 | |
8c730cae | 900 | void |
bb86dcd4 | 901 | testHttp1Parser::testParseRequestLineInvalid() |
8c730cae AJ |
902 | { |
903 | // ensure MemPools etc exist | |
904 | globalSetup(); | |
4c14658e | 905 | |
7a4fa6a0 | 906 | SBuf input; |
1b51ee7b | 907 | Http1::RequestParser output; |
4c14658e | 908 | |
947ca0c6 | 909 | // no method (or method delimiter) |
4245f3b9 | 910 | { |
947ca0c6 | 911 | // HTTP/0.9 requires method to be "GET" |
4245f3b9 | 912 | input.append("/ HTTP/1.0\n", 11); |
7a4fa6a0 | 913 | struct resultSet expect = { |
7a4fa6a0 | 914 | .parsed = false, |
36a9c964 | 915 | .needsMore = false, |
7a4fa6a0 AJ |
916 | .parserState = Http1::HTTP_PARSE_DONE, |
917 | .status = Http::scBadRequest, | |
7a4fa6a0 | 918 | .suffixSz = input.length(), |
7a4fa6a0 | 919 | .method = HttpRequestMethod(), |
7a4fa6a0 | 920 | .uri = NULL, |
7a4fa6a0 AJ |
921 | .version = AnyP::ProtocolVersion() |
922 | }; | |
36a9c964 | 923 | output.clear(); |
947ca0c6 | 924 | testResults(__LINE__, input, output, expect); |
7a4fa6a0 | 925 | input.clear(); |
4245f3b9 | 926 | } |
4c14658e | 927 | |
947ca0c6 | 928 | // no method (with method delimiter) |
4245f3b9 | 929 | { |
947ca0c6 AJ |
930 | input.append(" / HTTP/1.0\n", 12); |
931 | struct resultSet expectStrict = { | |
7a4fa6a0 | 932 | .parsed = false, |
36a9c964 | 933 | .needsMore = false, |
7a4fa6a0 AJ |
934 | .parserState = Http1::HTTP_PARSE_DONE, |
935 | .status = Http::scBadRequest, | |
7a4fa6a0 | 936 | .suffixSz = input.length(), |
7a4fa6a0 | 937 | .method = HttpRequestMethod(), |
7a4fa6a0 | 938 | .uri = NULL, |
7a4fa6a0 AJ |
939 | .version = AnyP::ProtocolVersion() |
940 | }; | |
36a9c964 | 941 | output.clear(); |
947ca0c6 | 942 | testResults(__LINE__, input, output, expectStrict); |
7a4fa6a0 | 943 | input.clear(); |
4245f3b9 | 944 | } |
4c14658e | 945 | |
e02f963c | 946 | // binary code after method (invalid) |
4245f3b9 | 947 | { |
e02f963c | 948 | input.append("GET\x16 / HTTP/1.1\r\n", 17); |
7a4fa6a0 AJ |
949 | struct resultSet expect = { |
950 | .parsed = false, | |
36a9c964 | 951 | .needsMore = false, |
7a4fa6a0 AJ |
952 | .parserState = Http1::HTTP_PARSE_DONE, |
953 | .status = Http::scBadRequest, | |
7a4fa6a0 | 954 | .suffixSz = input.length(), |
7a4fa6a0 | 955 | .method = HttpRequestMethod(), |
7a4fa6a0 | 956 | .uri = NULL, |
7a4fa6a0 AJ |
957 | .version = AnyP::ProtocolVersion() |
958 | }; | |
36a9c964 | 959 | output.clear(); |
7a4fa6a0 AJ |
960 | testResults(__LINE__, input, output, expect); |
961 | input.clear(); | |
4245f3b9 | 962 | } |
4c14658e | 963 | |
e02f963c | 964 | // binary code NUL! after method (always invalid) |
4245f3b9 | 965 | { |
947ca0c6 | 966 | input.append("GET\0 / HTTP/1.1\r\n", 17); |
7a4fa6a0 AJ |
967 | struct resultSet expect = { |
968 | .parsed = false, | |
36a9c964 | 969 | .needsMore = false, |
7a4fa6a0 AJ |
970 | .parserState = Http1::HTTP_PARSE_DONE, |
971 | .status = Http::scBadRequest, | |
7a4fa6a0 | 972 | .suffixSz = input.length(), |
7a4fa6a0 | 973 | .method = HttpRequestMethod(), |
7a4fa6a0 | 974 | .uri = NULL, |
7a4fa6a0 AJ |
975 | .version = AnyP::ProtocolVersion() |
976 | }; | |
36a9c964 | 977 | output.clear(); |
7a4fa6a0 AJ |
978 | testResults(__LINE__, input, output, expect); |
979 | input.clear(); | |
4245f3b9 | 980 | } |
4c14658e | 981 | |
e02f963c AR |
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. | |
4245f3b9 | 984 | { |
947ca0c6 | 985 | input.append("GET HTTP/1.1\r\n", 15); |
947ca0c6 | 986 | Config.onoff.relaxed_header_parser = 1; |
7a4fa6a0 | 987 | struct resultSet expect = { |
e02f963c | 988 | .parsed = false, |
36a9c964 | 989 | .needsMore = false, |
7a4fa6a0 | 990 | .parserState = Http1::HTTP_PARSE_DONE, |
e02f963c AR |
991 | .status = Http::scBadRequest, |
992 | .suffixSz = input.length(), | |
993 | .method = HttpRequestMethod(), | |
994 | .uri = NULL, | |
995 | .version = AnyP::ProtocolVersion() | |
7a4fa6a0 | 996 | }; |
36a9c964 | 997 | output.clear(); |
7a4fa6a0 | 998 | testResults(__LINE__, input, output, expect); |
947ca0c6 AJ |
999 | |
1000 | Config.onoff.relaxed_header_parser = 0; | |
1001 | struct resultSet expectStrict = { | |
1002 | .parsed = false, | |
1003 | .needsMore = false, | |
1004 | .parserState = Http1::HTTP_PARSE_DONE, | |
1005 | .status = Http::scBadRequest, | |
e02f963c AR |
1006 | .suffixSz = input.length(), |
1007 | .method = HttpRequestMethod(), | |
947ca0c6 AJ |
1008 | .uri = NULL, |
1009 | .version = AnyP::ProtocolVersion() | |
1010 | }; | |
1011 | output.clear(); | |
1012 | testResults(__LINE__, input, output, expectStrict); | |
7a4fa6a0 | 1013 | input.clear(); |
4245f3b9 | 1014 | } |
4c14658e | 1015 | |
e02f963c AR |
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. | |
4245f3b9 | 1018 | { |
947ca0c6 | 1019 | input.append("GET HTTP/1.1\r\n", 14); |
7a4fa6a0 | 1020 | struct resultSet expect = { |
e02f963c | 1021 | .parsed = false, |
36a9c964 | 1022 | .needsMore = false, |
7a4fa6a0 | 1023 | .parserState = Http1::HTTP_PARSE_DONE, |
e02f963c AR |
1024 | .status = Http::scBadRequest, |
1025 | .suffixSz = input.length(), | |
1026 | .method = HttpRequestMethod(), | |
1027 | .uri = NULL, | |
1028 | .version = AnyP::ProtocolVersion() | |
7a4fa6a0 | 1029 | }; |
36a9c964 | 1030 | output.clear(); |
7a4fa6a0 AJ |
1031 | testResults(__LINE__, input, output, expect); |
1032 | input.clear(); | |
4245f3b9 | 1033 | } |
4c14658e | 1034 | |
4c14658e | 1035 | // binary line |
4245f3b9 AJ |
1036 | { |
1037 | input.append("\xB\xC\xE\xF\n", 5); | |
7a4fa6a0 AJ |
1038 | struct resultSet expect = { |
1039 | .parsed = false, | |
36a9c964 | 1040 | .needsMore = false, |
7a4fa6a0 AJ |
1041 | .parserState = Http1::HTTP_PARSE_DONE, |
1042 | .status = Http::scBadRequest, | |
7a4fa6a0 | 1043 | .suffixSz = input.length(), |
7a4fa6a0 | 1044 | .method = HttpRequestMethod(), |
7a4fa6a0 | 1045 | .uri = NULL, |
7a4fa6a0 AJ |
1046 | .version = AnyP::ProtocolVersion() |
1047 | }; | |
36a9c964 | 1048 | output.clear(); |
7a4fa6a0 AJ |
1049 | testResults(__LINE__, input, output, expect); |
1050 | input.clear(); | |
4245f3b9 | 1051 | } |
4c14658e AJ |
1052 | |
1053 | // mixed whitespace line | |
4245f3b9 | 1054 | { |
4245f3b9 | 1055 | input.append("\t \t \t\n", 6); |
7a4fa6a0 AJ |
1056 | struct resultSet expect = { |
1057 | .parsed = false, | |
36a9c964 | 1058 | .needsMore = false, |
7a4fa6a0 AJ |
1059 | .parserState = Http1::HTTP_PARSE_DONE, |
1060 | .status = Http::scBadRequest, | |
7a4fa6a0 | 1061 | .suffixSz = input.length(), |
3248e962 | 1062 | .method = HttpRequestMethod(), |
7a4fa6a0 | 1063 | .uri = NULL, |
7a4fa6a0 AJ |
1064 | .version = AnyP::ProtocolVersion() |
1065 | }; | |
36a9c964 | 1066 | output.clear(); |
7a4fa6a0 AJ |
1067 | testResults(__LINE__, input, output, expect); |
1068 | input.clear(); | |
4245f3b9 | 1069 | } |
4c14658e | 1070 | |
947ca0c6 | 1071 | // mixed whitespace line with CR |
4245f3b9 | 1072 | { |
947ca0c6 | 1073 | input.append("\r \t \n", 6); |
7a4fa6a0 AJ |
1074 | struct resultSet expect = { |
1075 | .parsed = false, | |
36a9c964 | 1076 | .needsMore = false, |
7a4fa6a0 AJ |
1077 | .parserState = Http1::HTTP_PARSE_DONE, |
1078 | .status = Http::scBadRequest, | |
7a4fa6a0 | 1079 | .suffixSz = input.length(), |
7a4fa6a0 | 1080 | .method = HttpRequestMethod(), |
7a4fa6a0 | 1081 | .uri = NULL, |
7a4fa6a0 AJ |
1082 | .version = AnyP::ProtocolVersion() |
1083 | }; | |
36a9c964 | 1084 | output.clear(); |
7a4fa6a0 AJ |
1085 | testResults(__LINE__, input, output, expect); |
1086 | input.clear(); | |
4245f3b9 | 1087 | } |
4c14658e | 1088 | } |
b6a7fc85 AJ |
1089 | |
1090 | void | |
bb86dcd4 | 1091 | testHttp1Parser::testDripFeed() |
b6a7fc85 AJ |
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 | ||
7a4fa6a0 | 1097 | SBuf data; |
ebbb125d | 1098 | data.append("\n\n\n\n\n\n\n\n\n\n\n\n", 12); |
7a4fa6a0 | 1099 | SBuf::size_type garbageEnd = data.length(); |
947ca0c6 | 1100 | data.append("GET ", 4); |
947ca0c6 | 1101 | data.append("http://example.com/ ", 20); |
947ca0c6 | 1102 | data.append("HTTP/1.1\r\n", 10); |
7a4fa6a0 AJ |
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. | |
b6a7fc85 | 1107 | |
78a63ed1 | 1108 | SBuf ioBuf; |
36a9c964 | 1109 | Http1::RequestParser hp; |
b6a7fc85 | 1110 | |
78a63ed1 AJ |
1111 | // start with strict and move on to relaxed |
1112 | Config.onoff.relaxed_header_parser = 2; | |
7a4fa6a0 AJ |
1113 | |
1114 | Config.maxRequestHeaderSize = 1024; // large enough to hold the test data. | |
1115 | ||
78a63ed1 | 1116 | do { |
7a4fa6a0 | 1117 | |
78a63ed1 AJ |
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 | }; | |
f4880526 | 1129 | |
78a63ed1 AJ |
1130 | ioBuf.clear(); // begins empty for each parser type |
1131 | hp.clear(); | |
1132 | ||
1133 | --Config.onoff.relaxed_header_parser; | |
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) | |
1148 | expect.parserState = Http1::HTTP_PARSE_FIRST; | |
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 | ||
78a63ed1 AJ |
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) { | |
1157 | expect.parserState = Http1::HTTP_PARSE_MIME; | |
1158 | expect.suffixSz = 0; // and a checkpoint buffer reset | |
1159 | expect.status = Http::scOkay; | |
1160 | expect.method = HttpRequestMethod(Http::METHOD_GET); | |
1161 | expect.uri = "http://example.com/"; | |
1162 | expect.version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1); | |
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; | |
b6a7fc85 AJ |
1181 | } |
1182 | ||
78a63ed1 | 1183 | } while (Config.onoff.relaxed_header_parser); |
7a4fa6a0 | 1184 | |
b6a7fc85 | 1185 | } |
f53969cc | 1186 |