]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/tests/testHttp1Parser.cc
Source Format Enforcement (#532)
[thirdparty/squid.git] / src / tests / testHttp1Parser.cc
index fee350b5ad8dd5b6b9d92695a98268949a44b98c..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 "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 );
 
@@ -32,353 +39,393 @@ testHttp1Parser::globalSetup()
     Config.maxRequestHeaderSize = 1024; // XXX: unit test the RequestParser handling of this limit
 }
 
+struct resultSet {
+    bool parsed;
+    bool needsMore;
+    Http1::ParseState parserState;
+    Http::StatusCode status;
+    SBuf::size_type suffixSz;
+    HttpRequestMethod method;
+    const char *uri;
+    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)
+{
+#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
+
+    // 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.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
 testHttp1Parser::testParseRequestLineProtocols()
 {
     // ensure MemPools etc exist
     globalSetup();
 
-    MemBuf input;
+    SBuf input;
     Http1::RequestParser output;
-    input.init();
 
     // 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
     {
         input.append("GET /\r\n", 7);
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(true, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0,memcmp("GET /\r\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(2, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start], (output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start], (output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9), output.msgProtocol_);
-        input.reset();
+        struct resultSet expect = {
+            .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,0,9)
+        };
+        output.clear();
+        testResults(__LINE__, input, output, expect);
+        input.clear();
     }
 
     // RFC 1945 : invalid HTTP/0.9 simple-request (only GET is valid)
-#if 0
     {
-        input.append("POST /\r\n", 7);
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(true, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0,memcmp("POST /\r\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(3, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("POST", &output.buf[output.req.m_start], (output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_POST), output.method_);
-        CPPUNIT_ASSERT_EQUAL(5, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(5, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start], (output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_);
-        input.reset();
+        input.append("POST /\r\n", 8);
+        struct resultSet expect = {
+            .parsed = false,
+            .needsMore = false,
+            .parserState = Http1::HTTP_PARSE_DONE,
+            .status = Http::scBadRequest,
+            .suffixSz = input.length(),
+            .method = HttpRequestMethod(Http::METHOD_POST),
+            .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);
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
-        CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.0\r\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(2, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(6, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(13, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.0", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1)));
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,0), output.msgProtocol_);
-        input.reset();
+        struct resultSet expect = {
+            .parsed = false,
+            .needsMore = true,
+            .parserState = Http1::HTTP_PARSE_MIME,
+            .status = Http::scOkay,
+            .suffixSz = 0,
+            .method = HttpRequestMethod(Http::METHOD_GET),
+            .uri = "/",
+            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,0)
+        };
+        output.clear();
+        testResults(__LINE__, input, output, expect);
+        input.clear();
     }
 
-    // RFC 2616 : HTTP/1.1 request
+    // RFC 7230 : HTTP/1.1 request
     {
         input.append("GET / HTTP/1.1\r\n", 16);
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
-        CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.1\r\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(2, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(6, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(13, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1)));
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_);
-        input.reset();
+        struct resultSet expect = {
+            .parsed = false,
+            .needsMore = true,
+            .parserState = Http1::HTTP_PARSE_MIME,
+            .status = Http::scOkay,
+            .suffixSz = 0,
+            .method = HttpRequestMethod(Http::METHOD_GET),
+            .uri = "/",
+            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
+        };
+        output.clear();
+        testResults(__LINE__, input, output, expect);
+        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);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
-        CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.2\r\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(2, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(6, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(13, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.2", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1)));
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,2), output.msgProtocol_);
-        input.reset();
+        struct resultSet expect = {
+            .parsed = false,
+            .needsMore = true,
+            .parserState = Http1::HTTP_PARSE_MIME,
+            .status = Http::scOkay,
+            .suffixSz = 0,
+            .method = HttpRequestMethod(Http::METHOD_GET),
+            .uri = "/",
+            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,2)
+        };
+        output.clear();
+        testResults(__LINE__, input, output, expect);
+        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);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http::scHttpVersionNotSupported, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/10.12\r\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(2, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(6, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(15, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/10.12", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1)));
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,10,12), output.msgProtocol_);
-        input.reset();
+        struct resultSet expect = {
+            .parsed = false,
+            .needsMore = false,
+            .parserState = Http1::HTTP_PARSE_MIME,
+            .status = Http::scBadRequest,
+            .suffixSz = input.length(),
+            .method = HttpRequestMethod(Http::METHOD_GET),
+            .uri = "/",
+            .version = AnyP::ProtocolVersion()
+        };
+        output.clear();
+        testResults(__LINE__, input, output, expect);
+        input.clear();
     }
 
-    // This stage of the parser does not yet accept non-HTTP protocol names.
+    // unknown non-HTTP protocol names
     {
-        // violations mode treats them as HTTP/0.9 requests!
-        input.append("GET / FOO/1.0\n", 14);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-#if USE_HTTP_VIOLATIONS
-        CPPUNIT_ASSERT_EQUAL(true, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(12, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/ FOO/1.0", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9), output.msgProtocol_);
-#else
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http::scHttpVersionNotSupported, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_);
-#endif
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / FOO/1.0\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(2, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(6, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(12, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("FOO/1.0", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1)));
-        input.reset();
+        input.append("GET / FOO/1.0\r\n", 15);
+        struct resultSet expect = {
+            .parsed = false,
+            .needsMore = false,
+            .parserState = Http1::HTTP_PARSE_DONE,
+            .status = Http::scBadRequest,
+            .suffixSz = input.length(),
+            .method = HttpRequestMethod(Http::METHOD_GET),
+            .uri = "/",
+            .version = AnyP::ProtocolVersion()
+        };
+        output.clear();
+        testResults(__LINE__, input, output, expect);
+        input.clear();
     }
 
-    // no version
+    // no version digits
     {
-        input.append("GET / HTTP/\n", 12);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http::scHttpVersionNotSupported, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(2, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(6, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(10, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1)));
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,0), output.msgProtocol_);
-        input.reset();
+        input.append("GET / HTTP/\r\n", 13);
+        struct resultSet expect = {
+            .parsed = false,
+            .needsMore = false,
+            .parserState = Http1::HTTP_PARSE_DONE,
+            .status = Http::scBadRequest,
+            .suffixSz = input.length(),
+            .method = HttpRequestMethod(Http::METHOD_GET),
+            .uri = "/",
+            .version = AnyP::ProtocolVersion()
+        };
+        output.clear();
+        testResults(__LINE__, input, output, expect);
+        input.clear();
     }
 
     // no major version
     {
-        input.append("GET / HTTP/.1\n", 14);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http::scHttpVersionNotSupported, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(2, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(6, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(12, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1)));
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,0), output.msgProtocol_);
-        input.reset();
+        input.append("GET / HTTP/.1\r\n", 15);
+        struct resultSet expect = {
+            .parsed = false,
+            .needsMore = false,
+            .parserState = Http1::HTTP_PARSE_DONE,
+            .status = Http::scBadRequest,
+            .suffixSz = input.length(),
+            .method = HttpRequestMethod(Http::METHOD_GET),
+            .uri = "/",
+            .version = AnyP::ProtocolVersion()
+        };
+        output.clear();
+        testResults(__LINE__, input, output, expect);
+        input.clear();
     }
 
     // no version dot
     {
-        input.append("GET / HTTP/11\n", 14);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http::scHttpVersionNotSupported, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/11\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(2, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(6, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(12, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/11", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1)));
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,0), output.msgProtocol_);
-        input.reset();
+        input.append("GET / HTTP/11\r\n", 15);
+        struct resultSet expect = {
+            .parsed = false,
+            .needsMore = false,
+            .parserState = Http1::HTTP_PARSE_DONE,
+            .status = Http::scBadRequest,
+            .suffixSz = input.length(),
+            .method = HttpRequestMethod(Http::METHOD_GET),
+            .uri = "/",
+            .version = AnyP::ProtocolVersion()
+        };
+        output.clear();
+        testResults(__LINE__, input, output, expect);
+        input.clear();
     }
 
     // negative major version (bug 3062)
     {
-        input.append("GET / HTTP/-999999.1\n", 21);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http::scHttpVersionNotSupported, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/-999999.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(2, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(6, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(19, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/-999999.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1)));
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,0), output.msgProtocol_);
-        input.reset();
+        input.append("GET / HTTP/-999999.1\r\n", 22);
+        struct resultSet expect = {
+            .parsed = false,
+            .needsMore = false,
+            .parserState = Http1::HTTP_PARSE_DONE,
+            .status = Http::scBadRequest,
+            .suffixSz = input.length(),
+            .method = HttpRequestMethod(Http::METHOD_GET),
+            .uri = "/",
+            .version = AnyP::ProtocolVersion()
+        };
+        output.clear();
+        testResults(__LINE__, input, output, expect);
+        input.clear();
     }
 
     // no minor version
     {
-        input.append("GET / HTTP/1.\n", 14);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http::scHttpVersionNotSupported, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(2, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(6, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(12, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1)));
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,0), output.msgProtocol_);
-        input.reset();
+        input.append("GET / HTTP/1.\r\n", 15);
+        struct resultSet expect = {
+            .parsed = false,
+            .needsMore = false,
+            .parserState = Http1::HTTP_PARSE_DONE,
+            .status = Http::scBadRequest,
+            .suffixSz = input.length(),
+            .method = HttpRequestMethod(Http::METHOD_GET),
+            .uri = "/",
+            .version = AnyP::ProtocolVersion()
+        };
+        output.clear();
+        testResults(__LINE__, input, output, expect);
+        input.clear();
     }
 
     // negative major version (bug 3062 corollary)
     {
-        input.append("GET / HTTP/1.-999999\n", 21);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http::scHttpVersionNotSupported, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.-999999\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(2, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(6, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(19, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.-999999", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1)));
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,0), output.msgProtocol_);
-        input.reset();
+        input.append("GET / HTTP/1.-999999\r\n", 22);
+        struct resultSet expect = {
+            .parsed = false,
+            .needsMore = false,
+            .parserState = Http1::HTTP_PARSE_DONE,
+            .status = Http::scBadRequest,
+            .suffixSz = input.length(),
+            .method = HttpRequestMethod(Http::METHOD_GET),
+            .uri = "/",
+            .version = AnyP::ProtocolVersion()
+        };
+        output.clear();
+        testResults(__LINE__, input, output, expect);
+        input.clear();
     }
 }
 
