]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/tests/testHttp1Parser.cc
Source Format Enforcement (#532)
[thirdparty/squid.git] / src / tests / testHttp1Parser.cc
index 4ca81c0188f9ffee9a85a8d305b95b1061aebfcc..54cb566c371cbce806cd55780d33ca509662a610 100644 (file)
@@ -1,4 +1,11 @@
-#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 "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 );
 
@@ -37,45 +44,78 @@ struct resultSet {
     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
 
-    CPPUNIT_ASSERT_EQUAL(expect.parsed, output.parse(input));
+    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
+
+    // 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_);
 }
 
 void
@@ -86,18 +126,10 @@ testHttp1Parser::testParserConstruct()
         Http1::RequestParser output;
         CPPUNIT_ASSERT_EQUAL(true, output.needsMoreData());
         CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_NONE, output.parsingStage_);
-        CPPUNIT_ASSERT_EQUAL(Http::scNone, output.request_parse_status); // XXX: clear() not being called.
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.end);
+        CPPUNIT_ASSERT_EQUAL(Http::scNone, output.parseStatusCode); // XXX: clear() not being called.
         CPPUNIT_ASSERT(output.buf_.isEmpty());
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end);
         CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_NONE), output.method_);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end);
         CPPUNIT_ASSERT(output.uri_.isEmpty());
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end);
         CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_);
     }
 
@@ -106,18 +138,10 @@ testHttp1Parser::testParserConstruct()
         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->request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(-1, output->req.start);
-        CPPUNIT_ASSERT_EQUAL(-1, output->req.end);
+        CPPUNIT_ASSERT_EQUAL(Http::scNone, output->parseStatusCode);
         CPPUNIT_ASSERT(output->buf_.isEmpty());
-        CPPUNIT_ASSERT_EQUAL(-1, output->req.m_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output->req.m_end);
         CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_NONE), output->method_);
-        CPPUNIT_ASSERT_EQUAL(-1, output->req.u_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output->req.u_end);
         CPPUNIT_ASSERT(output->uri_.isEmpty());
-        CPPUNIT_ASSERT_EQUAL(-1, output->req.v_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output->req.v_end);
         CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output->msgProtocol_);
         delete output;
     }
@@ -133,7 +157,7 @@ testHttp1Parser::testParseRequestLineProtocols()
     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
     {
@@ -143,17 +167,9 @@ testHttp1Parser::testParseRequestLineProtocols()
             .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();
@@ -162,33 +178,24 @@ testHttp1Parser::testParseRequestLineProtocols()
     }
 
     // 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 = {
@@ -196,17 +203,9 @@ testHttp1Parser::testParseRequestLineProtocols()
             .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();
@@ -214,7 +213,7 @@ testHttp1Parser::testParseRequestLineProtocols()
         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 = {
@@ -222,17 +221,9 @@ testHttp1Parser::testParseRequestLineProtocols()
             .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();
@@ -240,7 +231,7 @@ testHttp1Parser::testParseRequestLineProtocols()
         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 = {
@@ -248,17 +239,9 @@ testHttp1Parser::testParseRequestLineProtocols()
             .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();
@@ -266,25 +249,52 @@ testHttp1Parser::testParseRequestLineProtocols()
         input.clear();
     }
 
-    // RFC 7230 : future versions do not use request-line syntax
+    // 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
     {
         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()
         };
         output.clear();
@@ -294,23 +304,15 @@ testHttp1Parser::testParseRequestLineProtocols()
 
     // unknown non-HTTP protocol names
     {
-        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();
@@ -318,25 +320,17 @@ testHttp1Parser::testParseRequestLineProtocols()
         input.clear();
     }
 
-    // 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()
         };
         output.clear();
@@ -346,23 +340,15 @@ testHttp1Parser::testParseRequestLineProtocols()
 
     // 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()
         };
         output.clear();
@@ -372,23 +358,15 @@ testHttp1Parser::testParseRequestLineProtocols()
 
     // 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()
         };
         output.clear();
@@ -398,23 +376,15 @@ testHttp1Parser::testParseRequestLineProtocols()
 
     // 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()
         };
         output.clear();
@@ -424,24 +394,16 @@ testHttp1Parser::testParseRequestLineProtocols()
 
     // 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);
@@ -450,24 +412,16 @@ testHttp1Parser::testParseRequestLineProtocols()
 
     // 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);
@@ -487,80 +441,87 @@ testHttp1Parser::testParseRequestLineStrange()
     // 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;
     }
 }
 
@@ -574,101 +535,67 @@ testHttp1Parser::testParseRequestLineTerminators()
     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();
@@ -678,25 +605,16 @@ testHttp1Parser::testParseRequestLineTerminators()
 
     // 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();
@@ -714,25 +632,35 @@ testHttp1Parser::testParseRequestLineMethods()
     SBuf input;
     Http1::RequestParser output;
 
-    // RFC 2616 : . method
+    // RFC 7230 : dot method
+    {
+        input.append(". / HTTP/1.1\r\n", 14);
+        struct resultSet expect = {
+            .parsed = false,
+            .needsMore = true,
+            .parserState = Http1::HTTP_PARSE_MIME,
+            .status = Http::scOkay,
+            .suffixSz = 0,
+            .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\n", 13);
+        input.append("!#$%&'*+-.^_`|~ / HTTP/1.1\r\n", 28);
         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 = "/",
-            .versionStart = 4,
-            .versionEnd = 11,
             .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
         };
         output.clear();
@@ -742,23 +670,15 @@ testHttp1Parser::testParseRequestLineMethods()
 
     // 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();
@@ -768,23 +688,15 @@ testHttp1Parser::testParseRequestLineMethods()
 
     // 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();
@@ -792,6 +704,26 @@ testHttp1Parser::testParseRequestLineMethods()
         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);
@@ -800,17 +732,9 @@ testHttp1Parser::testParseRequestLineMethods()
             .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();
@@ -825,17 +749,9 @@ testHttp1Parser::testParseRequestLineMethods()
             .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();
@@ -843,52 +759,50 @@ testHttp1Parser::testParseRequestLineMethods()
         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();
@@ -896,78 +810,89 @@ testHttp1Parser::testParseRequestLineMethods()
         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();
     }
 }
@@ -981,174 +906,72 @@ testHttp1Parser::testParseRequestLineInvalid()
     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();
@@ -1156,99 +979,53 @@ testHttp1Parser::testParseRequestLineInvalid()
         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);
@@ -1263,17 +1040,9 @@ testHttp1Parser::testParseRequestLineInvalid()
             .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();
@@ -1283,25 +1052,15 @@ testHttp1Parser::testParseRequestLineInvalid()
 
     // 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();
@@ -1309,27 +1068,17 @@ testHttp1Parser::testParseRequestLineInvalid()
         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();
@@ -1346,90 +1095,92 @@ testHttp1Parser::testDripFeed()
     // 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::scNone,
-        .msgStart = -1,
-        .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.remaining();
+    } while (Config.onoff.relaxed_header_parser);
 
-        // Squid stops using the parser once it has parsed the first message.
-        if (!hp.needsMoreData())
-            break;
-    }
 }
+