]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Shuffle request headersEnd call into Http1Parser::parseRequest actions
authorAmos Jeffries <squid3@treenet.co.nz>
Mon, 30 Dec 2013 18:18:03 +0000 (10:18 -0800)
committerAmos Jeffries <squid3@treenet.co.nz>
Mon, 30 Dec 2013 18:18:03 +0000 (10:18 -0800)
Http1Parser will now respond with signals for 'incomplete parse' until
the entire first line and any mime headers are present. The size and
content of mime headers are guaranteed once the parser responds with a
true result.

HTTP/0.9, HTTP/1.* and future versions using "HTTP/" are all accounted for
and handled in accordance with HTTP RFC 2616 requirements (adjusted for
later HTTPbis WG clarifications).

TODO: Parsing of the mime header fields.

src/Makefile.am
src/client_side.cc
src/http/Http1Parser.cc
src/http/Http1Parser.h
src/tests/testHttp1Parser.cc

index dca9bd5a1b168717ada67f2549418749432769be..f0544bb742f72ac04e3e7e6b566b9c1eb022fcc3 100644 (file)
@@ -2519,6 +2519,8 @@ tests_testHttp1Parser_SOURCES = \
        MemBuf.h \
        Mem.h \
        tests/stub_mem.cc \
+       mime_header.cc \
+       mime_header.h \
        String.cc \
        cache_cf.h \
        YesNoNone.h \