@@ -388,86 +435,93 @@ testHttp1Parser::testParseRequestLineStrange()
     // ensure MemPools etc exist
     globalSetup();
 
-    MemBuf input;
+    SBuf input;
     Http1::RequestParser output;
-    input.init();
 
     // space padded URL
     {
         input.append("GET  /     HTTP/1.1\r\n", 21);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
-        CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET  /     HTTP/1.1\r\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(2, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_);
-        CPPUNIT_ASSERT_EQUAL(5, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(5, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(11, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(18, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1)));
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_);
-        input.reset();
+        // 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,
+            .suffixSz = 0,
+            .method = HttpRequestMethod(Http::METHOD_GET),
+            .uri = "/",
+            .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)
     {
-        input.append("GET /fo o/ HTTP/1.1\n", 20);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
-        CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0,memcmp("GET /fo o/ HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(2, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(9, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/fo o/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(11, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(18, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1)));
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_);
-        input.reset();
+        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,
+            .suffixSz = 0,
+            .method = HttpRequestMethod(Http::METHOD_GET),
+            .uri = "/fo o/",
+            .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);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
-        CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-5, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET /     HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(2, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_end); // strangeness generated by following RFC
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(10, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(17, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1)));
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_);
-        input.reset();
+        input.append("GET / HTTP/1.1\r\nboo!", 20);
+        struct resultSet expect = {
+            .parsed = false,
+            .needsMore = true,
+            .parserState = Http1::HTTP_PARSE_MIME,
+            .status = Http::scOkay,
+            .suffixSz = 4, // strlen("boo!")
+            .method = HttpRequestMethod(Http::METHOD_GET),
+            .uri = "/",
+            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
+        };
+        output.clear();
+        testResults(__LINE__, input, output, expect);
+        input.clear();
+        Config.onoff.relaxed_header_parser = 0;
     }
 }
 
