]> git.ipfire.org Git - thirdparty/squid.git/blame - src/tests/testHttp1Parser.cc
Update RFC 7230 compliance
[thirdparty/squid.git] / src / tests / testHttp1Parser.cc
CommitLineData
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 24CPPUNIT_TEST_SUITE_REGISTRATION( testHttp1Parser );
4c14658e
AJ
25
26void
bb86dcd4 27testHttp1Parser::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
42struct 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
61static void
62testResults(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
88void
89testHttp1Parser::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 133void
bb86dcd4 134testHttp1Parser::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
485void
bb86dcd4 486testHttp1Parser::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
574void
bb86dcd4 575testHttp1Parser::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
715void
bb86dcd4 716testHttp1Parser::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 982void
bb86dcd4 983testHttp1Parser::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
1348void
bb86dcd4 1349testHttp1Parser::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}