-#define SQUID_UNIT_TEST 1
+/*
+ * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
#include "squid.h"
#include <cppunit/TestAssert.h>
#define private public
#define protected public
-#include "testHttp1Parser.h"
-#include "http/Http1Parser.h"
+#include "Debug.h"
+#include "http/one/RequestParser.h"
#include "http/RequestMethod.h"
-#include "Mem.h"
#include "MemBuf.h"
#include "SquidConfig.h"
#include "testHttp1Parser.h"
+#include "unitTestMain.h"
CPPUNIT_TEST_SUITE_REGISTRATION( testHttp1Parser );
bool needsMore;
Http1::ParseState parserState;
Http::StatusCode status;
- int msgStart;
- int msgEnd;
SBuf::size_type suffixSz;
- int methodStart;
- int methodEnd;
HttpRequestMethod method;
- int uriStart;
- int uriEnd;
const char *uri;
- int versionStart;
- int versionEnd;
AnyP::ProtocolVersion version;
};
+// define SQUID_DEBUG_TESTS to see exactly which test sub-cases fail and where
+#ifdef SQUID_DEBUG_TESTS
+// not optimized for runtime use
+static void
+Replace(SBuf &where, const SBuf &what, const SBuf &with)
+{
+ // prevent infinite loops
+ if (!what.length() || with.find(what) != SBuf::npos)
+ return;
+
+ SBuf::size_type pos = 0;
+ while ((pos = where.find(what, pos)) != SBuf::npos) {
+ SBuf buf = where.substr(0, pos);
+ buf.append(with);
+ buf.append(where.substr(pos+what.length()));
+ where = buf;
+ pos += with.length();
+ }
+}
+
+static SBuf Pretty(SBuf raw)
+{
+ Replace(raw, SBuf("\r"), SBuf("\\r"));
+ Replace(raw, SBuf("\n"), SBuf("\\n"));
+ return raw;
+}
+#endif
+
static void
testResults(int line, const SBuf &input, Http1::RequestParser &output, struct resultSet &expect)
{
-#if WHEN_TEST_DEBUG_IS_NEEDED
- printf("TEST @%d, in=%u: " SQUIDSBUFPH "\n", line, input.length(), SQUIDSBUFPRINT(input));
+#ifdef SQUID_DEBUG_TESTS
+ std::cerr << "TEST @" << line << ", in=" << Pretty(input) << "\n";
+#endif
+
+ const bool parsed = output.parse(input);
+
+#ifdef SQUID_DEBUG_TESTS
+ if (expect.parsed != parsed)
+ std::cerr << "\tparse-FAILED: " << expect.parsed << "!=" << parsed << "\n";
+ else if (parsed && expect.method != output.method_)
+ std::cerr << "\tmethod-FAILED: " << expect.method << "!=" << output.method_ << "\n";
+ if (expect.status != output.parseStatusCode)
+ std::cerr << "\tscode-FAILED: " << expect.status << "!=" << output.parseStatusCode << "\n";
+ if (expect.suffixSz != output.buf_.length())
+ std::cerr << "\tsuffixSz-FAILED: " << expect.suffixSz << "!=" << output.buf_.length() << "\n";
#endif
- CPPUNIT_ASSERT_EQUAL(expect.parsed, output.parse(input));
+ // runs the parse
+ CPPUNIT_ASSERT_EQUAL(expect.parsed, parsed);
+
+ // if parsing was successful, check easily visible field outputs
+ if (parsed) {
+ CPPUNIT_ASSERT_EQUAL(expect.method, output.method_);
+ if (expect.uri != NULL)
+ CPPUNIT_ASSERT_EQUAL(0, output.uri_.cmp(expect.uri));
+ CPPUNIT_ASSERT_EQUAL(expect.version, output.msgProtocol_);
+ }
+
+ CPPUNIT_ASSERT_EQUAL(expect.status, output.parseStatusCode);
+
+ // check more obscure states
CPPUNIT_ASSERT_EQUAL(expect.needsMore, output.needsMoreData());
if (output.needsMoreData())
CPPUNIT_ASSERT_EQUAL(expect.parserState, output.parsingStage_);
- CPPUNIT_ASSERT_EQUAL(expect.status, output.request_parse_status);
- CPPUNIT_ASSERT_EQUAL(expect.msgStart, output.req.start);
- CPPUNIT_ASSERT_EQUAL(expect.msgEnd, output.req.end);
- CPPUNIT_ASSERT_EQUAL(expect.suffixSz, output.buf.length());
- CPPUNIT_ASSERT_EQUAL(expect.methodStart, output.req.m_start);
- CPPUNIT_ASSERT_EQUAL(expect.methodEnd, output.req.m_end);
- CPPUNIT_ASSERT_EQUAL(expect.method, output.method_);
- CPPUNIT_ASSERT_EQUAL(expect.uriStart, output.req.u_start);
- CPPUNIT_ASSERT_EQUAL(expect.uriEnd, output.req.u_end);
- if (expect.uri != NULL)
- CPPUNIT_ASSERT_EQUAL(0, output.uri_.cmp(expect.uri));
- CPPUNIT_ASSERT_EQUAL(expect.versionStart, output.req.v_start);
- CPPUNIT_ASSERT_EQUAL(expect.versionEnd, output.req.v_end);
- CPPUNIT_ASSERT_EQUAL(expect.version, output.msgProtocol_);
+ CPPUNIT_ASSERT_EQUAL(expect.suffixSz, output.buf_.length());
+}
+
+void
+testHttp1Parser::testParserConstruct()
+{
+ // whether the constructor works
+ {
+ Http1::RequestParser output;
+ CPPUNIT_ASSERT_EQUAL(true, output.needsMoreData());
+ CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_NONE, output.parsingStage_);
+ CPPUNIT_ASSERT_EQUAL(Http::scNone, output.parseStatusCode); // XXX: clear() not being called.
+ CPPUNIT_ASSERT(output.buf_.isEmpty());
+ CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_NONE), output.method_);
+ CPPUNIT_ASSERT(output.uri_.isEmpty());
+ CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_);
+ }
+
+ // whether new() works
+ {
+ Http1::RequestParser *output = new Http1::RequestParser;
+ CPPUNIT_ASSERT_EQUAL(true, output->needsMoreData());
+ CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_NONE, output->parsingStage_);
+ CPPUNIT_ASSERT_EQUAL(Http::scNone, output->parseStatusCode);
+ CPPUNIT_ASSERT(output->buf_.isEmpty());
+ CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_NONE), output->method_);
+ CPPUNIT_ASSERT(output->uri_.isEmpty());
+ CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output->msgProtocol_);
+ delete output;
+ }
}
void
Http1::RequestParser output;
// TEST: Do we comply with RFC 1945 section 5.1 ?
- // TEST: Do we comply with RFC 2616 section 5.1 ?
+ // TEST: Do we comply with RFC 7230 sections 2.6, 3.1.1 and 3.5 ?
// RFC 1945 : HTTP/0.9 simple-request
{
.needsMore = false,
.parserState = Http1::HTTP_PARSE_DONE,
.status = Http::scOkay,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
.suffixSz = 0,
- .methodStart = 0,
- .methodEnd = 2,
.method = HttpRequestMethod(Http::METHOD_GET),
- .uriStart = 4,
- .uriEnd = 4,
.uri = "/",
- .versionStart = -1,
- .versionEnd = -1,
.version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9)
};
output.clear();
}
// RFC 1945 : invalid HTTP/0.9 simple-request (only GET is valid)
-#if WHEN_RFC_COMPLIANT
{
- input.append("POST /\r\n", 7);
+ input.append("POST /\r\n", 8);
struct resultSet expect = {
- .parsed = true,
+ .parsed = false,
.needsMore = false,
.parserState = Http1::HTTP_PARSE_DONE,
- .status = Http::scOkay,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
- .suffixSz = 0,
- .methodStart = 0,
- .methodEnd = 3,
+ .status = Http::scBadRequest,
+ .suffixSz = input.length(),
.method = HttpRequestMethod(Http::METHOD_POST),
- .uriStart = 5,
- .uriEnd = 5,
- .uri = "/",
- .versionStart = -1,
- .versionEnd = -1,
+ .uri = NULL,
.version = AnyP::ProtocolVersion()
};
output.clear();
testResults(__LINE__, input, output, expect);
input.clear();
}
-#endif
- // RFC 1945 and 2616 : HTTP/1.0 request
+
+ // RFC 1945 and 7230 : HTTP/1.0 request
{
input.append("GET / HTTP/1.0\r\n", 16);
struct resultSet expect = {
.needsMore = true,
.parserState = Http1::HTTP_PARSE_MIME,
.status = Http::scOkay,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
.suffixSz = 0,
- .methodStart = 0,
- .methodEnd = 2,
.method = HttpRequestMethod(Http::METHOD_GET),
- .uriStart = 4,
- .uriEnd = 4,
.uri = "/",
- .versionStart = 6,
- .versionEnd = 13,
.version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,0)
};
output.clear();
input.clear();
}
- // RFC 2616 : HTTP/1.1 request
+ // RFC 7230 : HTTP/1.1 request
{
input.append("GET / HTTP/1.1\r\n", 16);
struct resultSet expect = {
.needsMore = true,
.parserState = Http1::HTTP_PARSE_MIME,
.status = Http::scOkay,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
.suffixSz = 0,
- .methodStart = 0,
- .methodEnd = 2,
.method = HttpRequestMethod(Http::METHOD_GET),
- .uriStart = 4,
- .uriEnd = 4,
.uri = "/",
- .versionStart = 6,
- .versionEnd = 13,
.version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
};
output.clear();
input.clear();
}
- // RFC 2616 : future version full-request
+ // RFC 7230 : future 1.x version full-request
{
input.append("GET / HTTP/1.2\r\n", 16);
struct resultSet expect = {
.needsMore = true,
.parserState = Http1::HTTP_PARSE_MIME,
.status = Http::scOkay,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
.suffixSz = 0,
- .methodStart = 0,
- .methodEnd = 2,
.method = HttpRequestMethod(Http::METHOD_GET),
- .uriStart = 4,
- .uriEnd = 4,
.uri = "/",
- .versionStart = 6,
- .versionEnd = 13,
.version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,2)
};
output.clear();
input.clear();
}
- // RFC 2616 : future version full-request
+ // RFC 7230 : future versions do not use 1.x message syntax.
+ // However, it is still valid syntax for the single-digit forms
+ // to appear. The parser we are testing should accept them.
+ {
+ input.append("GET / HTTP/2.0\r\n", 16);
+ struct resultSet expectA = {
+ .parsed = true,
+ .needsMore = false,
+ .parserState = Http1::HTTP_PARSE_DONE,
+ .status = Http::scOkay,
+ .suffixSz = 0,
+ .method = HttpRequestMethod(Http::METHOD_GET),
+ .uri = "/",
+ .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,2,0)
+ };
+ output.clear();
+ testResults(__LINE__, input, output, expectA);
+ input.clear();
+
+ input.append("GET / HTTP/9.9\r\n", 16);
+ struct resultSet expectB = {
+ .parsed = true,
+ .needsMore = false,
+ .parserState = Http1::HTTP_PARSE_DONE,
+ .status = Http::scOkay,
+ .suffixSz = 0,
+ .method = HttpRequestMethod(Http::METHOD_GET),
+ .uri = "/",
+ .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,9,9)
+ };
+ output.clear();
+ testResults(__LINE__, input, output, expectB);
+ input.clear();
+ }
+
+ // RFC 7230 : future versions >= 10.0 are invalid syntax
{
- // IETF HTTPbis WG has made this two-digits format invalid.
- // it gets treated same as HTTP/0.9 for now
input.append("GET / HTTP/10.12\r\n", 18);
struct resultSet expect = {
.parsed = false,
.needsMore = false,
.parserState = Http1::HTTP_PARSE_MIME,
- .status = Http::scHttpVersionNotSupported,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
+ .status = Http::scBadRequest,
.suffixSz = input.length(),
- .methodStart = 0,
- .methodEnd = 2,
.method = HttpRequestMethod(Http::METHOD_GET),
- .uriStart = 4,
- .uriEnd = 4,
.uri = "/",
- .versionStart = 6,
- .versionEnd = 15,
- .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,10,12)
+ .version = AnyP::ProtocolVersion()
};
output.clear();
testResults(__LINE__, input, output, expect);
// unknown non-HTTP protocol names
{
- // XXX: violations mode treats them as HTTP/0.9 requests! which is wrong.
-#if !USE_HTTP_VIOLATIONS
- input.append("GET / FOO/1.0\n", 14);
+ input.append("GET / FOO/1.0\r\n", 15);
struct resultSet expect = {
.parsed = false,
.needsMore = false,
.parserState = Http1::HTTP_PARSE_DONE,
- .status = Http::scHttpVersionNotSupported,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
+ .status = Http::scBadRequest,
.suffixSz = input.length(),
- .methodStart = 0,
- .methodEnd = 2,
.method = HttpRequestMethod(Http::METHOD_GET),
- .uriStart = 4,
- .uriEnd = 4,
.uri = "/",
- .versionStart = 6,
- .versionEnd = 12,
.version = AnyP::ProtocolVersion()
};
output.clear();
testResults(__LINE__, input, output, expect);
input.clear();
-#endif
}
- // no version
+ // no version digits
{
- input.append("GET / HTTP/\n", 12);
+ input.append("GET / HTTP/\r\n", 13);
struct resultSet expect = {
.parsed = false,
.needsMore = false,
.parserState = Http1::HTTP_PARSE_DONE,
- .status = Http::scHttpVersionNotSupported,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
+ .status = Http::scBadRequest,
.suffixSz = input.length(),
- .methodStart = 0,
- .methodEnd = 2,
.method = HttpRequestMethod(Http::METHOD_GET),
- .uriStart = 4,
- .uriEnd = 4,
.uri = "/",
- .versionStart = 6,
- .versionEnd = 10,
- .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,0)
+ .version = AnyP::ProtocolVersion()
};
output.clear();
testResults(__LINE__, input, output, expect);
// no major version
{
- input.append("GET / HTTP/.1\n", 14);
+ input.append("GET / HTTP/.1\r\n", 15);
struct resultSet expect = {
.parsed = false,
.needsMore = false,
.parserState = Http1::HTTP_PARSE_DONE,
- .status = Http::scHttpVersionNotSupported,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
+ .status = Http::scBadRequest,
.suffixSz = input.length(),
- .methodStart = 0,
- .methodEnd = 2,
.method = HttpRequestMethod(Http::METHOD_GET),
- .uriStart = 4,
- .uriEnd = 4,
.uri = "/",
- .versionStart = 6,
- .versionEnd = 12,
- .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,0)
+ .version = AnyP::ProtocolVersion()
};
output.clear();
testResults(__LINE__, input, output, expect);
// no version dot
{
- input.append("GET / HTTP/11\n", 14);
+ input.append("GET / HTTP/11\r\n", 15);
struct resultSet expect = {
.parsed = false,
.needsMore = false,
.parserState = Http1::HTTP_PARSE_DONE,
- .status = Http::scHttpVersionNotSupported,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
+ .status = Http::scBadRequest,
.suffixSz = input.length(),
- .methodStart = 0,
- .methodEnd = 2,
.method = HttpRequestMethod(Http::METHOD_GET),
- .uriStart = 4,
- .uriEnd = 4,
.uri = "/",
- .versionStart = 6,
- .versionEnd = 12,
- .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,0)
+ .version = AnyP::ProtocolVersion()
};
output.clear();
testResults(__LINE__, input, output, expect);
// negative major version (bug 3062)
{
- input.append("GET / HTTP/-999999.1\n", 21);
+ input.append("GET / HTTP/-999999.1\r\n", 22);
struct resultSet expect = {
.parsed = false,
.needsMore = false,
.parserState = Http1::HTTP_PARSE_DONE,
- .status = Http::scHttpVersionNotSupported,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
+ .status = Http::scBadRequest,
.suffixSz = input.length(),
- .methodStart = 0,
- .methodEnd = 2,
.method = HttpRequestMethod(Http::METHOD_GET),
- .uriStart = 4,
- .uriEnd = 4,
.uri = "/",
- .versionStart = 6,
- .versionEnd = 19,
- .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,0)
+ .version = AnyP::ProtocolVersion()
};
output.clear();
testResults(__LINE__, input, output, expect);
// no minor version
{
- input.append("GET / HTTP/1.\n", 14);
+ input.append("GET / HTTP/1.\r\n", 15);
struct resultSet expect = {
.parsed = false,
.needsMore = false,
.parserState = Http1::HTTP_PARSE_DONE,
- .status = Http::scHttpVersionNotSupported,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
+ .status = Http::scBadRequest,
.suffixSz = input.length(),
- .methodStart = 0,
- .methodEnd = 2,
.method = HttpRequestMethod(Http::METHOD_GET),
- .uriStart = 4,
- .uriEnd = 4,
.uri = "/",
- .versionStart = 6,
- .versionEnd = 12,
- .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,0)
+ .version = AnyP::ProtocolVersion()
};
output.clear();
testResults(__LINE__, input, output, expect);
// negative major version (bug 3062 corollary)
{
- input.append("GET / HTTP/1.-999999\n", 21);
+ input.append("GET / HTTP/1.-999999\r\n", 22);
struct resultSet expect = {
.parsed = false,
.needsMore = false,
.parserState = Http1::HTTP_PARSE_DONE,
- .status = Http::scHttpVersionNotSupported,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
+ .status = Http::scBadRequest,
.suffixSz = input.length(),
- .methodStart = 0,
- .methodEnd = 2,
.method = HttpRequestMethod(Http::METHOD_GET),
- .uriStart = 4,
- .uriEnd = 4,
.uri = "/",
- .versionStart = 6,
- .versionEnd = 19,
- .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,0)
+ .version = AnyP::ProtocolVersion()
};
output.clear();
testResults(__LINE__, input, output, expect);
// space padded URL
{
input.append("GET / HTTP/1.1\r\n", 21);
+ // when being tolerant extra (sequential) SP delimiters are acceptable
+ Config.onoff.relaxed_header_parser = 1;
struct resultSet expect = {
.parsed = false,
.needsMore = true,
.parserState = Http1::HTTP_PARSE_MIME,
.status = Http::scOkay,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
.suffixSz = 0,
- .methodStart = 0,
- .methodEnd = 2,
.method = HttpRequestMethod(Http::METHOD_GET),
- .uriStart = 5,
- .uriEnd = 5,
.uri = "/",
- .versionStart = 11,
- .versionEnd = 18,
.version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
};
output.clear();
testResults(__LINE__, input, output, expect);
+
+ Config.onoff.relaxed_header_parser = 0;
+ struct resultSet expectStrict = {
+ .parsed = false,
+ .needsMore = false,
+ .parserState = Http1::HTTP_PARSE_DONE,
+ .status = Http::scBadRequest,
+ .suffixSz = input.length(),
+ .method = HttpRequestMethod(),
+ .uri = NULL,
+ .version = AnyP::ProtocolVersion()
+ };
+ output.clear();
+ testResults(__LINE__, input, output, expectStrict);
input.clear();
}
// whitespace inside URI. (nasty but happens)
- // XXX: depends on tolerant parser...
{
- input.append("GET /fo o/ HTTP/1.1\n", 20);
+ input.append("GET /fo o/ HTTP/1.1\r\n", 21);
+ Config.onoff.relaxed_header_parser = 1;
struct resultSet expect = {
.parsed = false,
.needsMore = true,
.parserState = Http1::HTTP_PARSE_MIME,
.status = Http::scOkay,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
.suffixSz = 0,
- .methodStart = 0,
- .methodEnd = 2,
.method = HttpRequestMethod(Http::METHOD_GET),
- .uriStart = 4,
- .uriEnd = 9,
.uri = "/fo o/",
- .versionStart = 11,
- .versionEnd = 18,
.version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
};
output.clear();
testResults(__LINE__, input, output, expect);
+
+ Config.onoff.relaxed_header_parser = 0;
+ struct resultSet expectStrict = {
+ .parsed = false,
+ .needsMore = false,
+ .parserState = Http1::HTTP_PARSE_DONE,
+ .status = Http::scBadRequest,
+ .suffixSz = input.length(),
+ .method = HttpRequestMethod(),
+ .uri = NULL,
+ .version = AnyP::ProtocolVersion()
+ };
+ output.clear();
+ testResults(__LINE__, input, output, expectStrict);
input.clear();
}
// additional data in buffer
{
- input.append("GET / HTTP/1.1\nboo!", 23);
+ input.append("GET / HTTP/1.1\r\nboo!", 20);
struct resultSet expect = {
.parsed = false,
.needsMore = true,
.parserState = Http1::HTTP_PARSE_MIME,
.status = Http::scOkay,
- .msgStart = 0,
- .msgEnd = (int)input.length()-5,
.suffixSz = 4, // strlen("boo!")
- .methodStart = 0,
- .methodEnd = 2,
.method = HttpRequestMethod(Http::METHOD_GET),
- .uriStart = 4,
- .uriEnd = 4,
.uri = "/",
- .versionStart = 10,
- .versionEnd = 17,
.version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
};
output.clear();
testResults(__LINE__, input, output, expect);
input.clear();
+ Config.onoff.relaxed_header_parser = 0;
}
}
Http1::RequestParser output;
// alternative EOL sequence: NL-only
+ // RFC 7230 tolerance permits omitted CR
{
input.append("GET / HTTP/1.1\n", 15);
+ Config.onoff.relaxed_header_parser = 1;
struct resultSet expect = {
.parsed = false,
.needsMore = true,
.parserState = Http1::HTTP_PARSE_MIME,
.status = Http::scOkay,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
.suffixSz = 0,
- .methodStart = 0,
- .methodEnd = 2,
.method = HttpRequestMethod(Http::METHOD_GET),
- .uriStart = 4,
- .uriEnd = 4,
.uri = "/",
- .versionStart = 6,
- .versionEnd = 13,
.version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
};
output.clear();
testResults(__LINE__, input, output, expect);
+
+ Config.onoff.relaxed_header_parser = 0;
+ struct resultSet expectStrict = {
+ .parsed = false,
+ .needsMore = false,
+ .parserState = Http1::HTTP_PARSE_DONE,
+ .status = Http::scBadRequest,
+ .suffixSz = input.length(),
+ .method = HttpRequestMethod(),
+ .uri = NULL,
+ .version = AnyP::ProtocolVersion()
+ };
+ output.clear();
+ testResults(__LINE__, input, output, expectStrict);
input.clear();
}
// alternative EOL sequence: double-NL-only
+ // RFC 7230 tolerance permits omitted CR
+ // NP: represents a request with no mime headers
{
input.append("GET / HTTP/1.1\n\n", 16);
+ Config.onoff.relaxed_header_parser = 1;
struct resultSet expect = {
.parsed = true,
.needsMore = false,
.parserState = Http1::HTTP_PARSE_DONE,
.status = Http::scOkay,
- .msgStart = 0,
- .msgEnd = (int)input.length()-2,
.suffixSz = 0,
- .methodStart = 0,
- .methodEnd = 2,
.method = HttpRequestMethod(Http::METHOD_GET),
- .uriStart = 4,
- .uriEnd = 4,
.uri = "/",
- .versionStart = 6,
- .versionEnd = 13,
.version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
};
output.clear();
testResults(__LINE__, input, output, expect);
- input.clear();
- }
- // alternative EOL sequence: multi-CR-NL
- {
- input.append("GET / HTTP/1.1\r\r\r\n", 18);
- // Being tolerant we can ignore and elide these apparently benign CR
- Config.onoff.relaxed_header_parser = 1;
- struct resultSet expectRelaxed = {
- .parsed = false,
- .needsMore = true,
- .parserState = Http1::HTTP_PARSE_MIME,
- .status = Http::scOkay,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
- .suffixSz = 0,
- .methodStart = 0,
- .methodEnd = 2,
- .method = HttpRequestMethod(Http::METHOD_GET),
- .uriStart = 4,
- .uriEnd = 4,
- .uri = "/",
- .versionStart = 6,
- .versionEnd = 13,
- .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
- };
- output.clear();
- testResults(__LINE__, input, output, expectRelaxed);
-
- // strict mode treats these as several bare-CR in the request line which is explicitly invalid.
Config.onoff.relaxed_header_parser = 0;
struct resultSet expectStrict = {
.parsed = false,
.needsMore = false,
- .parserState = Http1::HTTP_PARSE_MIME,
+ .parserState = Http1::HTTP_PARSE_DONE,
.status = Http::scBadRequest,
- .msgStart = 0,
- .msgEnd = -1,
.suffixSz = input.length(),
- .methodStart =-1,
- .methodEnd = -1,
.method = HttpRequestMethod(),
- .uriStart = -1,
- .uriEnd = -1,
.uri = NULL,
- .versionStart = -1,
- .versionEnd = -1,
.version = AnyP::ProtocolVersion()
};
output.clear();
// space padded version
{
- // RFC 1945 and 2616 specify version is followed by CRLF. No intermediary bytes.
- // NP: the terminal whitespace is a special case: invalid for even HTTP/0.9 with no version tag
- input.append("GET / HTTP/1.1 \n", 16);
+ // RFC 7230 specifies version is followed by CRLF. No intermediary bytes.
+ input.append("GET / HTTP/1.1 \r\n", 17);
struct resultSet expect = {
.parsed = false,
.needsMore = false,
.parserState = Http1::HTTP_PARSE_DONE,
.status = Http::scBadRequest,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
.suffixSz = input.length(),
- .methodStart = 0,
- .methodEnd = 2,
- .method = HttpRequestMethod(Http::METHOD_GET),
- .uriStart = 4,
- .uriEnd = 13,
- .uri = "/ HTTP/1.1",
- .versionStart = -1,
- .versionEnd = -1,
+ .method = HttpRequestMethod(),
+ .uri = NULL,
.version = AnyP::ProtocolVersion()
};
output.clear();
SBuf input;
Http1::RequestParser output;
- // RFC 2616 : . method
+ // RFC 7230 : dot method
{
- input.append(". / HTTP/1.1\n", 13);
+ input.append(". / HTTP/1.1\r\n", 14);
struct resultSet expect = {
.parsed = false,
.needsMore = true,
.parserState = Http1::HTTP_PARSE_MIME,
.status = Http::scOkay,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
.suffixSz = 0,
- .methodStart = 0,
- .methodEnd = 0,
- .method = HttpRequestMethod("."),
- .uriStart = 2,
- .uriEnd = 2,
+ .method = HttpRequestMethod(SBuf(".")),
+ .uri = "/",
+ .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
+ };
+ output.clear();
+ testResults(__LINE__, input, output, expect);
+ input.clear();
+ }
+
+ // RFC 7230 : special TCHAR method chars
+ {
+ input.append("!#$%&'*+-.^_`|~ / HTTP/1.1\r\n", 28);
+ struct resultSet expect = {
+ .parsed = false,
+ .needsMore = true,
+ .parserState = Http1::HTTP_PARSE_MIME,
+ .status = Http::scOkay,
+ .suffixSz = 0,
+ .method = HttpRequestMethod(SBuf("!#$%&'*+-.^_`|~")),
.uri = "/",
- .versionStart = 4,
- .versionEnd = 11,
.version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
};
output.clear();
// OPTIONS with * URL
{
- input.append("OPTIONS * HTTP/1.1\n", 19);
+ input.append("OPTIONS * HTTP/1.1\r\n", 20);
struct resultSet expect = {
.parsed = false,
.needsMore = true,
.parserState = Http1::HTTP_PARSE_MIME,
.status = Http::scOkay,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
.suffixSz = 0,
- .methodStart = 0,
- .methodEnd = 6,
.method = HttpRequestMethod(Http::METHOD_OPTIONS),
- .uriStart = 8,
- .uriEnd = 8,
.uri = "*",
- .versionStart = 10,
- .versionEnd = 17,
.version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
};
output.clear();
// unknown method
{
- input.append("HELLOWORLD / HTTP/1.1\n", 22);
+ input.append("HELLOWORLD / HTTP/1.1\r\n", 23);
struct resultSet expect = {
.parsed = false,
.needsMore = true,
.parserState = Http1::HTTP_PARSE_MIME,
.status = Http::scOkay,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
.suffixSz = 0,
- .methodStart = 0,
- .methodEnd = 9,
- .method = HttpRequestMethod("HELLOWORLD"),
- .uriStart = 11,
- .uriEnd = 11,
+ .method = HttpRequestMethod(SBuf("HELLOWORLD")),
.uri = "/",
- .versionStart = 13,
- .versionEnd = 20,
.version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
};
output.clear();
input.clear();
}
+#if 0
+ // too-long method (over 16 bytes)
+ {
+ input.append("HELLOSTRANGEWORLD / HTTP/1.1\r\n", 31);
+ struct resultSet expect = {
+ .parsed = false,
+ .needsMore = false,
+ .parserState = Http1::HTTP_PARSE_DONE,
+ .status = Http::scNotImplemented,
+ .suffixSz = input.length(),
+ .method = HttpRequestMethod(),
+ .uri = NULL,
+ .version = AnyP::ProtocolVersion()
+ };
+ output.clear();
+ testResults(__LINE__, input, output, expect);
+ input.clear();
+ }
+#endif
+
// method-only
{
input.append("A\n", 2);
.needsMore = false,
.parserState = Http1::HTTP_PARSE_DONE,
.status = Http::scBadRequest,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
.suffixSz = input.length(),
- .methodStart = 0,
- .methodEnd = -1,
.method = HttpRequestMethod(),
- .uriStart = -1,
- .uriEnd = -1,
.uri = NULL,
- .versionStart = -1,
- .versionEnd = -1,
.version = AnyP::ProtocolVersion()
};
output.clear();
.needsMore = false,
.parserState = Http1::HTTP_PARSE_DONE,
.status = Http::scBadRequest,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
.suffixSz = input.length(),
- .methodStart = 0,
- .methodEnd = -1,
.method = HttpRequestMethod(),
- .uriStart = -1,
- .uriEnd = -1,
.uri = NULL,
- .versionStart = -1,
- .versionEnd = -1,
.version = AnyP::ProtocolVersion()
};
output.clear();
input.clear();
}
- // space padded method (in strict mode SP is reserved so invalid as a method byte)
+ // space padded method (SP is reserved so invalid as a method byte)
+ {
+ input.append(" GET / HTTP/1.1\r\n", 17);
+ struct resultSet expect = {
+ .parsed = false,
+ .needsMore = false,
+ .parserState = Http1::HTTP_PARSE_DONE,
+ .status = Http::scBadRequest,
+ .suffixSz = input.length(),
+ .method = HttpRequestMethod(),
+ .uri = NULL,
+ .version = AnyP::ProtocolVersion()
+ };
+ output.clear();
+ testResults(__LINE__, input, output, expect);
+ input.clear();
+ }
+
+ // RFC 7230 defined tolerance: ignore empty line(s) prefix on messages
{
- input.append(" GET / HTTP/1.1\n", 16);
- // RELAXED mode Squid custom tolerance ignores SP
-#if USE_HTTP_VIOLATIONS
+ input.append("\r\n\r\n\nGET / HTTP/1.1\r\n", 21);
Config.onoff.relaxed_header_parser = 1;
- struct resultSet expectRelaxed = {
+ struct resultSet expect = {
.parsed = false,
.needsMore = true,
.parserState = Http1::HTTP_PARSE_MIME,
.status = Http::scOkay,
- .msgStart = 0, // garbage collection consumes the SP
- .msgEnd = (int)input.length()-2,
.suffixSz = 0,
- .methodStart = 0,
- .methodEnd = 2,
.method = HttpRequestMethod(Http::METHOD_GET),
- .uriStart = 4,
- .uriEnd = 4,
.uri = "/",
- .versionStart = 6,
- .versionEnd = 13,
.version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
};
output.clear();
- testResults(__LINE__, input, output, expectRelaxed);
-#endif
+ testResults(__LINE__, input, output, expect);
- // STRICT mode obeys RFC syntax
Config.onoff.relaxed_header_parser = 0;
struct resultSet expectStrict = {
.parsed = false,
.needsMore = false,
.parserState = Http1::HTTP_PARSE_DONE,
.status = Http::scBadRequest,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
.suffixSz = input.length(),
- .methodStart = 0,
- .methodEnd = -1,
.method = HttpRequestMethod(),
- .uriStart = -1,
- .uriEnd = -1,
.uri = NULL,
- .versionStart = -1,
- .versionEnd = -1,
.version = AnyP::ProtocolVersion()
};
output.clear();
input.clear();
}
- // RFC 2616 defined tolerance: ignore empty line(s) prefix on messages
-#if WHEN_RFC_COMPLIANT
+ // forbidden character in method
{
- input.append("\r\n\r\n\nGET / HTTP/1.1\r\n", 21);
+ input.append("\tGET / HTTP/1.1\r\n", 17);
struct resultSet expect = {
.parsed = false,
.needsMore = false,
+ .parserState = Http1::HTTP_PARSE_DONE,
+ .status = Http::scBadRequest,
+ .suffixSz = input.length(),
+ .method = HttpRequestMethod(),
+ .uri = NULL,
+ .version = AnyP::ProtocolVersion()
+ };
+ output.clear();
+ testResults(__LINE__, input, output, expect);
+ input.clear();
+ }
+
+ // CR in method delimiters
+ {
+ // RFC 7230 section 3.5 permits CR in whitespace but only for tolerant parsers
+ input.append("GET\r / HTTP/1.1\r\n", 17);
+ Config.onoff.relaxed_header_parser = 1;
+ struct resultSet expect = {
+ .parsed = false,
+ .needsMore = true,
.parserState = Http1::HTTP_PARSE_MIME,
.status = Http::scOkay,
- .msgStart = 5,
- .msgEnd = (int)input.length()-1,
- .suffixSz = 5,
- .methodStart = 0,
- .methodEnd = 2,
+ .suffixSz = 0,
.method = HttpRequestMethod(Http::METHOD_GET),
- .uriStart = 4,
- .uriEnd = 4,
.uri = "/",
- .versionStart = 6,
- .versionEnd = 13,
.version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
};
output.clear();
testResults(__LINE__, input, output, expect);
- input.clear();
- }
-#endif
- // tab padded method (NP: tab is not SP so treated as any other binary)
- {
- input.append("\tGET / HTTP/1.1\n", 16);
-#if WHEN_RFC_COMPLIANT
- struct resultSet expect = {
+ Config.onoff.relaxed_header_parser = 0;
+ struct resultSet expectStrict = {
.parsed = false,
.needsMore = false,
.parserState = Http1::HTTP_PARSE_DONE,
.status = Http::scBadRequest,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
.suffixSz = input.length(),
- .methodStart = -1,
- .methodEnd = -1,
.method = HttpRequestMethod(),
- .uriStart = -1,
- .uriEnd = -1,
.uri = NULL,
- .versionStart = -1,
- .versionEnd = -1,
.version = AnyP::ProtocolVersion()
};
-#else // XXX: currently broken
+ output.clear();
+ testResults(__LINE__, input, output, expectStrict);
+ input.clear();
+ }
+
+ // tolerant parser delimiters
+ {
+ // RFC 7230 section 3.5 permits certain binary characters as whitespace delimiters
+ input.append("GET\r\t\x0B\x0C / HTTP/1.1\r\n", 20);
+ Config.onoff.relaxed_header_parser = 1;
struct resultSet expect = {
.parsed = false,
.needsMore = true,
.parserState = Http1::HTTP_PARSE_MIME,
.status = Http::scOkay,
- .msgStart = 0, // garbage collection consumes the SP
- .msgEnd = (int)input.length()-1,
.suffixSz = 0,
- .methodStart = 0,
- .methodEnd = 3,
- .method = HttpRequestMethod(SBuf("\tGET")),
- .uriStart = 5,
- .uriEnd = 5,
+ .method = HttpRequestMethod(Http::METHOD_GET),
.uri = "/",
- .versionStart = 7,
- .versionEnd = 14,
.version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
};
-#endif
output.clear();
testResults(__LINE__, input, output, expect);
+
+ Config.onoff.relaxed_header_parser = 0;
+ struct resultSet expectStrict = {
+ .parsed = false,
+ .needsMore = false,
+ .parserState = Http1::HTTP_PARSE_DONE,
+ .status = Http::scBadRequest,
+ .suffixSz = input.length(),
+ .method = HttpRequestMethod(),
+ .uri = NULL,
+ .version = AnyP::ProtocolVersion()
+ };
+ output.clear();
+ testResults(__LINE__, input, output, expectStrict);
input.clear();
}
}
SBuf input;
Http1::RequestParser output;
- // no method (but in a form which is ambiguous with HTTP/0.9 simple-request)
+ // no method (or method delimiter)
{
- // XXX: HTTP/0.9 requires method to be "GET"
+ // HTTP/0.9 requires method to be "GET"
input.append("/ HTTP/1.0\n", 11);
struct resultSet expect = {
- .parsed = true,
+ .parsed = false,
.needsMore = false,
- .parserState = Http1::HTTP_PARSE_MIME,
- .status = Http::scOkay,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
- .suffixSz = 0,
- .methodStart = 0,
- .methodEnd = 0,
- .method = HttpRequestMethod("/"),
- .uriStart = 2,
- .uriEnd = 9,
- .uri = "HTTP/1.0",
- .versionStart = -1,
- .versionEnd = -1,
- .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9)
+ .parserState = Http1::HTTP_PARSE_DONE,
+ .status = Http::scBadRequest,
+ .suffixSz = input.length(),
+ .method = HttpRequestMethod(),
+ .uri = NULL,
+ .version = AnyP::ProtocolVersion()
};
output.clear();
testResults(__LINE__, input, output, expect);
input.clear();
}
- // no method (an invalid format)
+ // no method (with method delimiter)
{
input.append(" / HTTP/1.0\n", 12);
-
- // XXX: squid custom tolerance consumes initial SP.
- Config.onoff.relaxed_header_parser = 1;
- struct resultSet expectRelaxed = {
- .parsed = true,
- .needsMore = false,
- .parserState = Http1::HTTP_PARSE_MIME,
- .status = Http::scOkay,
- .msgStart = 0,
- .msgEnd = (int)input.length()-2,
- .suffixSz = 0,
- .methodStart = 0,
- .methodEnd = 0,
- .method = HttpRequestMethod("/"),
- .uriStart = 2,
- .uriEnd = 9,
- .uri = "HTTP/1.0",
- .versionStart = -1,
- .versionEnd = -1,
- .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9)
- };
- output.clear();
- testResults(__LINE__, input, output, expectRelaxed);
-
- // STRICT detect as invalid
- Config.onoff.relaxed_header_parser = 0;
-#if WHEN_RFC_COMPLIANT
- // XXX: except Squid does not
- struct resultSet expectStrict = {
- .parsed = false,
- .needsMore = false,
- .parserState = Http1::HTTP_PARSE_DONE,
- .status = Http::scBadRequest,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
- .suffixSz = 0,
- .methodStart = 0,
- .methodEnd = -1,
- .method = HttpRequestMethod(),
- .uriStart = -1,
- .uriEnd = -1,
- .uri = NULL,
- .versionStart = -1,
- .versionEnd = -1,
- .version = AnyP::ProtocolVersion()
- };
-#else
struct resultSet expectStrict = {
.parsed = false,
.needsMore = false,
.parserState = Http1::HTTP_PARSE_DONE,
.status = Http::scBadRequest,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
.suffixSz = input.length(),
- .methodStart = 0,
- .methodEnd = -1,
.method = HttpRequestMethod(),
- .uriStart = -1,
- .uriEnd = -1,
.uri = NULL,
- .versionStart = -1,
- .versionEnd = -1,
.version = AnyP::ProtocolVersion()
};
-#endif
output.clear();
testResults(__LINE__, input, output, expectStrict);
input.clear();
}
- // binary code in method (invalid)
+ // binary code after method (invalid)
{
- input.append("GET\x0B / HTTP/1.1\n", 16);
-#if WHEN_RFC_COMPLIANT
+ input.append("GET\x16 / HTTP/1.1\r\n", 17);
struct resultSet expect = {
.parsed = false,
.needsMore = false,
.parserState = Http1::HTTP_PARSE_DONE,
.status = Http::scBadRequest,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
.suffixSz = input.length(),
- .methodStart = -1,
- .methodEnd = -1,
.method = HttpRequestMethod(),
- .uriStart = -1,
- .uriEnd = -1,
.uri = NULL,
- .versionStart = -1,
- .versionEnd = -1,
.version = AnyP::ProtocolVersion()
};
-#else
- struct resultSet expect = {
- .parsed = false,
- .needsMore = true,
- .parserState = Http1::HTTP_PARSE_MIME,
- .status = Http::scOkay,
- .msgStart = 0, // garbage collection consumes the SP
- .msgEnd = (int)input.length()-1,
- .suffixSz = 0,
- .methodStart = 0,
- .methodEnd = 3,
- .method = HttpRequestMethod(SBuf("GET\x0B")),
- .uriStart = 5,
- .uriEnd = 5,
- .uri = "/",
- .versionStart = 7,
- .versionEnd = 14,
- .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
- };
-#endif
output.clear();
testResults(__LINE__, input, output, expect);
input.clear();
}
- // CR in method
+ // binary code NUL! after method (always invalid)
{
- // RFC 2616 sec 5.1 prohibits CR other than in terminator.
- input.append("GET\r / HTTP/1.1\r\n", 16);
+ input.append("GET\0 / HTTP/1.1\r\n", 17);
struct resultSet expect = {
.parsed = false,
.needsMore = false,
.parserState = Http1::HTTP_PARSE_DONE,
.status = Http::scBadRequest,
- .msgStart = 0,
- .msgEnd = -1, // halt at the first \r
.suffixSz = input.length(),
- .methodStart = -1,
- .methodEnd = -1,
.method = HttpRequestMethod(),
- .uriStart = -1,
- .uriEnd = -1,
.uri = NULL,
- .versionStart = -1,
- .versionEnd = -1,
.version = AnyP::ProtocolVersion()
};
output.clear();
input.clear();
}
- // binary code NUL! in method (strange but ...)
+ // Either an RFC 1945 HTTP/0.9 simple-request for an "HTTP/1.1" URI or
+ // an invalid (no URI) HTTP/1.1 request. We treat this as latter, naturally.
{
- input.append("GET\0 / HTTP/1.1\n", 16);
-#if WHEN_RFC_COMPLIANT
+ input.append("GET HTTP/1.1\r\n", 15);
+ Config.onoff.relaxed_header_parser = 1;
struct resultSet expect = {
.parsed = false,
.needsMore = false,
.parserState = Http1::HTTP_PARSE_DONE,
.status = Http::scBadRequest,
- .msgStart = 0,
- .msgEnd = -1, // halt at the \0
.suffixSz = input.length(),
- .methodStart = -1,
- .methodEnd = -1,
.method = HttpRequestMethod(),
- .uriStart = -1,
- .uriEnd = -1,
.uri = NULL,
- .versionStart = -1,
- .versionEnd = -1,
.version = AnyP::ProtocolVersion()
};
-#else
- struct resultSet expect = {
- .parsed = false,
- .needsMore = true,
- .parserState = Http1::HTTP_PARSE_MIME,
- .status = Http::scOkay,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
- .suffixSz = 0,
- .methodStart = 0,
- .methodEnd = 3,
- .method = HttpRequestMethod(SBuf("GET\0",4)),
- .uriStart = 5,
- .uriEnd = 5,
- .uri = "/",
- .versionStart = 7,
- .versionEnd = 14,
- .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
- };
-#endif
output.clear();
testResults(__LINE__, input, output, expect);
- input.clear();
- }
- // no URL (grammer invalid, ambiguous with RFC 1945 HTTP/0.9 simple-request)
- {
- input.append("GET HTTP/1.1\n", 14);
- struct resultSet expect = {
- .parsed = true,
+ Config.onoff.relaxed_header_parser = 0;
+ struct resultSet expectStrict = {
+ .parsed = false,
.needsMore = false,
.parserState = Http1::HTTP_PARSE_DONE,
- .status = Http::scOkay,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
- .suffixSz = 0,
- .methodStart = 0,
- .methodEnd = 2,
- .method = HttpRequestMethod(Http::METHOD_GET),
- .uriStart = 5,
- .uriEnd = 12,
- .uri = "HTTP/1.1",
- .versionStart = -1,
- .versionEnd = -1,
- .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9)
+ .status = Http::scBadRequest,
+ .suffixSz = input.length(),
+ .method = HttpRequestMethod(),
+ .uri = NULL,
+ .version = AnyP::ProtocolVersion()
};
output.clear();
- testResults(__LINE__, input, output, expect);
+ testResults(__LINE__, input, output, expectStrict);
input.clear();
}
- // no URL (grammer invalid, ambiguous with RFC 1945 HTTP/0.9 simple-request)
+ // Either an RFC 1945 HTTP/0.9 simple-request for an "HTTP/1.1" URI or
+ // an invalid (no URI) HTTP/1.1 request. We treat this as latter, naturally.
{
- input.append("GET HTTP/1.1\n", 13);
+ input.append("GET HTTP/1.1\r\n", 14);
struct resultSet expect = {
- .parsed = true,
+ .parsed = false,
.needsMore = false,
.parserState = Http1::HTTP_PARSE_DONE,
- .status = Http::scOkay,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
- .suffixSz = 0,
- .methodStart = 0,
- .methodEnd = 2,
- .method = HttpRequestMethod(Http::METHOD_GET),
- .uriStart = 4,
- .uriEnd = 11,
- .uri = "HTTP/1.1",
- .versionStart = -1,
- .versionEnd = -1,
- .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9)
+ .status = Http::scBadRequest,
+ .suffixSz = input.length(),
+ .method = HttpRequestMethod(),
+ .uri = NULL,
+ .version = AnyP::ProtocolVersion()
};
output.clear();
testResults(__LINE__, input, output, expect);
.needsMore = false,
.parserState = Http1::HTTP_PARSE_DONE,
.status = Http::scBadRequest,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
.suffixSz = input.length(),
- .methodStart = 0,
- .methodEnd = -1,
.method = HttpRequestMethod(),
- .uriStart = -1,
- .uriEnd = -1,
.uri = NULL,
- .versionStart = -1,
- .versionEnd = -1,
.version = AnyP::ProtocolVersion()
};
output.clear();
// mixed whitespace line
{
- // We accept non-space binary bytes for method so first \t shows up as that
- // but remaining space and tabs are skipped searching for URI-start
input.append("\t \t \t\n", 6);
struct resultSet expect = {
.parsed = false,
.needsMore = false,
.parserState = Http1::HTTP_PARSE_DONE,
.status = Http::scBadRequest,
- .msgStart = 0,
- .msgEnd = (int)input.length()-1,
.suffixSz = input.length(),
- .methodStart = 0,
- .methodEnd = 0,
- .method = HttpRequestMethod(SBuf("\t")),
- .uriStart = -1,
- .uriEnd = -1,
+ .method = HttpRequestMethod(),
.uri = NULL,
- .versionStart = -1,
- .versionEnd = -1,
.version = AnyP::ProtocolVersion()
};
output.clear();
input.clear();
}
- // mixed whitespace line with CR middle
+ // mixed whitespace line with CR
{
- // CR aborts on sight, so even initial \t method is not marked as above
- // (not when parsing clean with whole line available anyway)
- input.append("\t \r \n", 6);
+ input.append("\r \t \n", 6);
struct resultSet expect = {
.parsed = false,
.needsMore = false,
.parserState = Http1::HTTP_PARSE_DONE,
.status = Http::scBadRequest,
- .msgStart = 0,
- .msgEnd = -1, // halt on the \r
.suffixSz = input.length(),
- .methodStart = -1,
- .methodEnd = -1,
.method = HttpRequestMethod(),
- .uriStart = -1,
- .uriEnd = -1,
.uri = NULL,
- .versionStart = -1,
- .versionEnd = -1,
.version = AnyP::ProtocolVersion()
};
output.clear();
// calling the parser repeatedly as visible data grows.
SBuf data;
- data.append(" ", 12);
+ data.append("\n\n\n\n\n\n\n\n\n\n\n\n", 12);
SBuf::size_type garbageEnd = data.length();
- data.append("GET http://example.com/ HTTP/1.1\r\n", 34);
+ data.append("GET ", 4);
+ data.append("http://example.com/ ", 20);
+ data.append("HTTP/1.1\r\n", 10);
SBuf::size_type reqLineEnd = data.length() - 1;
data.append("Host: example.com\r\n\r\n", 21);
SBuf::size_type mimeEnd = data.length() - 1;
data.append("...", 3); // trailer to catch mime EOS errors.
- SBuf ioBuf; // begins empty
+ SBuf ioBuf;
Http1::RequestParser hp;
- // only relaxed parser accepts the garbage whitespace
- Config.onoff.relaxed_header_parser = 1;
-
- // state of things we expect right now
- struct resultSet expect = {
- .parsed = false,
- .needsMore = true,
- .parserState = Http1::HTTP_PARSE_NONE,
- .status = Http::scBadRequest,
- .msgStart = 0,
- .msgEnd = -1,
- .suffixSz = 0,
- .methodStart = -1,
- .methodEnd = -1,
- .method = HttpRequestMethod(),
- .uriStart = -1,
- .uriEnd = -1,
- .uri = NULL,
- .versionStart = -1,
- .versionEnd = -1,
- .version = AnyP::ProtocolVersion()
- };
+ // start with strict and move on to relaxed
+ Config.onoff.relaxed_header_parser = 2;
Config.maxRequestHeaderSize = 1024; // large enough to hold the test data.
- for (SBuf::size_type pos = 0; pos <= data.length(); ++pos) {
+ do {
- // simulate reading one more byte
- ioBuf.append(data.substr(pos,1));
-
- // when the garbage is passed we expect to start seeing first-line bytes
- if (pos == garbageEnd) {
- expect.parserState = Http1::HTTP_PARSE_FIRST;
- expect.msgStart = 0;
- }
-
- // all points after garbage start to see accumulated bytes looking for end of current section
- if (pos >= garbageEnd)
- expect.suffixSz = ioBuf.length();
-
- // at end of request line expect to see method, URI, version details
- // and switch to seeking Mime header section
- if (pos == reqLineEnd) {
- expect.parserState = Http1::HTTP_PARSE_MIME;
- expect.suffixSz = 0;
- expect.msgEnd = reqLineEnd-garbageEnd;
- expect.status = Http::scOkay;
- expect.methodStart = 0;
- expect.methodEnd = 2;
- expect.method = HttpRequestMethod(Http::METHOD_GET);
- expect.uriStart = 4;
- expect.uriEnd = 22;
- expect.uri = "http://example.com/";
- expect.versionStart = 24;
- expect.versionEnd = 31;
- expect.version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1);
- }
+ // state of things we expect right now
+ struct resultSet expect = {
+ .parsed = false,
+ .needsMore = true,
+ .parserState = Http1::HTTP_PARSE_NONE,
+ .status = Http::scNone,
+ .suffixSz = 0,
+ .method = HttpRequestMethod(),
+ .uri = NULL,
+ .version = AnyP::ProtocolVersion()
+ };
- // one mime header is done we are expectign a new request
- // parse results say true and initial data is all gone from the buffer
- if (pos == mimeEnd) {
- expect.parsed = true;
- expect.needsMore = false;
- expect.suffixSz = 0;
+ ioBuf.clear(); // begins empty for each parser type
+ hp.clear();
+
+ --Config.onoff.relaxed_header_parser;
+
+ for (SBuf::size_type pos = 0; pos <= data.length(); ++pos) {
+
+ // simulate reading one more byte
+ ioBuf.append(data.substr(pos,1));
+
+ // strict does not permit the garbage prefix
+ if (pos < garbageEnd && !Config.onoff.relaxed_header_parser) {
+ ioBuf.clear();
+ continue;
+ }
+
+ // when the garbage is passed we expect to start seeing first-line bytes
+ if (pos == garbageEnd)
+ expect.parserState = Http1::HTTP_PARSE_FIRST;
+
+ // all points after garbage start to see accumulated bytes looking for end of current section
+ if (pos >= garbageEnd)
+ expect.suffixSz = ioBuf.length();
+
+ // at end of request line expect to see method, URI, version details
+ // and switch to seeking Mime header section
+ if (pos == reqLineEnd) {
+ expect.parserState = Http1::HTTP_PARSE_MIME;
+ expect.suffixSz = 0; // and a checkpoint buffer reset
+ expect.status = Http::scOkay;
+ expect.method = HttpRequestMethod(Http::METHOD_GET);
+ expect.uri = "http://example.com/";
+ expect.version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1);
+ }
+
+ // one mime header is done we are expecting a new request
+ // parse results say true and initial data is all gone from the buffer
+ if (pos == mimeEnd) {
+ expect.parsed = true;
+ expect.needsMore = false;
+ expect.suffixSz = 0; // and a checkpoint buffer reset
+ }
+
+ testResults(__LINE__, ioBuf, hp, expect);
+
+ // sync the buffers like Squid does
+ ioBuf = hp.remaining();
+
+ // Squid stops using the parser once it has parsed the first message.
+ if (!hp.needsMoreData())
+ break;
}
- testResults(__LINE__, ioBuf, hp, expect);
-
- // sync the buffers like Squid does
- ioBuf = hp.buf;
+ } while (Config.onoff.relaxed_header_parser);
- // Squid stops using the parser once it has parsed the first message.
- if (!hp.needsMoreData())
- break;
- }
}
+