@@ -477,215 +531,95 @@ testHttp1Parser::testParseRequestLineTerminators()
     // ensure MemPools etc exist
     globalSetup();
 
-    MemBuf input;
+    SBuf input;
     Http1::RequestParser output;
-    input.init();
 
     // alternative EOL sequence: NL-only
+    // RFC 7230 tolerance permits omitted CR
     {
         input.append("GET / HTTP/1.1\n", 15);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
-        CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(2, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(6, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(13, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1)));
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_);
-        input.reset();
+        Config.onoff.relaxed_header_parser = 1;
+        struct resultSet expect = {
+            .parsed = false,
+            .needsMore = true,
+            .parserState = Http1::HTTP_PARSE_MIME,
+            .status = Http::scOkay,
+            .suffixSz = 0,
+            .method = HttpRequestMethod(Http::METHOD_GET),
+            .uri = "/",
+            .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);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(true, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-2, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(2, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(6, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(13, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1)));
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_);
-        input.reset();
-    }
-
-    // RELAXED alternative EOL sequence: multi-CR-NL
-    {
-        input.append("GET / HTTP/1.1\r\r\r\n", 18);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
         Config.onoff.relaxed_header_parser = 1;
-        // Being tolerant we can ignore and elide these apparently benign CR
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
-        CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.1\r\r\r\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(2, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(6, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(13, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1)));
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_);
-        input.reset();
-        Config.onoff.relaxed_header_parser = 0;
-    }
+        struct resultSet expect = {
+            .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,1,1)
+        };
+        output.clear();
+        testResults(__LINE__, input, output, expect);
 
