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