index a0052656a3907fb59de5a79c5f61794c35d7cfb1..9be78b100d80b1b7a7b3730dd6720dc0283887bd 100644 (file)
@@ -2214,8 +2214,6 @@ prepareTransparentURL(ConnStateData * conn, ClientHttpRequest *http, char *url,
 static ClientSocketContext *
 parseHttpRequest(ConnStateData *csd, const Http::Http1ParserPointer &hp)
 {
-    char *req_hdr = NULL;
-    char *end;
     size_t req_sz;
     ClientHttpRequest *http;
     ClientSocketContext *result;
@@ -2245,31 +2243,14 @@ parseHttpRequest(ConnStateData *csd, const Http::Http1ParserPointer &hp)
             return parseHttpRequestAbort(csd, "error:invalid-request");
     }
 
-    /* Request line is valid here .. */
-
-    /* This call scans the entire request, not just the headers */
-    if (hp->messageProtocol().major > 0) {
-        if ((req_sz = headersEnd(hp->buf, hp->bufsiz)) == 0) {
-            debugs(33, 5, "Incomplete request, waiting for end of headers");
-            return NULL;
-        }
-    } else {
-        debugs(33, 3, "parseHttpRequest: Missing HTTP identifier");
-        req_sz = hp->firstLineSize();
-    }
-
     /* We know the whole request is in hp->buf now */
-
+    req_sz = hp->messageHeaderSize();
     assert(req_sz <= (size_t) hp->bufsiz);
 
     /* Will the following be true with HTTP/0.9 requests? probably not .. */
     /* So the rest of the code will need to deal with '0'-byte headers (ie, none, so don't try parsing em) */
     assert(req_sz > 0);
 
-    hp->hdr_end = req_sz - 1;
-
-    hp->hdr_start = hp->req.end + 1;
-
     /* Enforce max_request_size */
     if (req_sz >= Config.maxRequestHeaderSize) {
         debugs(33, 5, "parseHttpRequest: Too large request");
@@ -2298,17 +2279,13 @@ parseHttpRequest(ConnStateData *csd, const Http::Http1ParserPointer &hp)
      * TODO: Use httpRequestParse here.
      */
     /* XXX this code should be modified to take a const char * later! */
-    req_hdr = (char *) hp->buf + hp->req.end + 1;
-
-    debugs(33, 3, "parseHttpRequest: req_hdr = {" << req_hdr << "}");
-
-    end = (char *) hp->buf + hp->hdr_end;
+    const char *req_hdr = hp->rawHeaderBuf();
 
-    debugs(33, 3, "parseHttpRequest: end = {" << end << "}");
+    debugs(33, 3, Raw("req_hdr", req_hdr, hp->headerBlockSize()));
 
-    debugs(33, 3, "parseHttpRequest: prefix_sz = " <<
-           hp->messageHeaderSize() << ", request-line-size=" <<
-           hp->firstLineSize());
+    debugs(33, 3, "prefix_sz = " << hp->messageHeaderSize() <<
+           ", request-line-size=" << hp->firstLineSize() <<
+           ", mime-header-size=" << hp->headerBlockSize());
 
     /* Ok, all headers are received */
     http = new ClientHttpRequest(csd);
index 8b535c0212008d72f7e8d05349af2927848b4d9a..db289f113092cff8dd42f6f55fc23b0b10f3e7ea 100644 (file)
@@ -2,6 +2,7 @@
 #include "Debug.h"
 #include "http/Http1Parser.h"
 #include "http/RequestMethod.h"
+#include "mime_header.h"
 #include "profiler/Profiler.h"
 #include "SquidConfig.h"
 
@@ -20,6 +21,7 @@ Http::Http1Parser::clear()
     req.v_start = req.v_end = -1;
     msgProtocol_ = AnyP::ProtocolVersion();
     method_ = NULL;
+    mimeHeaderBytes_ = 0;
 }
 
 void
@@ -44,8 +46,6 @@ Http::Http1Parser::parseRequestFirstLine()
 
     // Single-pass parse: (provided we have the whole line anyways)
 
-    assert(completedState_ == HTTP_PARSE_NEW);
-
     req.start = parseOffset_; // avoid re-parsing any portion we managed to complete
     if (Config.onoff.relaxed_header_parser) {
         if (Config.onoff.relaxed_header_parser < 0 && buf[req.start] == ' ')
@@ -155,6 +155,7 @@ Http::Http1Parser::parseRequestFirstLine()
         msgProtocol_ = Http::ProtocolVersion(0,9);
         req.u_end = line_end;
         request_parse_status = Http::scOkay; // HTTP/0.9
+        completedState_ = HTTP_PARSE_FIRST;
         parseOffset_ = line_end;
         return 1;
     } else {
@@ -254,16 +255,51 @@ Http::Http1Parser::parseRequestFirstLine()
 bool
 Http::Http1Parser::parseRequest()
 {
-    PROF_start(HttpParserParseReqLine);
-    int retcode = parseRequestFirstLine();
-    debugs(74, 5, "Parser: retval " << retcode << ": from " << req.start <<
-           "->" << req.end << ": method " << req.m_start << "->" <<
-           req.m_end << "; url " << req.u_start << "->" << req.u_end <<
-           "; proto-version " << req.v_start << "->" << req.v_end << " (" << msgProtocol_ << ")");
-    PROF_stop(HttpParserParseReqLine);
-
-    if (retcode != 0)
+    // stage 1: locate the request-line
+    // stage 2: parse the request-line
+    if (completedState_ == HTTP_PARSE_NEW) {
+        PROF_start(HttpParserParseReqLine);
+        int retcode = parseRequestFirstLine();
+        debugs(74, 5, "request-line: retval " << retcode << ": from " << req.start << "->" << req.end << " " << Raw("line", &buf[req.start], req.end-req.start));
+        debugs(74, 5, "request-line: method " << req.m_start << "->" << req.m_end << " (" << *method_ << ")");
+        debugs(74, 5, "request-line: url " << req.u_start << "->" << req.u_end << " " << Raw("field", &buf[req.u_start], req.u_end-req.u_start));
+        debugs(74, 5, "request-line: proto " << req.v_start << "->" << req.v_end << " (" << msgProtocol_ << ")");
+        debugs(74, 5, "Parser: parse-offset=" << parseOffset_);
+        PROF_stop(HttpParserParseReqLine);
+        if (retcode < 0) {
+            completedState_ = HTTP_PARSE_DONE;
+            return false;
+        }
+    }
+
+    // stage 3: locate the mime header block
+    if (completedState_ == HTTP_PARSE_FIRST) {
+
+        // NP: set these to same value representing 0-byte headers.
+        hdr_start = req.end + 1;
+        hdr_end = hdr_start;
+
+        // HTTP/1.x request-line is valid and parsing completed.
+        if (msgProtocol_.major == 1) {
+            /* NOTE: HTTP/0.9 requests do not have a mime header block.
+             *       So the rest of the code will need to deal with '0'-byte headers
+             *       (ie, none, so don't try parsing em)
+             */
+            if ((mimeHeaderBytes_ = headersEnd(buf+parseOffset_, bufsiz-parseOffset_)) == 0) {
+                debugs(33, 5, "Incomplete request, waiting for end of headers");
+                return false;
+            }
+
+            hdr_start = req.end + 1;
+            hdr_end = parseOffset_ + mimeHeaderBytes_ - 1;
+
+        } else
+            debugs(33, 3, "Missing HTTP/1.x identifier");
+
+        // NP: planned name for this stage is HTTP_PARSE_MIME
+        // but we do not do any further stages here yet so go straight to DONE
         completedState_ = HTTP_PARSE_DONE;
+    }
 
-    return (retcode > 0);
+    return isDone();
 }
index 0be9b09b33f5906314ca005ee1e235672b3c3cdb..c50685e159a44fb7c3c1be20c61ffb3418e8d394 100644 (file)
@@ -12,6 +12,7 @@ namespace Http {
 #define HTTP_PARSE_NONE   0 // nothing. completely unset state.
 #define HTTP_PARSE_NEW    1 // initialized, but nothing usefully parsed yet.
 #define HTTP_PARSE_FIRST  2 // have parsed request first line
+#define HTTP_PARSE_MIME   3 // have located end of mime header block
 #define HTTP_PARSE_DONE   99 // have done with parsing so far
 
 /** HTTP protocol parser.
@@ -54,16 +55,17 @@ public:
     /// including CRLF terminator
     int64_t firstLineSize() const {return req.end - req.start + 1;}
 
-    /// size in bytes of the message headers including CRLF terminator
+    /// size in bytes of the message headers including CRLF terminator(s)
     /// but excluding request-line bytes
-    int64_t headerBlockSize() const {return hdr_end - hdr_start + 1;}
+    int64_t headerBlockSize() const {return mimeHeaderBytes_;}
 
     /// size in bytes of HTTP message block, includes request-line and mime headers
     /// excludes any body/entity/payload bytes
-    int64_t messageHeaderSize() const {return hdr_end - req.start + 1;}
+    /// excludes any garbage prefix before the request-line
+    int64_t messageHeaderSize() const {return firstLineSize() + headerBlockSize();}
 
     /// buffer containing HTTP mime headers
-    // convert to SBuf
+    // TODO: convert to SBuf
     const char *rawHeaderBuf() {return buf + hdr_start;}
 
     /** Attempt to parse a request.
@@ -109,6 +111,7 @@ public:
     const HttpRequestMethodPointer & method() const {return method_;}
 
     // Offsets for pieces of the MiME Header segment
+    // \deprecated use rawHeaderBuf() and headerBlockSize() instead
     int hdr_start, hdr_end;
 
     // TODO: Offsets for pieces of the (HTTP reply) Status-Line as per RFC 2616
@@ -130,6 +133,9 @@ private:
 
     /// what request method has been found on the first line
     HttpRequestMethodPointer method_;
+
+    /// number of bytes in the mime header block
+    int64_t mimeHeaderBytes_;
 };
 
 } // namespace Http
index 50281f4caebcb122c022d69a9c462385f655e3b1..a49dbf45cacafb47bbaa585468d8cc07d31a6705 100644 (file)
@@ -92,8 +92,9 @@ testHttp1Parser::testParseRequestLineProtocols()
     {
         input.append("GET / HTTP/1.0\r\n", 16);
         output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(true, output.parseRequest());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
+        CPPUNIT_ASSERT_EQUAL(false, output.parseRequest());
+        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
+        CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_);
         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);
@@ -116,8 +117,9 @@ testHttp1Parser::testParseRequestLineProtocols()
     {
         input.append("GET / HTTP/1.1\r\n", 16);
         output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(true, output.parseRequest());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
+        CPPUNIT_ASSERT_EQUAL(false, output.parseRequest());
+        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
+        CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_);
         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);
@@ -137,11 +139,13 @@ testHttp1Parser::testParseRequestLineProtocols()
     }
 
     // RFC 2616 : future version full-request
-    {    input.append("GET / HTTP/1.2\r\n", 16);
+    {
+        input.append("GET / HTTP/1.2\r\n", 16);
         //printf("TEST: '%s'\n",input.content());
         output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(true, output.parseRequest());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
+        CPPUNIT_ASSERT_EQUAL(false, output.parseRequest());
+        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
+        CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_);
         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);
@@ -162,11 +166,12 @@ testHttp1Parser::testParseRequestLineProtocols()
 
     // RFC 2616 : future version full-request
     {
-        // XXX: IETF HTTPbis WG has made this two-digits format invalid.
+        // 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(true, output.parseRequest());
+        CPPUNIT_ASSERT_EQUAL(true, output.parseRequest()); // BUG: declares true
         CPPUNIT_ASSERT_EQUAL(true, output.isDone());
         CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
         CPPUNIT_ASSERT_EQUAL(0, output.req.start);
@@ -387,8 +392,9 @@ testHttp1Parser::testParseRequestLineStrange()
         input.append("GET  /     HTTP/1.1\r\n", 21);
         //printf("TEST: '%s'\n",input.content());
         output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(true, output.parseRequest());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
+        CPPUNIT_ASSERT_EQUAL(false, output.parseRequest());
+        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
+        CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_);
         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);
@@ -412,8 +418,9 @@ testHttp1Parser::testParseRequestLineStrange()
         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(true, output.parseRequest());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
+        CPPUNIT_ASSERT_EQUAL(false, output.parseRequest());
+        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
+        CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_);
         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);
@@ -437,8 +444,9 @@ testHttp1Parser::testParseRequestLineStrange()
         input.append("GET /     HTTP/1.1\nboo!", 23);
         //printf("TEST: '%s'\n",input.content());
         output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(true, output.parseRequest());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
+        CPPUNIT_ASSERT_EQUAL(false, output.parseRequest());
+        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
+        CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_);
         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);
@@ -473,8 +481,9 @@ testHttp1Parser::testParseRequestLineTerminators()
         input.append("GET / HTTP/1.1\n", 15);
         //printf("TEST: '%s'\n",input.content());
         output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(true, output.parseRequest());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
+        CPPUNIT_ASSERT_EQUAL(false, output.parseRequest());
+        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
+        CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_);
         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);
@@ -525,8 +534,9 @@ testHttp1Parser::testParseRequestLineTerminators()
         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(true, output.parseRequest());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
+        CPPUNIT_ASSERT_EQUAL(false, output.parseRequest());
+        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
+        CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_);
         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);
@@ -543,6 +553,7 @@ testHttp1Parser::testParseRequestLineTerminators()
         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;
     }
 
     // STRICT alternative EOL sequence: multi-CR-NL
@@ -684,8 +695,9 @@ testHttp1Parser::testParseRequestLineMethods()
         input.append(". / HTTP/1.1\n", 13);
         //printf("TEST: '%s'\n",input.content());
         output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(true, output.parseRequest());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
+        CPPUNIT_ASSERT_EQUAL(false, output.parseRequest());
+        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
+        CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_);
         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);
@@ -709,8 +721,9 @@ testHttp1Parser::testParseRequestLineMethods()
         input.append("OPTIONS * HTTP/1.1\n", 19);
         //printf("TEST: '%s'\n",input.content());
         output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(true, output.parseRequest());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
+        CPPUNIT_ASSERT_EQUAL(false, output.parseRequest());
+        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
+        CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_);
         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);
@@ -734,8 +747,9 @@ testHttp1Parser::testParseRequestLineMethods()
         input.append("HELLOWORLD / HTTP/1.1\n", 22);
         //printf("TEST: '%s'\n",input.content());
         output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(true, output.parseRequest());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
+        CPPUNIT_ASSERT_EQUAL(false, output.parseRequest());
+        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
+        CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_);
         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);
@@ -803,8 +817,9 @@ testHttp1Parser::testParseRequestLineMethods()
         //printf("TEST: '%s'\n",input.content());
         output.reset(input.content(), input.contentSize());
         Config.onoff.relaxed_header_parser = 1;
-        CPPUNIT_ASSERT_EQUAL(true, output.parseRequest());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
+        CPPUNIT_ASSERT_EQUAL(false, output.parseRequest());
+        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
+        CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_);
         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);
@@ -821,6 +836,7 @@ testHttp1Parser::testParseRequestLineMethods()
         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;
     }
 
     // STRICT space padded method (in strict mode SP is reserved so invalid as a method byte)
@@ -844,7 +860,6 @@ testHttp1Parser::testParseRequestLineMethods()
         CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end);
         CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(AnyP::PROTO_NONE,0,0), output.msgProtocol_);
         input.reset();
-        Config.onoff.relaxed_header_parser = 1;
     }
 
     // tab padded method (NP: tab is not SP so treated as any other binary)
@@ -852,8 +867,9 @@ testHttp1Parser::testParseRequestLineMethods()
         input.append("\tGET / HTTP/1.1\n", 16);
         //printf("TEST: '%s'\n",input.content());
         output.reset(input.content(), input.contentSize());
-        CPPUNIT_ASSERT_EQUAL(true, output.parseRequest());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
+        CPPUNIT_ASSERT_EQUAL(false, output.parseRequest());
+        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
+        CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_);
         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);
@@ -913,10 +929,11 @@ testHttp1Parser::testParseRequestLineInvalid()
         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)
+        // 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.parseRequest());
         CPPUNIT_ASSERT_EQUAL(true, output.isDone());
+//        CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_NEW, output.completedState_);
         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);
@@ -932,6 +949,7 @@ testHttp1Parser::testParseRequestLineInvalid()
         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;
     }
 
     // STRICT no method (an invalid format)
@@ -963,8 +981,9 @@ testHttp1Parser::testParseRequestLineInvalid()
         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(true, output.parseRequest());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
+        CPPUNIT_ASSERT_EQUAL(false, output.parseRequest());
+        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
+        CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_);
         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);
@@ -1010,8 +1029,9 @@ testHttp1Parser::testParseRequestLineInvalid()
         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(true, output.parseRequest());
-        CPPUNIT_ASSERT_EQUAL(true, output.isDone());
+        CPPUNIT_ASSERT_EQUAL(false, output.parseRequest());
+        CPPUNIT_ASSERT_EQUAL(false, output.isDone());
+        CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_FIRST, output.completedState_);
         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);
@@ -1162,14 +1182,14 @@ testHttp1Parser::testDripFeed()
     int garbageEnd = mb.contentSize();
     mb.append("GET http://example.com/ HTTP/1.1\r\n", 34);
     int reqLineEnd = mb.contentSize();
-    mb.append("\n", 1);
+    mb.append("Host: example.com\r\n\r\n.", 22);
 
     Http::Http1Parser hp(mb.content(), 0);
 
     // only relaxed parser accepts the garbage whitespace
     Config.onoff.relaxed_header_parser = 1;
 
-    for (; hp.bufsiz < mb.contentSize(); ++hp.bufsiz) {
+    for (; hp.bufsiz <= mb.contentSize(); ++hp.bufsiz) {
         bool parseResult = hp.parseRequest();
 
 #if WHEN_TEST_DEBUG_IS_NEEDED
@@ -1190,6 +1210,17 @@ testHttp1Parser::testDripFeed()
             CPPUNIT_ASSERT_EQUAL(garbageEnd, (int)hp.parseOffset_);
             CPPUNIT_ASSERT_EQUAL(false, parseResult);
             CPPUNIT_ASSERT_EQUAL(false, hp.isDone());
+            CPPUNIT_ASSERT_EQUAL((uint8_t)HTTP_PARSE_NEW, hp.completedState_);
+            continue;
+        }
+
+       // before request headers entirely found, parse announces incomplete
+        if (hp.bufsiz < mb.contentSize()-1) {
+            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((uint8_t)HTTP_PARSE_FIRST, hp.completedState_);
             continue;
         }