-    // STRICT alternative EOL sequence: multi-CR-NL
-    {
-        input.append("GET / HTTP/1.1\r\r\r\n", 18);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        // strict mode treats these as several bare-CR in the request line which is explicitly invalid.
         Config.onoff.relaxed_header_parser = 0;
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http::scBadRequest, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_);
-        input.reset();
+        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();
     }
 
     // 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);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http::scBadRequest, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.1 \n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(2, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(13, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/ HTTP/1.1", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_);
-        input.reset();
-    }
-
-    // incomplete line at various positions
-    {
-        input.append("GET", 3);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.parsingStage_);
-        CPPUNIT_ASSERT_EQUAL(Http::scNone, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(), output.method_);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_);
-        input.reset();
-
-        input.append("GET ", 4);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.parsingStage_);
-        CPPUNIT_ASSERT_EQUAL(Http::scNone, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(), output.method_);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_);
-        input.reset();
-
-        input.append("GET / HT", 8);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.parsingStage_);
-        CPPUNIT_ASSERT_EQUAL(Http::scNone, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(), output.method_);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_);
-        input.reset();
-
-        input.append("GET / HTTP/1.1", 14);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.parsingStage_);
-        CPPUNIT_ASSERT_EQUAL(Http::scNone, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(), output.method_);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_);
-        input.reset();
+        // 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,
+            .suffixSz = input.length(),
+            .method = HttpRequestMethod(),
+            .uri = NULL,
+            .version = AnyP::ProtocolVersion()
+        };
+        output.clear();
+        testResults(__LINE__, input, output, expect);
+        input.clear();
     }
 }
 
@@ -695,207 +629,271 @@ testHttp1Parser::testParseRequestLineMethods()
     // ensure MemPools etc exist
     globalSetup();
 
-    MemBuf input;
+    SBuf input;
     Http1::RequestParser output;
-    input.init();
 
-    // 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);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
-        CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp(". / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp(".", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(".", NULL), output.method_);
-        CPPUNIT_ASSERT_EQUAL(2, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(2, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(4, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(11, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1)));
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_);
-        input.reset();
+        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 = "/",
+            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
+        };
+        output.clear();
+        testResults(__LINE__, input, output, expect);
+        input.clear();
     }
 
     // OPTIONS with * URL
     {
-        input.append("OPTIONS * HTTP/1.1\n", 19);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
-        CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("OPTIONS * HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(6, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("OPTIONS", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_OPTIONS), output.method_);
-        CPPUNIT_ASSERT_EQUAL(8, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(8, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("*", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(10, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(17, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1)));
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_);
-        input.reset();
+        input.append("OPTIONS * HTTP/1.1\r\n", 20);
+        struct resultSet expect = {
+            .parsed = false,
+            .needsMore = true,
+            .parserState = Http1::HTTP_PARSE_MIME,
+            .status = Http::scOkay,
+            .suffixSz = 0,
+            .method = HttpRequestMethod(Http::METHOD_OPTIONS),
+            .uri = "*",
+            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
+        };
+        output.clear();
+        testResults(__LINE__, input, output, expect);
+        input.clear();
     }
 
     // unknown method
     {
-        input.append("HELLOWORLD / HTTP/1.1\n", 22);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
-        CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HELLOWORLD / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(9, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HELLOWORLD", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod("HELLOWORLD",NULL), output.method_);
-        CPPUNIT_ASSERT_EQUAL(11, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(11, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(13, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(20, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1)));
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_);
-        input.reset();
+        input.append("HELLOWORLD / HTTP/1.1\r\n", 23);
+        struct resultSet expect = {
+            .parsed = false,
+            .needsMore = true,
+            .parserState = Http1::HTTP_PARSE_MIME,
+            .status = Http::scOkay,
+            .suffixSz = 0,
+            .method = HttpRequestMethod(SBuf("HELLOWORLD")),
+            .uri = "/",
+            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
+        };
+        output.clear();
+        testResults(__LINE__, input, output, expect);
+        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);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http::scBadRequest, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("A\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(), output.method_);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_);
-        input.reset();
+        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();
     }
 
-    input.append("GET\n", 4);
     {
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http::scBadRequest, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(), output.method_);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_);
-        input.reset();
+        input.append("GET\n", 4);
+        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();
+    }
+
+    // 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();
     }
 
