]>
Commit | Line | Data |
---|---|---|
48a37aee AJ |
1 | /* |
2 | * Copyright (C) 1996-2014 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 | ||
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 | ||
bb86dcd4 | 16 | #include "testHttp1Parser.h" |
c99510dd | 17 | #include "http/one/RequestParser.h" |
274bd5ad | 18 | #include "http/RequestMethod.h" |
4c14658e AJ |
19 | #include "Mem.h" |
20 | #include "MemBuf.h" | |
4d5904f7 | 21 | #include "SquidConfig.h" |
bb86dcd4 | 22 | #include "testHttp1Parser.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; | |
47 | int msgStart; | |
48 | int msgEnd; | |
49 | SBuf::size_type suffixSz; | |
50 | int methodStart; | |
51 | int methodEnd; | |
52 | HttpRequestMethod method; | |
53 | int uriStart; | |
54 | int uriEnd; | |
55 | const char *uri; | |
56 | int versionStart; | |
57 | int versionEnd; | |
58 | AnyP::ProtocolVersion version; | |
59 | }; | |
60 | ||
61 | static void | |
62 | testResults(int line, const SBuf &input, Http1::RequestParser &output, struct resultSet &expect) | |
63 | { | |
64 | #if WHEN_TEST_DEBUG_IS_NEEDED | |
65 | printf("TEST @%d, in=%u: " SQUIDSBUFPH "\n", line, input.length(), SQUIDSBUFPRINT(input)); | |
66 | #endif | |
67 | ||
36a9c964 AJ |
68 | CPPUNIT_ASSERT_EQUAL(expect.parsed, output.parse(input)); |
69 | CPPUNIT_ASSERT_EQUAL(expect.needsMore, output.needsMoreData()); | |
70 | if (output.needsMoreData()) | |
7a4fa6a0 AJ |
71 | CPPUNIT_ASSERT_EQUAL(expect.parserState, output.parsingStage_); |
72 | CPPUNIT_ASSERT_EQUAL(expect.status, output.request_parse_status); | |
73 | CPPUNIT_ASSERT_EQUAL(expect.msgStart, output.req.start); | |
74 | CPPUNIT_ASSERT_EQUAL(expect.msgEnd, output.req.end); | |
b749de75 | 75 | CPPUNIT_ASSERT_EQUAL(expect.suffixSz, output.buf_.length()); |
7a4fa6a0 AJ |
76 | CPPUNIT_ASSERT_EQUAL(expect.methodStart, output.req.m_start); |
77 | CPPUNIT_ASSERT_EQUAL(expect.methodEnd, output.req.m_end); | |
78 | CPPUNIT_ASSERT_EQUAL(expect.method, output.method_); | |
79 | CPPUNIT_ASSERT_EQUAL(expect.uriStart, output.req.u_start); | |
80 | CPPUNIT_ASSERT_EQUAL(expect.uriEnd, output.req.u_end); | |
81 | if (expect.uri != NULL) | |
82 | CPPUNIT_ASSERT_EQUAL(0, output.uri_.cmp(expect.uri)); | |
83 | CPPUNIT_ASSERT_EQUAL(expect.versionStart, output.req.v_start); | |
84 | CPPUNIT_ASSERT_EQUAL(expect.versionEnd, output.req.v_end); | |
85 | CPPUNIT_ASSERT_EQUAL(expect.version, output.msgProtocol_); | |
86 | } | |
87 | ||
10c0d360 AJ |
88 | void |
89 | testHttp1Parser::testParserConstruct() | |
90 | { | |
91 | // whether the constructor works | |
92 | { | |
93 | Http1::RequestParser output; | |
94 | CPPUNIT_ASSERT_EQUAL(true, output.needsMoreData()); | |
95 | CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_NONE, output.parsingStage_); | |
96 | CPPUNIT_ASSERT_EQUAL(Http::scNone, output.request_parse_status); // XXX: clear() not being called. | |
97 | CPPUNIT_ASSERT_EQUAL(-1, output.req.start); | |
98 | CPPUNIT_ASSERT_EQUAL(-1, output.req.end); | |
99 | CPPUNIT_ASSERT(output.buf_.isEmpty()); | |
100 | CPPUNIT_ASSERT_EQUAL(-1, output.req.m_start); | |
101 | CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end); | |
102 | CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_NONE), output.method_); | |
103 | CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start); | |
104 | CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end); | |
105 | CPPUNIT_ASSERT(output.uri_.isEmpty()); | |
106 | CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start); | |
107 | CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end); | |
108 | CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_); | |
109 | } | |
110 | ||
111 | // whether new() works | |
112 | { | |
113 | Http1::RequestParser *output = new Http1::RequestParser; | |
114 | CPPUNIT_ASSERT_EQUAL(true, output->needsMoreData()); | |
115 | CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_NONE, output->parsingStage_); | |
116 | CPPUNIT_ASSERT_EQUAL(Http::scNone, output->request_parse_status); | |
117 | CPPUNIT_ASSERT_EQUAL(-1, output->req.start); | |
118 | CPPUNIT_ASSERT_EQUAL(-1, output->req.end); | |
119 | CPPUNIT_ASSERT(output->buf_.isEmpty()); | |
120 | CPPUNIT_ASSERT_EQUAL(-1, output->req.m_start); | |
121 | CPPUNIT_ASSERT_EQUAL(-1, output->req.m_end); | |
122 | CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_NONE), output->method_); | |
123 | CPPUNIT_ASSERT_EQUAL(-1, output->req.u_start); | |
124 | CPPUNIT_ASSERT_EQUAL(-1, output->req.u_end); | |
125 | CPPUNIT_ASSERT(output->uri_.isEmpty()); | |
126 | CPPUNIT_ASSERT_EQUAL(-1, output->req.v_start); | |
127 | CPPUNIT_ASSERT_EQUAL(-1, output->req.v_end); | |
128 | CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output->msgProtocol_); | |
129 | delete output; | |
130 | } | |
131 | } | |
132 | ||
4c14658e | 133 | void |
bb86dcd4 | 134 | testHttp1Parser::testParseRequestLineProtocols() |
4c14658e AJ |
135 | { |
136 | // ensure MemPools etc exist | |
137 | globalSetup(); | |
138 | ||
7a4fa6a0 | 139 | SBuf input; |
1b51ee7b | 140 | Http1::RequestParser output; |
4c14658e AJ |
141 | |
142 | // TEST: Do we comply with RFC 1945 section 5.1 ? | |
143 | // TEST: Do we comply with RFC 2616 section 5.1 ? | |
144 | ||
145 | // RFC 1945 : HTTP/0.9 simple-request | |
4245f3b9 AJ |
146 | { |
147 | input.append("GET /\r\n", 7); | |
7a4fa6a0 AJ |
148 | struct resultSet expect = { |
149 | .parsed = true, | |
36a9c964 | 150 | .needsMore = false, |
7a4fa6a0 AJ |
151 | .parserState = Http1::HTTP_PARSE_DONE, |
152 | .status = Http::scOkay, | |
153 | .msgStart = 0, | |
154 | .msgEnd = (int)input.length()-1, | |
155 | .suffixSz = 0, | |
156 | .methodStart = 0, | |
157 | .methodEnd = 2, | |
158 | .method = HttpRequestMethod(Http::METHOD_GET), | |
159 | .uriStart = 4, | |
160 | .uriEnd = 4, | |
161 | .uri = "/", | |
162 | .versionStart = -1, | |
163 | .versionEnd = -1, | |
164 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9) | |
165 | }; | |
36a9c964 | 166 | output.clear(); |
7a4fa6a0 AJ |
167 | testResults(__LINE__, input, output, expect); |
168 | input.clear(); | |
4245f3b9 | 169 | } |
4c14658e | 170 | |
8c730cae | 171 | // RFC 1945 : invalid HTTP/0.9 simple-request (only GET is valid) |
7a4fa6a0 | 172 | #if WHEN_RFC_COMPLIANT |
4245f3b9 AJ |
173 | { |
174 | input.append("POST /\r\n", 7); | |
7a4fa6a0 AJ |
175 | struct resultSet expect = { |
176 | .parsed = true, | |
36a9c964 | 177 | .needsMore = false, |
7a4fa6a0 AJ |
178 | .parserState = Http1::HTTP_PARSE_DONE, |
179 | .status = Http::scOkay, | |
180 | .msgStart = 0, | |
181 | .msgEnd = (int)input.length()-1, | |
182 | .suffixSz = 0, | |
183 | .methodStart = 0, | |
184 | .methodEnd = 3, | |
185 | .method = HttpRequestMethod(Http::METHOD_POST), | |
186 | .uriStart = 5, | |
187 | .uriEnd = 5, | |
188 | .uri = "/", | |
189 | .versionStart = -1, | |
190 | .versionEnd = -1, | |
191 | .version = AnyP::ProtocolVersion() | |
192 | }; | |
36a9c964 | 193 | output.clear(); |
7a4fa6a0 AJ |
194 | testResults(__LINE__, input, output, expect); |
195 | input.clear(); | |
4245f3b9 | 196 | } |
8c730cae | 197 | #endif |
8c730cae | 198 | // RFC 1945 and 2616 : 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, | |
206 | .msgStart = 0, | |
207 | .msgEnd = (int)input.length()-1, | |
208 | .suffixSz = 0, | |
209 | .methodStart = 0, | |
210 | .methodEnd = 2, | |
211 | .method = HttpRequestMethod(Http::METHOD_GET), | |
212 | .uriStart = 4, | |
213 | .uriEnd = 4, | |
214 | .uri = "/", | |
215 | .versionStart = 6, | |
216 | .versionEnd = 13, | |
217 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,0) | |
218 | }; | |
36a9c964 | 219 | output.clear(); |
7a4fa6a0 AJ |
220 | testResults(__LINE__, input, output, expect); |
221 | input.clear(); | |
4245f3b9 | 222 | } |
4c14658e | 223 | |
8c730cae | 224 | // RFC 2616 : HTTP/1.1 request |
4245f3b9 AJ |
225 | { |
226 | input.append("GET / HTTP/1.1\r\n", 16); | |
7a4fa6a0 AJ |
227 | struct resultSet expect = { |
228 | .parsed = false, | |
36a9c964 | 229 | .needsMore = true, |
7a4fa6a0 AJ |
230 | .parserState = Http1::HTTP_PARSE_MIME, |
231 | .status = Http::scOkay, | |
232 | .msgStart = 0, | |
233 | .msgEnd = (int)input.length()-1, | |
234 | .suffixSz = 0, | |
235 | .methodStart = 0, | |
236 | .methodEnd = 2, | |
237 | .method = HttpRequestMethod(Http::METHOD_GET), | |
238 | .uriStart = 4, | |
239 | .uriEnd = 4, | |
240 | .uri = "/", | |
241 | .versionStart = 6, | |
242 | .versionEnd = 13, | |
243 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) | |
244 | }; | |
36a9c964 | 245 | output.clear(); |
7a4fa6a0 AJ |
246 | testResults(__LINE__, input, output, expect); |
247 | input.clear(); | |
4245f3b9 | 248 | } |
4c14658e AJ |
249 | |
250 | // RFC 2616 : future version full-request | |
f4880526 AJ |
251 | { |
252 | input.append("GET / HTTP/1.2\r\n", 16); | |
7a4fa6a0 AJ |
253 | struct resultSet expect = { |
254 | .parsed = false, | |
36a9c964 | 255 | .needsMore = true, |
7a4fa6a0 AJ |
256 | .parserState = Http1::HTTP_PARSE_MIME, |
257 | .status = Http::scOkay, | |
258 | .msgStart = 0, | |
259 | .msgEnd = (int)input.length()-1, | |
260 | .suffixSz = 0, | |
261 | .methodStart = 0, | |
262 | .methodEnd = 2, | |
263 | .method = HttpRequestMethod(Http::METHOD_GET), | |
264 | .uriStart = 4, | |
265 | .uriEnd = 4, | |
266 | .uri = "/", | |
267 | .versionStart = 6, | |
268 | .versionEnd = 13, | |
269 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,2) | |
270 | }; | |
36a9c964 | 271 | output.clear(); |
7a4fa6a0 AJ |
272 | testResults(__LINE__, input, output, expect); |
273 | input.clear(); | |
4245f3b9 | 274 | } |
8c730cae | 275 | |
9651320a | 276 | // RFC 7230 : future versions do not use request-line syntax |
4245f3b9 | 277 | { |
4245f3b9 | 278 | input.append("GET / HTTP/10.12\r\n", 18); |
7a4fa6a0 AJ |
279 | struct resultSet expect = { |
280 | .parsed = false, | |
36a9c964 | 281 | .needsMore = false, |
7a4fa6a0 AJ |
282 | .parserState = Http1::HTTP_PARSE_MIME, |
283 | .status = Http::scHttpVersionNotSupported, | |
284 | .msgStart = 0, | |
285 | .msgEnd = (int)input.length()-1, | |
286 | .suffixSz = input.length(), | |
287 | .methodStart = 0, | |
288 | .methodEnd = 2, | |
289 | .method = HttpRequestMethod(Http::METHOD_GET), | |
290 | .uriStart = 4, | |
291 | .uriEnd = 4, | |
292 | .uri = "/", | |
293 | .versionStart = 6, | |
294 | .versionEnd = 15, | |
9651320a | 295 | .version = AnyP::ProtocolVersion() |
7a4fa6a0 | 296 | }; |
36a9c964 | 297 | output.clear(); |
7a4fa6a0 AJ |
298 | testResults(__LINE__, input, output, expect); |
299 | input.clear(); | |
4245f3b9 | 300 | } |
4c14658e | 301 | |
7a4fa6a0 | 302 | // unknown non-HTTP protocol names |
4245f3b9 | 303 | { |
4245f3b9 | 304 | input.append("GET / FOO/1.0\n", 14); |
7a4fa6a0 AJ |
305 | struct resultSet expect = { |
306 | .parsed = false, | |
36a9c964 | 307 | .needsMore = false, |
7a4fa6a0 AJ |
308 | .parserState = Http1::HTTP_PARSE_DONE, |
309 | .status = Http::scHttpVersionNotSupported, | |
310 | .msgStart = 0, | |
311 | .msgEnd = (int)input.length()-1, | |
312 | .suffixSz = input.length(), | |
313 | .methodStart = 0, | |
314 | .methodEnd = 2, | |
315 | .method = HttpRequestMethod(Http::METHOD_GET), | |
316 | .uriStart = 4, | |
317 | .uriEnd = 4, | |
318 | .uri = "/", | |
319 | .versionStart = 6, | |
320 | .versionEnd = 12, | |
321 | .version = AnyP::ProtocolVersion() | |
322 | }; | |
36a9c964 | 323 | output.clear(); |
7a4fa6a0 AJ |
324 | testResults(__LINE__, input, output, expect); |
325 | input.clear(); | |
4245f3b9 | 326 | } |
8c730cae AJ |
327 | |
328 | // no version | |
4245f3b9 AJ |
329 | { |
330 | input.append("GET / HTTP/\n", 12); | |
7a4fa6a0 AJ |
331 | struct resultSet expect = { |
332 | .parsed = false, | |
36a9c964 | 333 | .needsMore = false, |
7a4fa6a0 AJ |
334 | .parserState = Http1::HTTP_PARSE_DONE, |
335 | .status = Http::scHttpVersionNotSupported, | |
336 | .msgStart = 0, | |
337 | .msgEnd = (int)input.length()-1, | |
338 | .suffixSz = input.length(), | |
339 | .methodStart = 0, | |
340 | .methodEnd = 2, | |
341 | .method = HttpRequestMethod(Http::METHOD_GET), | |
342 | .uriStart = 4, | |
343 | .uriEnd = 4, | |
344 | .uri = "/", | |
345 | .versionStart = 6, | |
346 | .versionEnd = 10, | |
9651320a | 347 | .version = AnyP::ProtocolVersion() |
7a4fa6a0 | 348 | }; |
36a9c964 | 349 | output.clear(); |
7a4fa6a0 AJ |
350 | testResults(__LINE__, input, output, expect); |
351 | input.clear(); | |
4245f3b9 | 352 | } |
4c14658e | 353 | |
8c730cae | 354 | // no major version |
4245f3b9 AJ |
355 | { |
356 | input.append("GET / HTTP/.1\n", 14); | |
7a4fa6a0 AJ |
357 | struct resultSet expect = { |
358 | .parsed = false, | |
36a9c964 | 359 | .needsMore = false, |
7a4fa6a0 AJ |
360 | .parserState = Http1::HTTP_PARSE_DONE, |
361 | .status = Http::scHttpVersionNotSupported, | |
362 | .msgStart = 0, | |
363 | .msgEnd = (int)input.length()-1, | |
364 | .suffixSz = input.length(), | |
365 | .methodStart = 0, | |
366 | .methodEnd = 2, | |
367 | .method = HttpRequestMethod(Http::METHOD_GET), | |
368 | .uriStart = 4, | |
369 | .uriEnd = 4, | |
370 | .uri = "/", | |
371 | .versionStart = 6, | |
372 | .versionEnd = 12, | |
9651320a | 373 | .version = AnyP::ProtocolVersion() |
7a4fa6a0 | 374 | }; |
36a9c964 | 375 | output.clear(); |
7a4fa6a0 AJ |
376 | testResults(__LINE__, input, output, expect); |
377 | input.clear(); | |
4245f3b9 | 378 | } |
8c730cae AJ |
379 | |
380 | // no version dot | |
4245f3b9 AJ |
381 | { |
382 | input.append("GET / HTTP/11\n", 14); | |
7a4fa6a0 AJ |
383 | struct resultSet expect = { |
384 | .parsed = false, | |
36a9c964 | 385 | .needsMore = false, |
7a4fa6a0 AJ |
386 | .parserState = Http1::HTTP_PARSE_DONE, |
387 | .status = Http::scHttpVersionNotSupported, | |
388 | .msgStart = 0, | |
389 | .msgEnd = (int)input.length()-1, | |
390 | .suffixSz = input.length(), | |
391 | .methodStart = 0, | |
392 | .methodEnd = 2, | |
393 | .method = HttpRequestMethod(Http::METHOD_GET), | |
394 | .uriStart = 4, | |
395 | .uriEnd = 4, | |
396 | .uri = "/", | |
397 | .versionStart = 6, | |
398 | .versionEnd = 12, | |
9651320a | 399 | .version = AnyP::ProtocolVersion() |
7a4fa6a0 | 400 | }; |
36a9c964 | 401 | output.clear(); |
7a4fa6a0 AJ |
402 | testResults(__LINE__, input, output, expect); |
403 | input.clear(); | |
4245f3b9 | 404 | } |
8c730cae AJ |
405 | |
406 | // negative major version (bug 3062) | |
4245f3b9 AJ |
407 | { |
408 | input.append("GET / HTTP/-999999.1\n", 21); | |
7a4fa6a0 AJ |
409 | struct resultSet expect = { |
410 | .parsed = false, | |
36a9c964 | 411 | .needsMore = false, |
7a4fa6a0 AJ |
412 | .parserState = Http1::HTTP_PARSE_DONE, |
413 | .status = Http::scHttpVersionNotSupported, | |
414 | .msgStart = 0, | |
415 | .msgEnd = (int)input.length()-1, | |
416 | .suffixSz = input.length(), | |
417 | .methodStart = 0, | |
418 | .methodEnd = 2, | |
419 | .method = HttpRequestMethod(Http::METHOD_GET), | |
420 | .uriStart = 4, | |
421 | .uriEnd = 4, | |
422 | .uri = "/", | |
423 | .versionStart = 6, | |
424 | .versionEnd = 19, | |
9651320a | 425 | .version = AnyP::ProtocolVersion() |
7a4fa6a0 | 426 | }; |
36a9c964 | 427 | output.clear(); |
7a4fa6a0 AJ |
428 | testResults(__LINE__, input, output, expect); |
429 | input.clear(); | |
4245f3b9 | 430 | } |
4c14658e | 431 | |
8c730cae | 432 | // no minor version |
4245f3b9 AJ |
433 | { |
434 | input.append("GET / HTTP/1.\n", 14); | |
7a4fa6a0 AJ |
435 | struct resultSet expect = { |
436 | .parsed = false, | |
36a9c964 | 437 | .needsMore = false, |
7a4fa6a0 AJ |
438 | .parserState = Http1::HTTP_PARSE_DONE, |
439 | .status = Http::scHttpVersionNotSupported, | |
440 | .msgStart = 0, | |
441 | .msgEnd = (int)input.length()-1, | |
442 | .suffixSz = input.length(), | |
443 | .methodStart = 0, | |
444 | .methodEnd = 2, | |
445 | .method = HttpRequestMethod(Http::METHOD_GET), | |
446 | .uriStart = 4, | |
447 | .uriEnd = 4, | |
448 | .uri = "/", | |
449 | .versionStart = 6, | |
450 | .versionEnd = 12, | |
451 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,0) | |
452 | }; | |
36a9c964 | 453 | output.clear(); |
7a4fa6a0 AJ |
454 | testResults(__LINE__, input, output, expect); |
455 | input.clear(); | |
4245f3b9 | 456 | } |
8c730cae AJ |
457 | |
458 | // negative major version (bug 3062 corollary) | |
4245f3b9 AJ |
459 | { |
460 | input.append("GET / HTTP/1.-999999\n", 21); | |
7a4fa6a0 AJ |
461 | struct resultSet expect = { |
462 | .parsed = false, | |
36a9c964 | 463 | .needsMore = false, |
7a4fa6a0 AJ |
464 | .parserState = Http1::HTTP_PARSE_DONE, |
465 | .status = Http::scHttpVersionNotSupported, | |
466 | .msgStart = 0, | |
467 | .msgEnd = (int)input.length()-1, | |
468 | .suffixSz = input.length(), | |
469 | .methodStart = 0, | |
470 | .methodEnd = 2, | |
471 | .method = HttpRequestMethod(Http::METHOD_GET), | |
472 | .uriStart = 4, | |
473 | .uriEnd = 4, | |
474 | .uri = "/", | |
475 | .versionStart = 6, | |
476 | .versionEnd = 19, | |
477 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,0) | |
478 | }; | |
36a9c964 | 479 | output.clear(); |
7a4fa6a0 AJ |
480 | testResults(__LINE__, input, output, expect); |
481 | input.clear(); | |
4245f3b9 | 482 | } |
8c730cae AJ |
483 | } |
484 | ||
485 | void | |
bb86dcd4 | 486 | testHttp1Parser::testParseRequestLineStrange() |
8c730cae AJ |
487 | { |
488 | // ensure MemPools etc exist | |
489 | globalSetup(); | |
490 | ||
7a4fa6a0 | 491 | SBuf input; |
1b51ee7b | 492 | Http1::RequestParser output; |
8c730cae AJ |
493 | |
494 | // space padded URL | |
4245f3b9 AJ |
495 | { |
496 | input.append("GET / HTTP/1.1\r\n", 21); | |
7a4fa6a0 AJ |
497 | struct resultSet expect = { |
498 | .parsed = false, | |
36a9c964 | 499 | .needsMore = true, |
7a4fa6a0 AJ |
500 | .parserState = Http1::HTTP_PARSE_MIME, |
501 | .status = Http::scOkay, | |
502 | .msgStart = 0, | |
503 | .msgEnd = (int)input.length()-1, | |
504 | .suffixSz = 0, | |
505 | .methodStart = 0, | |
506 | .methodEnd = 2, | |
507 | .method = HttpRequestMethod(Http::METHOD_GET), | |
508 | .uriStart = 5, | |
509 | .uriEnd = 5, | |
510 | .uri = "/", | |
511 | .versionStart = 11, | |
512 | .versionEnd = 18, | |
513 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) | |
514 | }; | |
36a9c964 | 515 | output.clear(); |
7a4fa6a0 AJ |
516 | testResults(__LINE__, input, output, expect); |
517 | input.clear(); | |
4245f3b9 | 518 | } |
8c730cae | 519 | |
4c14658e | 520 | // whitespace inside URI. (nasty but happens) |
7a4fa6a0 | 521 | // XXX: depends on tolerant parser... |
4245f3b9 AJ |
522 | { |
523 | input.append("GET /fo o/ HTTP/1.1\n", 20); | |
7a4fa6a0 AJ |
524 | struct resultSet expect = { |
525 | .parsed = false, | |
36a9c964 | 526 | .needsMore = true, |
7a4fa6a0 AJ |
527 | .parserState = Http1::HTTP_PARSE_MIME, |
528 | .status = Http::scOkay, | |
529 | .msgStart = 0, | |
530 | .msgEnd = (int)input.length()-1, | |
531 | .suffixSz = 0, | |
532 | .methodStart = 0, | |
533 | .methodEnd = 2, | |
534 | .method = HttpRequestMethod(Http::METHOD_GET), | |
535 | .uriStart = 4, | |
536 | .uriEnd = 9, | |
537 | .uri = "/fo o/", | |
538 | .versionStart = 11, | |
539 | .versionEnd = 18, | |
540 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) | |
541 | }; | |
36a9c964 | 542 | output.clear(); |
7a4fa6a0 AJ |
543 | testResults(__LINE__, input, output, expect); |
544 | input.clear(); | |
4245f3b9 | 545 | } |
4c14658e AJ |
546 | |
547 | // additional data in buffer | |
4245f3b9 AJ |
548 | { |
549 | input.append("GET / HTTP/1.1\nboo!", 23); | |
7a4fa6a0 AJ |
550 | struct resultSet expect = { |
551 | .parsed = false, | |
36a9c964 | 552 | .needsMore = true, |
7a4fa6a0 AJ |
553 | .parserState = Http1::HTTP_PARSE_MIME, |
554 | .status = Http::scOkay, | |
555 | .msgStart = 0, | |
556 | .msgEnd = (int)input.length()-5, | |
557 | .suffixSz = 4, // strlen("boo!") | |
558 | .methodStart = 0, | |
559 | .methodEnd = 2, | |
560 | .method = HttpRequestMethod(Http::METHOD_GET), | |
561 | .uriStart = 4, | |
562 | .uriEnd = 4, | |
563 | .uri = "/", | |
564 | .versionStart = 10, | |
565 | .versionEnd = 17, | |
566 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) | |
567 | }; | |
36a9c964 | 568 | output.clear(); |
7a4fa6a0 AJ |
569 | testResults(__LINE__, input, output, expect); |
570 | input.clear(); | |
4245f3b9 | 571 | } |
8c730cae AJ |
572 | } |
573 | ||
574 | void | |
bb86dcd4 | 575 | testHttp1Parser::testParseRequestLineTerminators() |
8c730cae AJ |
576 | { |
577 | // ensure MemPools etc exist | |
578 | globalSetup(); | |
579 | ||
7a4fa6a0 | 580 | SBuf input; |
1b51ee7b | 581 | Http1::RequestParser output; |
4c14658e AJ |
582 | |
583 | // alternative EOL sequence: NL-only | |
4245f3b9 AJ |
584 | { |
585 | input.append("GET / HTTP/1.1\n", 15); | |
7a4fa6a0 AJ |
586 | struct resultSet expect = { |
587 | .parsed = false, | |
36a9c964 | 588 | .needsMore = true, |
7a4fa6a0 AJ |
589 | .parserState = Http1::HTTP_PARSE_MIME, |
590 | .status = Http::scOkay, | |
591 | .msgStart = 0, | |
592 | .msgEnd = (int)input.length()-1, | |
593 | .suffixSz = 0, | |
594 | .methodStart = 0, | |
595 | .methodEnd = 2, | |
596 | .method = HttpRequestMethod(Http::METHOD_GET), | |
597 | .uriStart = 4, | |
598 | .uriEnd = 4, | |
599 | .uri = "/", | |
600 | .versionStart = 6, | |
601 | .versionEnd = 13, | |
602 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) | |
603 | }; | |
36a9c964 | 604 | output.clear(); |
7a4fa6a0 AJ |
605 | testResults(__LINE__, input, output, expect); |
606 | input.clear(); | |
4245f3b9 | 607 | } |
4c14658e AJ |
608 | |
609 | // alternative EOL sequence: double-NL-only | |
4245f3b9 AJ |
610 | { |
611 | input.append("GET / HTTP/1.1\n\n", 16); | |
7a4fa6a0 AJ |
612 | struct resultSet expect = { |
613 | .parsed = true, | |
36a9c964 | 614 | .needsMore = false, |
7a4fa6a0 AJ |
615 | .parserState = Http1::HTTP_PARSE_DONE, |
616 | .status = Http::scOkay, | |
617 | .msgStart = 0, | |
618 | .msgEnd = (int)input.length()-2, | |
619 | .suffixSz = 0, | |
620 | .methodStart = 0, | |
621 | .methodEnd = 2, | |
622 | .method = HttpRequestMethod(Http::METHOD_GET), | |
623 | .uriStart = 4, | |
624 | .uriEnd = 4, | |
625 | .uri = "/", | |
626 | .versionStart = 6, | |
627 | .versionEnd = 13, | |
628 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) | |
629 | }; | |
36a9c964 | 630 | output.clear(); |
7a4fa6a0 AJ |
631 | testResults(__LINE__, input, output, expect); |
632 | input.clear(); | |
4245f3b9 | 633 | } |
4c14658e | 634 | |
7a4fa6a0 | 635 | // alternative EOL sequence: multi-CR-NL |
4245f3b9 AJ |
636 | { |
637 | input.append("GET / HTTP/1.1\r\r\r\n", 18); | |
4245f3b9 | 638 | // Being tolerant we can ignore and elide these apparently benign CR |
7a4fa6a0 AJ |
639 | Config.onoff.relaxed_header_parser = 1; |
640 | struct resultSet expectRelaxed = { | |
641 | .parsed = false, | |
36a9c964 | 642 | .needsMore = true, |
7a4fa6a0 AJ |
643 | .parserState = Http1::HTTP_PARSE_MIME, |
644 | .status = Http::scOkay, | |
645 | .msgStart = 0, | |
646 | .msgEnd = (int)input.length()-1, | |
647 | .suffixSz = 0, | |
648 | .methodStart = 0, | |
649 | .methodEnd = 2, | |
650 | .method = HttpRequestMethod(Http::METHOD_GET), | |
651 | .uriStart = 4, | |
652 | .uriEnd = 4, | |
653 | .uri = "/", | |
654 | .versionStart = 6, | |
655 | .versionEnd = 13, | |
656 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) | |
657 | }; | |
36a9c964 | 658 | output.clear(); |
7a4fa6a0 | 659 | testResults(__LINE__, input, output, expectRelaxed); |
4c14658e | 660 | |
4245f3b9 AJ |
661 | // strict mode treats these as several bare-CR in the request line which is explicitly invalid. |
662 | Config.onoff.relaxed_header_parser = 0; | |
7a4fa6a0 AJ |
663 | struct resultSet expectStrict = { |
664 | .parsed = false, | |
36a9c964 | 665 | .needsMore = false, |
7a4fa6a0 AJ |
666 | .parserState = Http1::HTTP_PARSE_MIME, |
667 | .status = Http::scBadRequest, | |
668 | .msgStart = 0, | |
669 | .msgEnd = -1, | |
670 | .suffixSz = input.length(), | |
671 | .methodStart =-1, | |
672 | .methodEnd = -1, | |
673 | .method = HttpRequestMethod(), | |
674 | .uriStart = -1, | |
675 | .uriEnd = -1, | |
676 | .uri = NULL, | |
677 | .versionStart = -1, | |
678 | .versionEnd = -1, | |
679 | .version = AnyP::ProtocolVersion() | |
680 | }; | |
36a9c964 | 681 | output.clear(); |
7a4fa6a0 AJ |
682 | testResults(__LINE__, input, output, expectStrict); |
683 | input.clear(); | |
4245f3b9 | 684 | } |
4c14658e | 685 | |
8c730cae | 686 | // space padded version |
4245f3b9 AJ |
687 | { |
688 | // RFC 1945 and 2616 specify version is followed by CRLF. No intermediary bytes. | |
689 | // NP: the terminal whitespace is a special case: invalid for even HTTP/0.9 with no version tag | |
690 | input.append("GET / HTTP/1.1 \n", 16); | |
7a4fa6a0 AJ |
691 | struct resultSet expect = { |
692 | .parsed = false, | |
36a9c964 | 693 | .needsMore = false, |
7a4fa6a0 AJ |
694 | .parserState = Http1::HTTP_PARSE_DONE, |
695 | .status = Http::scBadRequest, | |
696 | .msgStart = 0, | |
697 | .msgEnd = (int)input.length()-1, | |
698 | .suffixSz = input.length(), | |
699 | .methodStart = 0, | |
700 | .methodEnd = 2, | |
701 | .method = HttpRequestMethod(Http::METHOD_GET), | |
702 | .uriStart = 4, | |
703 | .uriEnd = 13, | |
704 | .uri = "/ HTTP/1.1", | |
705 | .versionStart = -1, | |
706 | .versionEnd = -1, | |
707 | .version = AnyP::ProtocolVersion() | |
708 | }; | |
36a9c964 | 709 | output.clear(); |
7a4fa6a0 AJ |
710 | testResults(__LINE__, input, output, expect); |
711 | input.clear(); | |
4245f3b9 | 712 | } |
8c730cae AJ |
713 | } |
714 | ||
715 | void | |
bb86dcd4 | 716 | testHttp1Parser::testParseRequestLineMethods() |
8c730cae AJ |
717 | { |
718 | // ensure MemPools etc exist | |
719 | globalSetup(); | |
720 | ||
7a4fa6a0 | 721 | SBuf input; |
1b51ee7b | 722 | Http1::RequestParser output; |
8c730cae | 723 | |
4c14658e | 724 | // RFC 2616 : . method |
4245f3b9 AJ |
725 | { |
726 | input.append(". / HTTP/1.1\n", 13); | |
7a4fa6a0 AJ |
727 | struct resultSet expect = { |
728 | .parsed = false, | |
36a9c964 | 729 | .needsMore = true, |
7a4fa6a0 AJ |
730 | .parserState = Http1::HTTP_PARSE_MIME, |
731 | .status = Http::scOkay, | |
732 | .msgStart = 0, | |
733 | .msgEnd = (int)input.length()-1, | |
734 | .suffixSz = 0, | |
735 | .methodStart = 0, | |
736 | .methodEnd = 0, | |
737 | .method = HttpRequestMethod("."), | |
738 | .uriStart = 2, | |
739 | .uriEnd = 2, | |
740 | .uri = "/", | |
741 | .versionStart = 4, | |
742 | .versionEnd = 11, | |
743 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) | |
744 | }; | |
36a9c964 | 745 | output.clear(); |
7a4fa6a0 AJ |
746 | testResults(__LINE__, input, output, expect); |
747 | input.clear(); | |
4245f3b9 | 748 | } |
4c14658e AJ |
749 | |
750 | // OPTIONS with * URL | |
4245f3b9 AJ |
751 | { |
752 | input.append("OPTIONS * HTTP/1.1\n", 19); | |
7a4fa6a0 AJ |
753 | struct resultSet expect = { |
754 | .parsed = false, | |
36a9c964 | 755 | .needsMore = true, |
7a4fa6a0 AJ |
756 | .parserState = Http1::HTTP_PARSE_MIME, |
757 | .status = Http::scOkay, | |
758 | .msgStart = 0, | |
759 | .msgEnd = (int)input.length()-1, | |
760 | .suffixSz = 0, | |
761 | .methodStart = 0, | |
762 | .methodEnd = 6, | |
763 | .method = HttpRequestMethod(Http::METHOD_OPTIONS), | |
764 | .uriStart = 8, | |
765 | .uriEnd = 8, | |
766 | .uri = "*", | |
767 | .versionStart = 10, | |
768 | .versionEnd = 17, | |
769 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) | |
770 | }; | |
36a9c964 | 771 | output.clear(); |
7a4fa6a0 AJ |
772 | testResults(__LINE__, input, output, expect); |
773 | input.clear(); | |
4245f3b9 | 774 | } |
4c14658e AJ |
775 | |
776 | // unknown method | |
4245f3b9 AJ |
777 | { |
778 | input.append("HELLOWORLD / HTTP/1.1\n", 22); | |
7a4fa6a0 AJ |
779 | struct resultSet expect = { |
780 | .parsed = false, | |
36a9c964 | 781 | .needsMore = true, |
7a4fa6a0 AJ |
782 | .parserState = Http1::HTTP_PARSE_MIME, |
783 | .status = Http::scOkay, | |
784 | .msgStart = 0, | |
785 | .msgEnd = (int)input.length()-1, | |
786 | .suffixSz = 0, | |
787 | .methodStart = 0, | |
788 | .methodEnd = 9, | |
789 | .method = HttpRequestMethod("HELLOWORLD"), | |
790 | .uriStart = 11, | |
791 | .uriEnd = 11, | |
792 | .uri = "/", | |
793 | .versionStart = 13, | |
794 | .versionEnd = 20, | |
795 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) | |
796 | }; | |
36a9c964 | 797 | output.clear(); |
7a4fa6a0 AJ |
798 | testResults(__LINE__, input, output, expect); |
799 | input.clear(); | |
4245f3b9 | 800 | } |
4c14658e | 801 | |
8c730cae | 802 | // method-only |
4245f3b9 AJ |
803 | { |
804 | input.append("A\n", 2); | |
7a4fa6a0 AJ |
805 | struct resultSet expect = { |
806 | .parsed = false, | |
36a9c964 | 807 | .needsMore = false, |
7a4fa6a0 AJ |
808 | .parserState = Http1::HTTP_PARSE_DONE, |
809 | .status = Http::scBadRequest, | |
810 | .msgStart = 0, | |
811 | .msgEnd = (int)input.length()-1, | |
812 | .suffixSz = input.length(), | |
813 | .methodStart = 0, | |
814 | .methodEnd = -1, | |
815 | .method = HttpRequestMethod(), | |
816 | .uriStart = -1, | |
817 | .uriEnd = -1, | |
818 | .uri = NULL, | |
819 | .versionStart = -1, | |
820 | .versionEnd = -1, | |
821 | .version = AnyP::ProtocolVersion() | |
822 | }; | |
36a9c964 | 823 | output.clear(); |
7a4fa6a0 AJ |
824 | testResults(__LINE__, input, output, expect); |
825 | input.clear(); | |
4245f3b9 | 826 | } |
8c730cae | 827 | |
4245f3b9 | 828 | { |
7a4fa6a0 AJ |
829 | input.append("GET\n", 4); |
830 | struct resultSet expect = { | |
831 | .parsed = false, | |
36a9c964 | 832 | .needsMore = false, |
7a4fa6a0 AJ |
833 | .parserState = Http1::HTTP_PARSE_DONE, |
834 | .status = Http::scBadRequest, | |
835 | .msgStart = 0, | |
836 | .msgEnd = (int)input.length()-1, | |
837 | .suffixSz = input.length(), | |
838 | .methodStart = 0, | |
839 | .methodEnd = -1, | |
840 | .method = HttpRequestMethod(), | |
841 | .uriStart = -1, | |
842 | .uriEnd = -1, | |
843 | .uri = NULL, | |
844 | .versionStart = -1, | |
845 | .versionEnd = -1, | |
846 | .version = AnyP::ProtocolVersion() | |
847 | }; | |
36a9c964 | 848 | output.clear(); |
7a4fa6a0 AJ |
849 | testResults(__LINE__, input, output, expect); |
850 | input.clear(); | |
4245f3b9 | 851 | } |
4c14658e | 852 | |
7a4fa6a0 | 853 | // space padded method (in strict mode SP is reserved so invalid as a method byte) |
4245f3b9 AJ |
854 | { |
855 | input.append(" GET / HTTP/1.1\n", 16); | |
7a4fa6a0 AJ |
856 | // RELAXED mode Squid custom tolerance ignores SP |
857 | #if USE_HTTP_VIOLATIONS | |
4245f3b9 | 858 | Config.onoff.relaxed_header_parser = 1; |
7a4fa6a0 AJ |
859 | struct resultSet expectRelaxed = { |
860 | .parsed = false, | |
36a9c964 | 861 | .needsMore = true, |
7a4fa6a0 AJ |
862 | .parserState = Http1::HTTP_PARSE_MIME, |
863 | .status = Http::scOkay, | |
864 | .msgStart = 0, // garbage collection consumes the SP | |
865 | .msgEnd = (int)input.length()-2, | |
866 | .suffixSz = 0, | |
867 | .methodStart = 0, | |
868 | .methodEnd = 2, | |
869 | .method = HttpRequestMethod(Http::METHOD_GET), | |
870 | .uriStart = 4, | |
871 | .uriEnd = 4, | |
872 | .uri = "/", | |
873 | .versionStart = 6, | |
874 | .versionEnd = 13, | |
875 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) | |
876 | }; | |
36a9c964 | 877 | output.clear(); |
7a4fa6a0 AJ |
878 | testResults(__LINE__, input, output, expectRelaxed); |
879 | #endif | |
880 | ||
881 | // STRICT mode obeys RFC syntax | |
f4880526 | 882 | Config.onoff.relaxed_header_parser = 0; |
7a4fa6a0 AJ |
883 | struct resultSet expectStrict = { |
884 | .parsed = false, | |
36a9c964 | 885 | .needsMore = false, |
7a4fa6a0 AJ |
886 | .parserState = Http1::HTTP_PARSE_DONE, |
887 | .status = Http::scBadRequest, | |
888 | .msgStart = 0, | |
889 | .msgEnd = (int)input.length()-1, | |
890 | .suffixSz = input.length(), | |
891 | .methodStart = 0, | |
892 | .methodEnd = -1, | |
893 | .method = HttpRequestMethod(), | |
894 | .uriStart = -1, | |
895 | .uriEnd = -1, | |
896 | .uri = NULL, | |
897 | .versionStart = -1, | |
898 | .versionEnd = -1, | |
899 | .version = AnyP::ProtocolVersion() | |
900 | }; | |
36a9c964 | 901 | output.clear(); |
7a4fa6a0 AJ |
902 | testResults(__LINE__, input, output, expectStrict); |
903 | input.clear(); | |
4245f3b9 | 904 | } |
4c14658e | 905 | |
7a4fa6a0 AJ |
906 | // RFC 2616 defined tolerance: ignore empty line(s) prefix on messages |
907 | #if WHEN_RFC_COMPLIANT | |
4245f3b9 | 908 | { |
7a4fa6a0 AJ |
909 | input.append("\r\n\r\n\nGET / HTTP/1.1\r\n", 21); |
910 | struct resultSet expect = { | |
911 | .parsed = false, | |
36a9c964 | 912 | .needsMore = false, |
7a4fa6a0 AJ |
913 | .parserState = Http1::HTTP_PARSE_MIME, |
914 | .status = Http::scOkay, | |
915 | .msgStart = 5, | |
916 | .msgEnd = (int)input.length()-1, | |
917 | .suffixSz = 5, | |
918 | .methodStart = 0, | |
919 | .methodEnd = 2, | |
920 | .method = HttpRequestMethod(Http::METHOD_GET), | |
921 | .uriStart = 4, | |
922 | .uriEnd = 4, | |
923 | .uri = "/", | |
924 | .versionStart = 6, | |
925 | .versionEnd = 13, | |
926 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) | |
927 | }; | |
36a9c964 | 928 | output.clear(); |
7a4fa6a0 AJ |
929 | testResults(__LINE__, input, output, expect); |
930 | input.clear(); | |
4245f3b9 | 931 | } |
7a4fa6a0 | 932 | #endif |
4c14658e AJ |
933 | |
934 | // tab padded method (NP: tab is not SP so treated as any other binary) | |
4245f3b9 AJ |
935 | { |
936 | input.append("\tGET / HTTP/1.1\n", 16); | |
7a4fa6a0 AJ |
937 | #if WHEN_RFC_COMPLIANT |
938 | struct resultSet expect = { | |
939 | .parsed = false, | |
36a9c964 | 940 | .needsMore = false, |
7a4fa6a0 AJ |
941 | .parserState = Http1::HTTP_PARSE_DONE, |
942 | .status = Http::scBadRequest, | |
943 | .msgStart = 0, | |
944 | .msgEnd = (int)input.length()-1, | |
945 | .suffixSz = input.length(), | |
946 | .methodStart = -1, | |
947 | .methodEnd = -1, | |
948 | .method = HttpRequestMethod(), | |
949 | .uriStart = -1, | |
950 | .uriEnd = -1, | |
951 | .uri = NULL, | |
952 | .versionStart = -1, | |
953 | .versionEnd = -1, | |
954 | .version = AnyP::ProtocolVersion() | |
955 | }; | |
956 | #else // XXX: currently broken | |
957 | struct resultSet expect = { | |
958 | .parsed = false, | |
36a9c964 | 959 | .needsMore = true, |
7a4fa6a0 AJ |
960 | .parserState = Http1::HTTP_PARSE_MIME, |
961 | .status = Http::scOkay, | |
962 | .msgStart = 0, // garbage collection consumes the SP | |
963 | .msgEnd = (int)input.length()-1, | |
964 | .suffixSz = 0, | |
965 | .methodStart = 0, | |
966 | .methodEnd = 3, | |
967 | .method = HttpRequestMethod(SBuf("\tGET")), | |
968 | .uriStart = 5, | |
969 | .uriEnd = 5, | |
970 | .uri = "/", | |
971 | .versionStart = 7, | |
972 | .versionEnd = 14, | |
973 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) | |
974 | }; | |
975 | #endif | |
36a9c964 | 976 | output.clear(); |
7a4fa6a0 AJ |
977 | testResults(__LINE__, input, output, expect); |
978 | input.clear(); | |
4245f3b9 | 979 | } |
8c730cae | 980 | } |
4c14658e | 981 | |
8c730cae | 982 | void |
bb86dcd4 | 983 | testHttp1Parser::testParseRequestLineInvalid() |
8c730cae AJ |
984 | { |
985 | // ensure MemPools etc exist | |
986 | globalSetup(); | |
4c14658e | 987 | |
7a4fa6a0 | 988 | SBuf input; |
1b51ee7b | 989 | Http1::RequestParser output; |
4c14658e AJ |
990 | |
991 | // no method (but in a form which is ambiguous with HTTP/0.9 simple-request) | |
4245f3b9 | 992 | { |
7a4fa6a0 | 993 | // XXX: HTTP/0.9 requires method to be "GET" |
4245f3b9 | 994 | input.append("/ HTTP/1.0\n", 11); |
7a4fa6a0 AJ |
995 | struct resultSet expect = { |
996 | .parsed = true, | |
36a9c964 | 997 | .needsMore = false, |
7a4fa6a0 AJ |
998 | .parserState = Http1::HTTP_PARSE_MIME, |
999 | .status = Http::scOkay, | |
1000 | .msgStart = 0, | |
1001 | .msgEnd = (int)input.length()-1, | |
1002 | .suffixSz = 0, | |
1003 | .methodStart = 0, | |
1004 | .methodEnd = 0, | |
1005 | .method = HttpRequestMethod("/"), | |
1006 | .uriStart = 2, | |
1007 | .uriEnd = 9, | |
1008 | .uri = "HTTP/1.0", | |
1009 | .versionStart = -1, | |
1010 | .versionEnd = -1, | |
1011 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9) | |
1012 | }; | |
36a9c964 | 1013 | output.clear(); |
7a4fa6a0 AJ |
1014 | testResults(__LINE__, input, output, expect); |
1015 | input.clear(); | |
4245f3b9 | 1016 | } |
4c14658e | 1017 | |
7a4fa6a0 | 1018 | // no method (an invalid format) |
4245f3b9 AJ |
1019 | { |
1020 | input.append(" / HTTP/1.0\n", 12); | |
4c14658e | 1021 | |
7a4fa6a0 AJ |
1022 | // XXX: squid custom tolerance consumes initial SP. |
1023 | Config.onoff.relaxed_header_parser = 1; | |
1024 | struct resultSet expectRelaxed = { | |
1025 | .parsed = true, | |
36a9c964 | 1026 | .needsMore = false, |
7a4fa6a0 AJ |
1027 | .parserState = Http1::HTTP_PARSE_MIME, |
1028 | .status = Http::scOkay, | |
1029 | .msgStart = 0, | |
1030 | .msgEnd = (int)input.length()-2, | |
1031 | .suffixSz = 0, | |
1032 | .methodStart = 0, | |
1033 | .methodEnd = 0, | |
1034 | .method = HttpRequestMethod("/"), | |
1035 | .uriStart = 2, | |
1036 | .uriEnd = 9, | |
1037 | .uri = "HTTP/1.0", | |
1038 | .versionStart = -1, | |
1039 | .versionEnd = -1, | |
1040 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9) | |
1041 | }; | |
36a9c964 | 1042 | output.clear(); |
7a4fa6a0 AJ |
1043 | testResults(__LINE__, input, output, expectRelaxed); |
1044 | ||
1045 | // STRICT detect as invalid | |
4245f3b9 | 1046 | Config.onoff.relaxed_header_parser = 0; |
7a4fa6a0 AJ |
1047 | #if WHEN_RFC_COMPLIANT |
1048 | // XXX: except Squid does not | |
1049 | struct resultSet expectStrict = { | |
1050 | .parsed = false, | |
36a9c964 | 1051 | .needsMore = false, |
7a4fa6a0 AJ |
1052 | .parserState = Http1::HTTP_PARSE_DONE, |
1053 | .status = Http::scBadRequest, | |
1054 | .msgStart = 0, | |
1055 | .msgEnd = (int)input.length()-1, | |
1056 | .suffixSz = 0, | |
1057 | .methodStart = 0, | |
1058 | .methodEnd = -1, | |
1059 | .method = HttpRequestMethod(), | |
1060 | .uriStart = -1, | |
1061 | .uriEnd = -1, | |
1062 | .uri = NULL, | |
1063 | .versionStart = -1, | |
1064 | .versionEnd = -1, | |
1065 | .version = AnyP::ProtocolVersion() | |
1066 | }; | |
1067 | #else | |
1068 | struct resultSet expectStrict = { | |
1069 | .parsed = false, | |
36a9c964 | 1070 | .needsMore = false, |
7a4fa6a0 AJ |
1071 | .parserState = Http1::HTTP_PARSE_DONE, |
1072 | .status = Http::scBadRequest, | |
1073 | .msgStart = 0, | |
1074 | .msgEnd = (int)input.length()-1, | |
1075 | .suffixSz = input.length(), | |
1076 | .methodStart = 0, | |
1077 | .methodEnd = -1, | |
1078 | .method = HttpRequestMethod(), | |
1079 | .uriStart = -1, | |
1080 | .uriEnd = -1, | |
1081 | .uri = NULL, | |
1082 | .versionStart = -1, | |
1083 | .versionEnd = -1, | |
1084 | .version = AnyP::ProtocolVersion() | |
1085 | }; | |
1086 | #endif | |
36a9c964 | 1087 | output.clear(); |
7a4fa6a0 AJ |
1088 | testResults(__LINE__, input, output, expectStrict); |
1089 | input.clear(); | |
4245f3b9 | 1090 | } |
4c14658e | 1091 | |
7a4fa6a0 | 1092 | // binary code in method (invalid) |
4245f3b9 AJ |
1093 | { |
1094 | input.append("GET\x0B / HTTP/1.1\n", 16); | |
7a4fa6a0 AJ |
1095 | #if WHEN_RFC_COMPLIANT |
1096 | struct resultSet expect = { | |
1097 | .parsed = false, | |
36a9c964 | 1098 | .needsMore = false, |
7a4fa6a0 AJ |
1099 | .parserState = Http1::HTTP_PARSE_DONE, |
1100 | .status = Http::scBadRequest, | |
1101 | .msgStart = 0, | |
1102 | .msgEnd = (int)input.length()-1, | |
1103 | .suffixSz = input.length(), | |
1104 | .methodStart = -1, | |
1105 | .methodEnd = -1, | |
1106 | .method = HttpRequestMethod(), | |
1107 | .uriStart = -1, | |
1108 | .uriEnd = -1, | |
1109 | .uri = NULL, | |
1110 | .versionStart = -1, | |
1111 | .versionEnd = -1, | |
1112 | .version = AnyP::ProtocolVersion() | |
1113 | }; | |
1114 | #else | |
1115 | struct resultSet expect = { | |
1116 | .parsed = false, | |
36a9c964 | 1117 | .needsMore = true, |
7a4fa6a0 AJ |
1118 | .parserState = Http1::HTTP_PARSE_MIME, |
1119 | .status = Http::scOkay, | |
1120 | .msgStart = 0, // garbage collection consumes the SP | |
1121 | .msgEnd = (int)input.length()-1, | |
1122 | .suffixSz = 0, | |
1123 | .methodStart = 0, | |
1124 | .methodEnd = 3, | |
1125 | .method = HttpRequestMethod(SBuf("GET\x0B")), | |
1126 | .uriStart = 5, | |
1127 | .uriEnd = 5, | |
1128 | .uri = "/", | |
1129 | .versionStart = 7, | |
1130 | .versionEnd = 14, | |
1131 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) | |
1132 | }; | |
1133 | #endif | |
36a9c964 | 1134 | output.clear(); |
7a4fa6a0 AJ |
1135 | testResults(__LINE__, input, output, expect); |
1136 | input.clear(); | |
4245f3b9 | 1137 | } |
4c14658e AJ |
1138 | |
1139 | // CR in method | |
4245f3b9 AJ |
1140 | { |
1141 | // RFC 2616 sec 5.1 prohibits CR other than in terminator. | |
1142 | input.append("GET\r / HTTP/1.1\r\n", 16); | |
7a4fa6a0 AJ |
1143 | struct resultSet expect = { |
1144 | .parsed = false, | |
36a9c964 | 1145 | .needsMore = false, |
7a4fa6a0 AJ |
1146 | .parserState = Http1::HTTP_PARSE_DONE, |
1147 | .status = Http::scBadRequest, | |
1148 | .msgStart = 0, | |
1149 | .msgEnd = -1, // halt at the first \r | |
1150 | .suffixSz = input.length(), | |
1151 | .methodStart = -1, | |
1152 | .methodEnd = -1, | |
1153 | .method = HttpRequestMethod(), | |
1154 | .uriStart = -1, | |
1155 | .uriEnd = -1, | |
1156 | .uri = NULL, | |
1157 | .versionStart = -1, | |
1158 | .versionEnd = -1, | |
1159 | .version = AnyP::ProtocolVersion() | |
1160 | }; | |
36a9c964 | 1161 | output.clear(); |
7a4fa6a0 AJ |
1162 | testResults(__LINE__, input, output, expect); |
1163 | input.clear(); | |
4245f3b9 | 1164 | } |
4c14658e AJ |
1165 | |
1166 | // binary code NUL! in method (strange but ...) | |
4245f3b9 AJ |
1167 | { |
1168 | input.append("GET\0 / HTTP/1.1\n", 16); | |
7a4fa6a0 AJ |
1169 | #if WHEN_RFC_COMPLIANT |
1170 | struct resultSet expect = { | |
1171 | .parsed = false, | |
36a9c964 | 1172 | .needsMore = false, |
7a4fa6a0 AJ |
1173 | .parserState = Http1::HTTP_PARSE_DONE, |
1174 | .status = Http::scBadRequest, | |
1175 | .msgStart = 0, | |
1176 | .msgEnd = -1, // halt at the \0 | |
1177 | .suffixSz = input.length(), | |
1178 | .methodStart = -1, | |
1179 | .methodEnd = -1, | |
1180 | .method = HttpRequestMethod(), | |
1181 | .uriStart = -1, | |
1182 | .uriEnd = -1, | |
1183 | .uri = NULL, | |
1184 | .versionStart = -1, | |
1185 | .versionEnd = -1, | |
1186 | .version = AnyP::ProtocolVersion() | |
1187 | }; | |
1188 | #else | |
1189 | struct resultSet expect = { | |
1190 | .parsed = false, | |
36a9c964 | 1191 | .needsMore = true, |
7a4fa6a0 AJ |
1192 | .parserState = Http1::HTTP_PARSE_MIME, |
1193 | .status = Http::scOkay, | |
1194 | .msgStart = 0, | |
1195 | .msgEnd = (int)input.length()-1, | |
1196 | .suffixSz = 0, | |
1197 | .methodStart = 0, | |
1198 | .methodEnd = 3, | |
1199 | .method = HttpRequestMethod(SBuf("GET\0",4)), | |
1200 | .uriStart = 5, | |
1201 | .uriEnd = 5, | |
1202 | .uri = "/", | |
1203 | .versionStart = 7, | |
1204 | .versionEnd = 14, | |
1205 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1) | |
1206 | }; | |
1207 | #endif | |
36a9c964 | 1208 | output.clear(); |
7a4fa6a0 AJ |
1209 | testResults(__LINE__, input, output, expect); |
1210 | input.clear(); | |
4245f3b9 | 1211 | } |
4c14658e | 1212 | |
7a4fa6a0 | 1213 | // no URL (grammer invalid, ambiguous with RFC 1945 HTTP/0.9 simple-request) |
4245f3b9 AJ |
1214 | { |
1215 | input.append("GET HTTP/1.1\n", 14); | |
7a4fa6a0 AJ |
1216 | struct resultSet expect = { |
1217 | .parsed = true, | |
36a9c964 | 1218 | .needsMore = false, |
7a4fa6a0 AJ |
1219 | .parserState = Http1::HTTP_PARSE_DONE, |
1220 | .status = Http::scOkay, | |
1221 | .msgStart = 0, | |
1222 | .msgEnd = (int)input.length()-1, | |
1223 | .suffixSz = 0, | |
1224 | .methodStart = 0, | |
1225 | .methodEnd = 2, | |
1226 | .method = HttpRequestMethod(Http::METHOD_GET), | |
1227 | .uriStart = 5, | |
1228 | .uriEnd = 12, | |
1229 | .uri = "HTTP/1.1", | |
1230 | .versionStart = -1, | |
1231 | .versionEnd = -1, | |
1232 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9) | |
1233 | }; | |
36a9c964 | 1234 | output.clear(); |
7a4fa6a0 AJ |
1235 | testResults(__LINE__, input, output, expect); |
1236 | input.clear(); | |
4245f3b9 | 1237 | } |
4c14658e AJ |
1238 | |
1239 | // no URL (grammer invalid, ambiguous with RFC 1945 HTTP/0.9 simple-request) | |
4245f3b9 AJ |
1240 | { |
1241 | input.append("GET HTTP/1.1\n", 13); | |
7a4fa6a0 AJ |
1242 | struct resultSet expect = { |
1243 | .parsed = true, | |
36a9c964 | 1244 | .needsMore = false, |
7a4fa6a0 AJ |
1245 | .parserState = Http1::HTTP_PARSE_DONE, |
1246 | .status = Http::scOkay, | |
1247 | .msgStart = 0, | |
1248 | .msgEnd = (int)input.length()-1, | |
1249 | .suffixSz = 0, | |
1250 | .methodStart = 0, | |
1251 | .methodEnd = 2, | |
1252 | .method = HttpRequestMethod(Http::METHOD_GET), | |
1253 | .uriStart = 4, | |
1254 | .uriEnd = 11, | |
1255 | .uri = "HTTP/1.1", | |
1256 | .versionStart = -1, | |
1257 | .versionEnd = -1, | |
1258 | .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9) | |
1259 | }; | |
36a9c964 | 1260 | output.clear(); |
7a4fa6a0 AJ |
1261 | testResults(__LINE__, input, output, expect); |
1262 | input.clear(); | |
4245f3b9 | 1263 | } |
4c14658e | 1264 | |
4c14658e | 1265 | // binary line |
4245f3b9 AJ |
1266 | { |
1267 | input.append("\xB\xC\xE\xF\n", 5); | |
7a4fa6a0 AJ |
1268 | struct resultSet expect = { |
1269 | .parsed = false, | |
36a9c964 | 1270 | .needsMore = false, |
7a4fa6a0 AJ |
1271 | .parserState = Http1::HTTP_PARSE_DONE, |
1272 | .status = Http::scBadRequest, | |
1273 | .msgStart = 0, | |
1274 | .msgEnd = (int)input.length()-1, | |
1275 | .suffixSz = input.length(), | |
1276 | .methodStart = 0, | |
1277 | .methodEnd = -1, | |
1278 | .method = HttpRequestMethod(), | |
1279 | .uriStart = -1, | |
1280 | .uriEnd = -1, | |
1281 | .uri = NULL, | |
1282 | .versionStart = -1, | |
1283 | .versionEnd = -1, | |
1284 | .version = AnyP::ProtocolVersion() | |
1285 | }; | |
36a9c964 | 1286 | output.clear(); |
7a4fa6a0 AJ |
1287 | testResults(__LINE__, input, output, expect); |
1288 | input.clear(); | |
4245f3b9 | 1289 | } |
4c14658e AJ |
1290 | |
1291 | // mixed whitespace line | |
4245f3b9 AJ |
1292 | { |
1293 | // We accept non-space binary bytes for method so first \t shows up as that | |
1294 | // but remaining space and tabs are skipped searching for URI-start | |
1295 | input.append("\t \t \t\n", 6); | |
7a4fa6a0 AJ |
1296 | struct resultSet expect = { |
1297 | .parsed = false, | |
36a9c964 | 1298 | .needsMore = false, |
7a4fa6a0 AJ |
1299 | .parserState = Http1::HTTP_PARSE_DONE, |
1300 | .status = Http::scBadRequest, | |
1301 | .msgStart = 0, | |
1302 | .msgEnd = (int)input.length()-1, | |
1303 | .suffixSz = input.length(), | |
1304 | .methodStart = 0, | |
1305 | .methodEnd = 0, | |
1306 | .method = HttpRequestMethod(SBuf("\t")), | |
1307 | .uriStart = -1, | |
1308 | .uriEnd = -1, | |
1309 | .uri = NULL, | |
1310 | .versionStart = -1, | |
1311 | .versionEnd = -1, | |
1312 | .version = AnyP::ProtocolVersion() | |
1313 | }; | |
36a9c964 | 1314 | output.clear(); |
7a4fa6a0 AJ |
1315 | testResults(__LINE__, input, output, expect); |
1316 | input.clear(); | |
4245f3b9 | 1317 | } |
4c14658e AJ |
1318 | |
1319 | // mixed whitespace line with CR middle | |
4245f3b9 AJ |
1320 | { |
1321 | // CR aborts on sight, so even initial \t method is not marked as above | |
1322 | // (not when parsing clean with whole line available anyway) | |
1323 | input.append("\t \r \n", 6); | |
7a4fa6a0 AJ |
1324 | struct resultSet expect = { |
1325 | .parsed = false, | |
36a9c964 | 1326 | .needsMore = false, |
7a4fa6a0 AJ |
1327 | .parserState = Http1::HTTP_PARSE_DONE, |
1328 | .status = Http::scBadRequest, | |
1329 | .msgStart = 0, | |
1330 | .msgEnd = -1, // halt on the \r | |
1331 | .suffixSz = input.length(), | |
1332 | .methodStart = -1, | |
1333 | .methodEnd = -1, | |
1334 | .method = HttpRequestMethod(), | |
1335 | .uriStart = -1, | |
1336 | .uriEnd = -1, | |
1337 | .uri = NULL, | |
1338 | .versionStart = -1, | |
1339 | .versionEnd = -1, | |
1340 | .version = AnyP::ProtocolVersion() | |
1341 | }; | |
36a9c964 | 1342 | output.clear(); |
7a4fa6a0 AJ |
1343 | testResults(__LINE__, input, output, expect); |
1344 | input.clear(); | |
4245f3b9 | 1345 | } |
4c14658e | 1346 | } |
b6a7fc85 AJ |
1347 | |
1348 | void | |
bb86dcd4 | 1349 | testHttp1Parser::testDripFeed() |
b6a7fc85 AJ |
1350 | { |
1351 | // Simulate a client drip-feeding Squid a few bytes at a time. | |
1352 | // extend the size of the buffer from 0 bytes to full request length | |
1353 | // calling the parser repeatedly as visible data grows. | |
1354 | ||
7a4fa6a0 AJ |
1355 | SBuf data; |
1356 | data.append(" ", 12); | |
1357 | SBuf::size_type garbageEnd = data.length(); | |
1358 | data.append("GET http://example.com/ HTTP/1.1\r\n", 34); | |
1359 | SBuf::size_type reqLineEnd = data.length() - 1; | |
1360 | data.append("Host: example.com\r\n\r\n", 21); | |
1361 | SBuf::size_type mimeEnd = data.length() - 1; | |
1362 | data.append("...", 3); // trailer to catch mime EOS errors. | |
b6a7fc85 | 1363 | |
7a4fa6a0 | 1364 | SBuf ioBuf; // begins empty |
36a9c964 | 1365 | Http1::RequestParser hp; |
b6a7fc85 AJ |
1366 | |
1367 | // only relaxed parser accepts the garbage whitespace | |
1368 | Config.onoff.relaxed_header_parser = 1; | |
1369 | ||
7a4fa6a0 AJ |
1370 | // state of things we expect right now |
1371 | struct resultSet expect = { | |
1372 | .parsed = false, | |
36a9c964 AJ |
1373 | .needsMore = true, |
1374 | .parserState = Http1::HTTP_PARSE_NONE, | |
10c0d360 AJ |
1375 | .status = Http::scNone, |
1376 | .msgStart = -1, | |
7a4fa6a0 AJ |
1377 | .msgEnd = -1, |
1378 | .suffixSz = 0, | |
1379 | .methodStart = -1, | |
1380 | .methodEnd = -1, | |
1381 | .method = HttpRequestMethod(), | |
1382 | .uriStart = -1, | |
1383 | .uriEnd = -1, | |
1384 | .uri = NULL, | |
1385 | .versionStart = -1, | |
1386 | .versionEnd = -1, | |
1387 | .version = AnyP::ProtocolVersion() | |
1388 | }; | |
1389 | ||
1390 | Config.maxRequestHeaderSize = 1024; // large enough to hold the test data. | |
1391 | ||
1392 | for (SBuf::size_type pos = 0; pos <= data.length(); ++pos) { | |
1393 | ||
1394 | // simulate reading one more byte | |
1395 | ioBuf.append(data.substr(pos,1)); | |
1396 | ||
7a4fa6a0 AJ |
1397 | // when the garbage is passed we expect to start seeing first-line bytes |
1398 | if (pos == garbageEnd) { | |
1399 | expect.parserState = Http1::HTTP_PARSE_FIRST; | |
1400 | expect.msgStart = 0; | |
b6a7fc85 AJ |
1401 | } |
1402 | ||
7a4fa6a0 AJ |
1403 | // all points after garbage start to see accumulated bytes looking for end of current section |
1404 | if (pos >= garbageEnd) | |
1405 | expect.suffixSz = ioBuf.length(); | |
1406 | ||
1407 | // at end of request line expect to see method, URI, version details | |
1408 | // and switch to seeking Mime header section | |
1409 | if (pos == reqLineEnd) { | |
1410 | expect.parserState = Http1::HTTP_PARSE_MIME; | |
1411 | expect.suffixSz = 0; | |
1412 | expect.msgEnd = reqLineEnd-garbageEnd; | |
1413 | expect.status = Http::scOkay; | |
1414 | expect.methodStart = 0; | |
1415 | expect.methodEnd = 2; | |
1416 | expect.method = HttpRequestMethod(Http::METHOD_GET); | |
1417 | expect.uriStart = 4; | |
1418 | expect.uriEnd = 22; | |
1419 | expect.uri = "http://example.com/"; | |
1420 | expect.versionStart = 24; | |
1421 | expect.versionEnd = 31; | |
1422 | expect.version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1); | |
f4880526 AJ |
1423 | } |
1424 | ||
7a4fa6a0 AJ |
1425 | // one mime header is done we are expectign a new request |
1426 | // parse results say true and initial data is all gone from the buffer | |
1427 | if (pos == mimeEnd) { | |
1428 | expect.parsed = true; | |
36a9c964 | 1429 | expect.needsMore = false; |
7a4fa6a0 | 1430 | expect.suffixSz = 0; |
b6a7fc85 AJ |
1431 | } |
1432 | ||
7a4fa6a0 AJ |
1433 | testResults(__LINE__, ioBuf, hp, expect); |
1434 | ||
1435 | // sync the buffers like Squid does | |
b749de75 | 1436 | ioBuf = hp.remaining(); |
7a4fa6a0 | 1437 | |
36a9c964 AJ |
1438 | // Squid stops using the parser once it has parsed the first message. |
1439 | if (!hp.needsMoreData()) | |
7a4fa6a0 | 1440 | break; |
b6a7fc85 AJ |
1441 | } |
1442 | } |