-    // RELAXED space padded method (in strict mode SP is reserved so invalid as a method byte)
+    // RFC 7230 defined tolerance: ignore empty line(s) prefix on messages
     {
-        input.append(" GET / HTTP/1.1\n", 16);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
+        input.append("\r\n\r\n\nGET / HTTP/1.1\r\n", 21);
         Config.onoff.relaxed_header_parser = 1;
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
-        CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(1, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(1, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(3, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_);
-        CPPUNIT_ASSERT_EQUAL(5, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(5, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(7, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(14, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1)));
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_);
-        input.reset();
+        struct resultSet expect = {
+            .parsed = false,
+            .needsMore = true,
+            .parserState = Http1::HTTP_PARSE_MIME,
+            .status = Http::scOkay,
+            .suffixSz = 0,
+            .method = HttpRequestMethod(Http::METHOD_GET),
+            .uri = "/",
+            .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();
+    }
+
+    // forbidden character in method
+    {
+        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();
     }
 
-    // STRICT space padded method (in strict mode SP is reserved so invalid as a method byte)
+    // CR in method delimiters
     {
-        input.append(" GET / HTTP/1.1\n", 16);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
+        // 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,
+            .suffixSz = 0,
+            .method = HttpRequestMethod(Http::METHOD_GET),
+            .uri = "/",
+            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
+        };
+        output.clear();
+        testResults(__LINE__, input, output, expect);
+
         Config.onoff.relaxed_header_parser = 0;
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http::scBadRequest, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp(" GET / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(), output.method_);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_NONE,0,0), output.msgProtocol_);
-        input.reset();
+        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();
     }
 
-    // tab padded method (NP: tab is not SP so treated as any other binary)
-    // XXX: binary codes are non-compliant
+    // tolerant parser delimiters
     {
-        input.append("\tGET / HTTP/1.1\n", 16);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
-        CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("\tGET / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(3, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("\tGET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(&output.buf[output.req.m_start],&output.buf[output.req.m_end+1]), output.method_);
-        CPPUNIT_ASSERT_EQUAL(5, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(5, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(7, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(14, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1)));
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_);
-        input.reset();
+        // 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,
+            .suffixSz = 0,
+            .method = HttpRequestMethod(Http::METHOD_GET),
+            .uri = "/",
+            .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();
     }
 }
 
@@ -905,277 +903,187 @@ testHttp1Parser::testParseRequestLineInvalid()
     // ensure MemPools etc exist
     globalSetup();
 
-    MemBuf input;
+    SBuf input;
     Http1::RequestParser output;
-    input.init();
 
-    // no method (but in a form which is ambiguous with HTTP/0.9 simple-request)
+    // no method (or method delimiter)
     {
-        // XXX: Bug: HTTP/0.9 requires method to be "GET"
+        // HTTP/0.9 requires method to be "GET"
         input.append("/ HTTP/1.0\n", 11);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(true, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/ HTTP/1.0\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod("/",NULL), output.method_);
-        CPPUNIT_ASSERT_EQUAL(2, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(9, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.0", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9), output.msgProtocol_);
-        input.reset();
-    }
-
-    // RELAXED no method (an invalid format)
-    {
-        input.append(" / HTTP/1.0\n", 12);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        // BUG: When tolerantly ignoring SP prefix this case becomes ambiguous with HTTP/0.9 simple-request)
-        Config.onoff.relaxed_header_parser = 1;
-        CPPUNIT_ASSERT_EQUAL(true, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-//        CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_DONE, output.parsingStage_);
-        CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(1, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/ HTTP/1.0\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(1, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(1, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod("/",NULL), output.method_);
-        CPPUNIT_ASSERT_EQUAL(3, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(10, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.0", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9), output.msgProtocol_);
-        input.reset();
-        Config.onoff.relaxed_header_parser = 0;
+        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();
     }
 
-    // STRICT no method (an invalid format)
+    // no method (with method delimiter)
     {
         input.append(" / HTTP/1.0\n", 12);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        // When tolerantly ignoring SP prefix this case becomes ambiguous with HTTP/0.9 simple-request)
-        Config.onoff.relaxed_header_parser = 0;
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http::scBadRequest, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp(" / HTTP/1.0\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(), output.method_);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_NONE,0,0), output.msgProtocol_);
-        input.reset();
+        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();
     }
 
-    // binary code in method (strange but ...)
+    // binary code after method (invalid)
     {
-        input.append("GET\x0B / HTTP/1.1\n", 16);
-        //printf("TEST: %d-%d/%d '%.*s'\n", output.req.start, output.req.end, input.contentSize(), 16, input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
-        CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET\x0B / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(3, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET\x0B", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-//        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod("GET\0x0B",NULL), output.method_);
-        CPPUNIT_ASSERT_EQUAL(5, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(5, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(7, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(14, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1)));
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_);
-        input.reset();
+        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,
+            .suffixSz = input.length(),
+            .method = HttpRequestMethod(),
+            .uri = NULL,
+            .version = AnyP::ProtocolVersion()
+        };
+        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);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http::scBadRequest, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(), output.method_);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_);
-        input.reset();
+        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,
+            .suffixSz = input.length(),
+            .method = HttpRequestMethod(),
+            .uri = NULL,
+            .version = AnyP::ProtocolVersion()
+        };
+        output.clear();
+        testResults(__LINE__, input, output, expect);
+        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);
-        //printf("TEST: %d-%d/%d '%.*s'\n", output.req.start, output.req.end, input.contentSize(), 16, input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
-        CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET\0 / HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(3, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET\0", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-//        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod("GET\0",NULL), output.method_);
-        CPPUNIT_ASSERT_EQUAL(5, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(5, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("/", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(7, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(14, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.v_start],(output.req.v_end-output.req.v_start+1)));
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1), output.msgProtocol_);
-        input.reset();
-    }
+        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,
+            .suffixSz = input.length(),
+            .method = HttpRequestMethod(),
+            .uri = NULL,
+            .version = AnyP::ProtocolVersion()
+        };
+        output.clear();
+        testResults(__LINE__, input, output, expect);
 
-    // no URL (grammer otherwise correct)
-    {
-        input.append("GET  HTTP/1.1\n", 14);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(true, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET  HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(2, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_);
-        CPPUNIT_ASSERT_EQUAL(5, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(12, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9), output.msgProtocol_);
-        input.reset();
+        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();
     }
 
-    // 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);
-        //printf("TEST: '%s'\n",input.content());
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(true, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET HTTP/1.1\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(2, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("GET", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_GET), output.method_);
-        CPPUNIT_ASSERT_EQUAL(4, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(11, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("HTTP/1.1", &output.buf[output.req.u_start],(output.req.u_end-output.req.u_start+1)));
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9), output.msgProtocol_);
-        input.reset();
+        input.append("GET HTTP/1.1\r\n", 14);
+        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();
     }
 
     // binary line
     {
         input.append("\xB\xC\xE\xF\n", 5);
-        //printf("TEST: binary-line\n");
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http::scBadRequest, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("\xB\xC\xE\xF\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(), output.method_);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_);
-        input.reset();
+        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();
     }
 
     // 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);
-        //printf("TEST: mixed whitespace\n");
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http::scBadRequest, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("\t \t \t\n", &output.buf[output.req.start],(output.req.end-output.req.start+1)));
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(0, memcmp("\t", &output.buf[output.req.m_start],(output.req.m_end-output.req.m_start+1)));
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(&output.buf[output.req.m_start],&output.buf[output.req.m_end+1]), output.method_);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_);
-        input.reset();
+        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();
     }
 
-    // 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);
-        //printf("TEST: mixed whitespace with CR\n");
-        output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(false, output.parse());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-        CPPUNIT_ASSERT_EQUAL(Http::scBadRequest, output.request_parse_status);
-        CPPUNIT_ASSERT_EQUAL(0, output.req.start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.end);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.m_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end);
-        CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(), output.method_);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start);
-        CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end);
-        CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_);
-        input.reset();
+        input.append("\r  \t \n", 6);
+        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();
     }
 }
 
@@ -1186,60 +1094,93 @@ testHttp1Parser::testDripFeed()
     // extend the size of the buffer from 0 bytes to full request length
     // calling the parser repeatedly as visible data grows.
 
-    MemBuf mb;
-    mb.init(1024, 1024);
-    mb.append("            ", 12);
-    int garbageEnd = mb.contentSize();
-    mb.append("GET http://example.com/ HTTP/1.1\r\n", 34);
-    int reqLineEnd = mb.contentSize();
-    mb.append("Host: example.com\r\n\r\n", 21);
-    int mimeEnd = mb.contentSize();
-    mb.append("...", 3); // trailer to catch mime EOS errors.
-
-    Http1::RequestParser hp(mb.content(), 0);
-
-    // only relaxed parser accepts the garbage whitespace
-    Config.onoff.relaxed_header_parser = 1;
-
-    for (; hp.bufsiz <= mb.contentSize(); ++hp.bufsiz) {
-        bool parseResult = hp.parse();
-
-#if WHEN_TEST_DEBUG_IS_NEEDED
-        printf("%d/%d :: %d, %d, %d '%c'\n", hp.bufsiz, mb.contentSize(),
-               garbageEnd, reqLineEnd, parseResult,
-               mb.content()[hp.bufsiz]);
-#endif
-
-       // before end of garbage found its a moving offset.
-       if (hp.bufsiz <= garbageEnd) {
-            CPPUNIT_ASSERT_EQUAL(hp.bufsiz, (int)hp.parseOffset_);
-            CPPUNIT_ASSERT_EQUAL(false, hp.isDone());
-            CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_NEW, hp.parsingStage_);
-            continue;
+    SBuf data;
+    data.append("\n\n\n\n\n\n\n\n\n\n\n\n", 12);
+    SBuf::size_type garbageEnd = data.length();
+    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;
+    Http1::RequestParser hp;
+
+    // start with strict and move on to relaxed
+    Config.onoff.relaxed_header_parser = 2;
+
+    Config.maxRequestHeaderSize = 1024; // large enough to hold the test data.
+
+    do {
+
+        // 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()
+        };
+
+        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;
         }
 
-       // before request line found, parse announces incomplete
-        if (hp.bufsiz < reqLineEnd) {
-            CPPUNIT_ASSERT_EQUAL(garbageEnd, (int)hp.parseOffset_);
-            CPPUNIT_ASSERT_EQUAL(false, parseResult);
-            CPPUNIT_ASSERT_EQUAL(false, hp.isDone());
-            CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, hp.parsingStage_);
-            continue;
-        }
+    } while (Config.onoff.relaxed_header_parser);
 
-       // before request headers entirely found, parse announces incomplete
-        if (hp.bufsiz < mimeEnd) {
-            CPPUNIT_ASSERT_EQUAL(reqLineEnd, (int)hp.parseOffset_);
-            CPPUNIT_ASSERT_EQUAL(false, parseResult);
-            CPPUNIT_ASSERT_EQUAL(false, hp.isDone());
-            // TODO: add all the other usual tests for request-line details
-            CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, hp.parsingStage_);
-            continue;
-        }
-
-        // once request line is found (AND the following \n) current parser announces success
-        CPPUNIT_ASSERT_EQUAL(mimeEnd, (int)hp.parseOffset_);
-        CPPUNIT_ASSERT_EQUAL(true, parseResult);
-        CPPUNIT_ASSERT_EQUAL(true, hp.isDone());
-    }
 }
+