]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Merge from trunk rev.13939
authorAmos Jeffries <squid3@treenet.co.nz>
Fri, 20 Feb 2015 03:25:12 +0000 (19:25 -0800)
committerAmos Jeffries <squid3@treenet.co.nz>
Fri, 20 Feb 2015 03:25:12 +0000 (19:25 -0800)
1  2 
src/client_side.cc
src/http.cc
src/http/one/RequestParser.cc
src/http/one/RequestParser.h
src/parser/Tokenizer.cc
src/parser/Tokenizer.h
src/tests/testHttp1Parser.cc

Simple merge
diff --cc src/http.cc
Simple merge
index c47ef3741f62b5f4501e9d99698ed87131951d0f,49c395d6650ec0bbec1fdc105a05e62edfe2cfa8..8577b7c27bda935066c0ce7ea8ebfb6df9e14a4e
  #include "Debug.h"
  #include "http/one/RequestParser.h"
  #include "http/ProtocolVersion.h"
 -#include "mime_header.h"
+ #include "parser/Tokenizer.h"
  #include "profiler/Profiler.h"
  #include "SquidConfig.h"
  
  Http::One::RequestParser::RequestParser() :
-     Parser()
+     Parser(),
 -    request_parse_status(Http::scNone),
+     firstLineGarbage_(0)
+ {}
+ Http1::Parser::size_type
+ Http::One::RequestParser::firstLineSize() const
  {
-     req.start = req.end = -1;
-     req.m_start = req.m_end = -1;
-     req.u_start = req.u_end = -1;
-     req.v_start = req.v_end = -1;
+     // RFC 7230 section 2.6
+     /* method SP request-target SP "HTTP/" DIGIT "." DIGIT CRLF */
+     return method_.image().length() + uri_.length() + 12;
  }
  
  /**
@@@ -75,214 -63,302 +61,302 @@@ Http::One::RequestParser::skipGarbageLi
   *
   * Governed by:
   *  RFC 1945 section 5.1
-  *  RFC 7230 section 3.1 and 3.5
+  *  RFC 7230 section 2.6, 3.1 and 3.5
   *
-  * Parsing state is stored between calls. However the current implementation
-  * begins parsing from scratch on every call.
-  * The return value tells you whether the parsing state fields are valid or not.
+  * Parsing state is stored between calls. The current implementation uses
+  * checkpoints after each successful request-line field.
+  * The return value tells you whether the parsing is completed or not.
   *
 - * \retval -1  an error occurred. request_parse_status indicates HTTP status result.
 + * \retval -1  an error occurred. parseStatusCode indicates HTTP status result.
-  * \retval  1  successful parse. member fields contain the request-line items
+  * \retval  1  successful parse. method_ is filled and buffer consumed including first delimiter.
   * \retval  0  more data is needed to complete the parse
   */
  int
- Http::One::RequestParser::parseRequestFirstLine()
+ Http::One::RequestParser::parseMethodField(::Parser::Tokenizer &tok, const CharacterSet &WspDelim)
  {
-     int second_word = -1; // track the suspected URI start
-     int first_whitespace = -1, last_whitespace = -1; // track the first and last SP byte
-     int line_end = -1; // tracks the last byte BEFORE terminal \r\n or \n sequence
+     // scan for up to 16 valid method characters.
+     static const size_t maxMethodLength = 16; // TODO: make this configurable?
  
-     debugs(74, 5, "parsing possible request: buf.length=" << buf_.length());
-     debugs(74, DBG_DATA, buf_);
+     // method field is a sequence of TCHAR.
+     SBuf methodFound;
+     if (tok.prefix(methodFound, CharacterSet::TCHAR, maxMethodLength) && tok.skipOne(WspDelim)) {
  
-     // Single-pass parse: (provided we have the whole line anyways)
+         method_ = HttpRequestMethod(methodFound);
+         buf_ = tok.remaining(); // incremental parse checkpoint
+         return 1;
  
-     req.start = 0;
-     req.end = -1;
-     for (SBuf::size_type i = 0; i < buf_.length(); ++i) {
-         // track first and last whitespace (SP only)
-         if (buf_[i] == ' ') {
-             last_whitespace = i;
-             if (first_whitespace < req.start)
-                 first_whitespace = i;
-         }
+     } else if (tok.atEnd()) {
+         debugs(74, 5, "Parser needs more data to find method");
+         return 0;
  
-         // track next non-SP/non-HT byte after first_whitespace
-         if (second_word < first_whitespace && buf_[i] != ' ' && buf_[i] != '\t') {
-             second_word = i;
-         }
+     } // else error(s)
  
-         // locate line terminator
-         if (buf_[i] == '\n') {
-             req.end = i;
-             line_end = i - 1;
-             break;
-         }
-         if (i < buf_.length() - 1 && buf_[i] == '\r') {
-             if (Config.onoff.relaxed_header_parser) {
-                 if (Config.onoff.relaxed_header_parser < 0 && buf_[i + 1] == '\r')
-                     debugs(74, DBG_IMPORTANT, "WARNING: Invalid HTTP Request: " <<
-                            "Series of carriage-return bytes received prior to line terminator. " <<
-                            "Ignored due to relaxed_header_parser.");
-                 // Be tolerant of invalid multiple \r prior to terminal \n
-                 if (buf_[i + 1] == '\n' || buf_[i + 1] == '\r')
-                     line_end = i - 1;
-                 while (i < buf_.length() - 1 && buf_[i + 1] == '\r')
-                     ++i;
-                 if (buf_[i + 1] == '\n') {
-                     req.end = i + 1;
-                     break;
-                 }
-             } else {
-                 if (buf_[i + 1] == '\n') {
-                     req.end = i + 1;
-                     line_end = i - 1;
-                     break;
-                 }
-             }
+     // non-delimiter found after accepted method bytes means ...
+     if (methodFound.length() == maxMethodLength) {
+         // method longer than acceptible.
+         // RFC 7230 section 3.1.1 mandatory (SHOULD) 501 response
 -        request_parse_status = Http::scNotImplemented;
++        parseStatusCode = Http::scNotImplemented;
+         debugs(33, 5, "invalid request-line. method too long");
+     } else {
+         // invalid character in the URL
+         // RFC 7230 section 3.1.1 required (SHOULD) 400 response
 -        request_parse_status = Http::scBadRequest;
++        parseStatusCode = Http::scBadRequest;
+         debugs(33, 5, "invalid request-line. missing method delimiter");
+     }
+     return -1;
+ }
  
-             // RFC 7230 section 3.1.1 does not prohibit embeded CR like RFC 2616 used to.
-             // However it does explicitly state an exact syntax which omits un-encoded CR
-             // and defines 400 (Bad Request) as the required action when
-             // handed an invalid request-line.
-             parseStatusCode = Http::scBadRequest;
-             return -1;
-         }
+ static CharacterSet
+ uriValidCharacters()
+ {
+     CharacterSet UriChars("URI-Chars","");
  
-         // We are expecting printable ascii characters for method/first word
-         if (first_whitespace < 0 && (!xisascii(buf_[i]) || !xisprint(buf_[i]))) {
-             parseStatusCode = Http::scBadRequest;
-             return -1;
-         }
-     }
+     /* RFC 3986 section 2:
+      * "
+      *   A URI is composed from a limited set of characters consisting of
+      *   digits, letters, and a few graphic symbols.
+      * "
+      */
+     // RFC 3986 section 2.1 - percent encoding "%" HEXDIG
+     UriChars.add('%');
+     UriChars += CharacterSet::HEXDIG;
+     // RFC 3986 section 2.2 - reserved characters
+     UriChars += CharacterSet("gen-delims", ":/?#[]@");
+     UriChars += CharacterSet("sub-delims", "!$&'()*+,;=");
+     // RFC 3986 section 2.3 - unreserved characters
+     UriChars += CharacterSet::ALPHA;
+     UriChars += CharacterSet::DIGIT;
+     UriChars += CharacterSet("unreserved", "-._~");
+     return UriChars;
+ }
  
-     if (req.end == -1) {
-         // DoS protection against long first-line
-         if ((size_t)buf_.length() >= Config.maxRequestHeaderSize) {
-             debugs(33, 5, "Too large request-line");
-             // RFC 7230 section 3.1.1 mandatory 414 response if URL longer than acceptible.
-             parseStatusCode = Http::scUriTooLong;
-             return -1;
-         }
+ int
+ Http::One::RequestParser::parseUriField(::Parser::Tokenizer &tok)
+ {
+     // URI field is a sequence of ... what? segments all have different valid charset
+     // go with non-whitespace non-binary characters for now
+     static CharacterSet UriChars = uriValidCharacters();
+     /* Arbitrary 64KB URI upper length limit.
+      *
+      * Not quite as arbitrary as it seems though. Old SquidString objects
+      * cannot store strings larger than 64KB, so we must limit until they
+      * have all been replaced with SBuf.
+      *
+      * Not that it matters but RFC 7230 section 3.1.1 requires (RECOMMENDED)
+      * at least 8000 octets for the whole line, including method and version.
+      */
+     const size_t maxUriLength = min(static_cast<size_t>(Config.maxRequestHeaderSize) - firstLineSize(),
+                                     static_cast<size_t>((64*1024)-1));
  
-         debugs(74, 5, "Parser: retval 0: from " << req.start <<
-                "->" << req.end << ": needs more data to complete first line.");
-         return 0;
-     }
+     SBuf uriFound;
  
-     // NP: we have now seen EOL, more-data (0) cannot occur.
-     //     From here on any failure is -1, success is 1
+     // RFC 7230 HTTP/1.x URI are followed by at least one whitespace delimiter
+     if (tok.prefix(uriFound, UriChars, maxUriLength) && tok.skipOne(CharacterSet::SP)) {
+         uri_ = uriFound;
+         buf_ = tok.remaining(); // incremental parse checkpoint
+         return 1;
  
-     // Input Validation:
+         // RFC 1945 for GET the line terminator may follow URL instead of a delimiter
+     } else if (method_ == Http::METHOD_GET && skipLineTerminator(tok)) {
+         debugs(33, 5, "HTTP/0.9 syntax request-line detected");
+         msgProtocol_ = Http::ProtocolVersion(0,9);
+         uri_ = uriFound; // found by successful prefix() call earlier.
 -        request_parse_status = Http::scOkay;
++        parseStatusCode = Http::scOkay;
+         buf_ = tok.remaining(); // incremental parse checkpoint
+         return 1;
  
-     // DoS protection against long first-line
-     if ((size_t)(req.end-req.start) >= Config.maxRequestHeaderSize) {
-         debugs(33, 5, "Too large request-line");
-         parseStatusCode = Http::scUriTooLong;
-         return -1;
+     } else if (tok.atEnd()) {
+         debugs(74, 5, "Parser needs more data to find URI");
+         return 0;
      }
  
-     // Process what we now know about the line structure into field offsets
-     // generating HTTP status for any aborts as we go.
+     // else errors...
  
-     // First non-whitespace = beginning of method
-     if (req.start > line_end) {
+     if (uriFound.length() == maxUriLength) {
+         // RFC 7230 section 3.1.1 mandatory (MUST) 414 response
 -        request_parse_status = Http::scUriTooLong;
++        parseStatusCode = Http::scUriTooLong;
+         debugs(33, 5, "invalid request-line. URI longer than " << maxUriLength << " bytes");
+     } else {
+         // RFC 7230 section 3.1.1 required (SHOULD) 400 response
 -        request_parse_status = Http::scBadRequest;
 +        parseStatusCode = Http::scBadRequest;
-         return -1;
+         debugs(33, 5, "invalid request-line. missing URI delimiter");
      }
-     req.m_start = req.start;
+     return -1;
+ }
  
-     // First whitespace = end of method
-     if (first_whitespace > line_end || first_whitespace < req.start) {
-         parseStatusCode = Http::scBadRequest; // no method
-         return -1;
+ int
+ Http::One::RequestParser::parseHttpVersionField(::Parser::Tokenizer &tok)
+ {
+     // partial match of HTTP/1 magic prefix
+     if (tok.remaining().length() < Http1magic.length() && Http1magic.startsWith(tok.remaining())) {
+         debugs(74, 5, "Parser needs more data to find version");
+         return 0;
      }
-     req.m_end = first_whitespace - 1;
-     if (req.m_end < req.m_start) {
-         parseStatusCode = Http::scBadRequest; // missing URI?
+     if (!tok.skip(Http1magic)) {
+         debugs(74, 5, "invalid request-line. not HTTP/1 protocol");
 -        request_parse_status = Http::scHttpVersionNotSupported;
++        parseStatusCode = Http::scHttpVersionNotSupported;
          return -1;
      }
  
-     /* Set method_ */
-     const SBuf tmp = buf_.substr(req.m_start, req.m_end - req.m_start + 1);
-     method_ = HttpRequestMethod(tmp);
-     // First non-whitespace after first SP = beginning of URL+Version
-     if (second_word > line_end || second_word < req.start) {
-         parseStatusCode = Http::scBadRequest; // missing URI
-         return -1;
+     if (tok.atEnd()) {
+         debugs(74, 5, "Parser needs more data to find version");
+         return 0;
      }
-     req.u_start = second_word;
  
-     // RFC 1945: SP and version following URI are optional, marking version 0.9
-     // we identify this by the last whitespace being earlier than URI start
-     if (last_whitespace < second_word && last_whitespace >= req.start) {
-         msgProtocol_ = Http::ProtocolVersion(0,9);
-         req.u_end = line_end;
-         uri_ = buf_.substr(req.u_start, req.u_end - req.u_start + 1);
-         parseStatusCode = Http::scOkay; // HTTP/0.9
+     // get the version minor DIGIT
+     SBuf digit;
+     if (tok.prefix(digit, CharacterSet::DIGIT, 1) && skipLineTerminator(tok)) {
+         // found version fully AND terminator
+         msgProtocol_ = Http::ProtocolVersion(1, (*digit.rawContent() - '0'));
 -        request_parse_status = Http::scOkay;
++        parseStatusCode = Http::scOkay;
+         buf_ = tok.remaining(); // incremental parse checkpoint
          return 1;
-     } else {
-         // otherwise last whitespace is somewhere after end of URI.
-         req.u_end = last_whitespace;
-         // crop any trailing whitespace in the area we think of as URI
-         for (; req.u_end >= req.u_start && xisspace(buf_[req.u_end]); --req.u_end);
-     }
-     if (req.u_end < req.u_start) {
-         parseStatusCode = Http::scBadRequest; // missing URI
-         return -1;
+     } else if (tok.atEnd() || (tok.skip('\r') && tok.atEnd())) {
+         debugs(74, 5, "Parser needs more data to find version");
+         return 0;
+     } // else error ...
+     // non-DIGIT. invalid version number.
 -    request_parse_status = Http::scHttpVersionNotSupported;
++    parseStatusCode = Http::scHttpVersionNotSupported;
+     debugs(33, 5, "invalid request-line. garbage before line terminator");
+     return -1;
+ }
+ /**
+  * Attempt to parse the first line of a new request message.
+  *
+  * Governed by:
+  *  RFC 1945 section 5.1
+  *  RFC 7230 section 2.6, 3.1 and 3.5
+  *
+  * Parsing state is stored between calls. The current implementation uses
+  * checkpoints after each successful request-line field.
+  * The return value tells you whether the parsing is completed or not.
+  *
 - * \retval -1  an error occurred. request_parse_status indicates HTTP status result.
++ * \retval -1  an error occurred. parseStatusCode indicates HTTP status result.
+  * \retval  1  successful parse. member fields contain the request-line items
+  * \retval  0  more data is needed to complete the parse
+  */
+ int
+ Http::One::RequestParser::parseRequestFirstLine()
+ {
+     ::Parser::Tokenizer tok(buf_);
+     debugs(74, 5, "parsing possible request: buf.length=" << buf_.length());
+     debugs(74, DBG_DATA, buf_);
+     // NP: would be static, except it need to change with reconfigure
+     CharacterSet WspDelim = CharacterSet::SP; // strict parse only accepts SP
+     if (Config.onoff.relaxed_header_parser) {
+         // RFC 7230 section 3.5
+         // tolerant parser MAY accept any of SP, HTAB, VT (%x0B), FF (%x0C), or bare CR
+         // as whitespace between request-line fields
+         WspDelim += CharacterSet::HTAB
+                     + CharacterSet("VT,FF","\x0B\x0C")
+                     + CharacterSet::CR;
      }
-     uri_ = buf_.substr(req.u_start, req.u_end - req.u_start + 1);
  
-     // Last whitespace SP = before start of protocol/version
-     if (last_whitespace >= line_end) {
-         parseStatusCode = Http::scBadRequest; // missing version
-         return -1;
+     // only search for method if we have not yet found one
+     if (method_ == Http::METHOD_NONE) {
+         const int res = parseMethodField(tok, WspDelim);
+         if (res < 1)
+             return res;
+         // else keep going...
      }
-     req.v_start = last_whitespace + 1;
-     req.v_end = line_end;
  
-     /* RFC 7230 section 2.6 : handle unsupported HTTP major versions cleanly. */
-     if ((req.v_end - req.v_start +1) < (int)Http1magic.length() || !buf_.substr(req.v_start, SBuf::npos).startsWith(Http1magic)) {
-         // non-HTTP/1 protocols not supported / implemented.
-         parseStatusCode = Http::scHttpVersionNotSupported;
-         return -1;
+     // tolerant parser allows multiple whitespace characters between request-line fields
+     if (Config.onoff.relaxed_header_parser) {
+         const size_t garbage = tok.skipAll(WspDelim);
+         if (garbage > 0) {
+             firstLineGarbage_ += garbage;
+             buf_ = tok.remaining(); // re-checkpoint after garbage
+         }
+     }
+     if (tok.atEnd()) {
+         debugs(74, 5, "Parser needs more data");
+         return 0;
      }
-     // NP: magic octets include the protocol name and major version DIGIT.
-     msgProtocol_.protocol = AnyP::PROTO_HTTP;
-     msgProtocol_.major = 1;
  
-     int i = req.v_start + Http1magic.length() -1;
+     // from here on, we have two possible parse paths: whitespace tolerant, and strict
+     if (Config.onoff.relaxed_header_parser) {
+         // whitespace tolerant
+         // NOTES:
+         // * this would be static, except WspDelim changes with reconfigure
+         // * HTTP-version charset is included by uriValidCharacters()
+         // * terminal CR is included by WspDelim here in relaxed parsing
+         CharacterSet LfDelim = uriValidCharacters() + WspDelim;
+         // seek the LF character, then tokenize the line in reverse
+         SBuf line;
+         if (tok.prefix(line, LfDelim) && tok.skip('\n')) {
+             ::Parser::Tokenizer rTok(line);
+             SBuf nil;
+             (void)rTok.suffix(nil,CharacterSet::CR); // optional CR in terminator
+             SBuf digit;
+             if (rTok.suffix(digit,CharacterSet::DIGIT) && rTok.skipSuffix(Http1magic) && rTok.suffix(nil,WspDelim)) {
+                 uri_ = rTok.remaining();
+                 msgProtocol_ = Http::ProtocolVersion(1, (*digit.rawContent() - '0'));
+                 if (uri_.isEmpty()) {
+                     debugs(33, 5, "invalid request-line. missing URL");
 -                    request_parse_status = Http::scBadRequest;
++                    parseStatusCode = Http::scBadRequest;
+                     return -1;
+                 }
  
-     // catch missing minor part
-     if (++i > line_end) {
-         parseStatusCode = Http::scHttpVersionNotSupported;
-         return -1;
 -                request_parse_status = Http::scOkay;
++                parseStatusCode = Http::scOkay;
+                 buf_ = tok.remaining(); // incremental parse checkpoint
+                 return 1;
+             } else if (method_ == Http::METHOD_GET) {
+                 // RFC 1945 - for GET the line terminator may follow URL instead of a delimiter
+                 debugs(33, 5, "HTTP/0.9 syntax request-line detected");
+                 msgProtocol_ = Http::ProtocolVersion(0,9);
+                 static const SBuf cr("\r",1);
+                 uri_ = line.trim(cr,false,true);
 -                request_parse_status = Http::scOkay;
++                parseStatusCode = Http::scOkay;
+                 buf_ = tok.remaining(); // incremental parse checkpoint
+                 return 1;
+             }
+             debugs(33, 5, "invalid request-line. not HTTP");
 -            request_parse_status = Http::scBadRequest;
++            parseStatusCode = Http::scBadRequest;
+             return -1;
+         }
+         debugs(74, 5, "Parser needs more data");
+         return 0;
      }
-     /* next should be one or more digits */
-     if (!isdigit(buf_[i])) {
-         parseStatusCode = Http::scHttpVersionNotSupported;
-         return -1;
+     // else strict non-whitespace tolerant parse
+     // only search for request-target (URL) if we have not yet found one
+     if (uri_.isEmpty()) {
+         const int res = parseUriField(tok);
+         if (res < 1 || msgProtocol_.protocol == AnyP::PROTO_HTTP)
+             return res;
+         // else keep going...
      }
-     int min = 0;
-     for (; i <= line_end && (isdigit(buf_[i])) && min < 65536; ++i) {
-         min = min * 10;
-         min = min + (buf_[i]) - '0';
+     if (tok.atEnd()) {
+         debugs(74, 5, "Parser needs more data");
+         return 0;
      }
-     // catch too-big values or trailing garbage
-     if (min >= 65536 || i < line_end) {
-         parseStatusCode = Http::scHttpVersionNotSupported;
-         return -1;
+     // HTTP/1 version suffix (protocol magic) followed by CR*LF
+     if (msgProtocol_.protocol == AnyP::PROTO_NONE) {
+         return parseHttpVersionField(tok);
      }
-     msgProtocol_.minor = min;
  
-     /*
-      * Rightio - we have all the schtuff. Return true; we've got enough.
-      */
-     parseStatusCode = Http::scOkay;
-     return 1;
+     // If we got here this method has been called too many times
 -    request_parse_status = Http::scInternalServerError;
++    parseStatusCode = Http::scInternalServerError;
+     debugs(33, 5, "ERROR: Parser already processed request-line");
+     return -1;
  }
  
  bool
index 01c28e7de9e910c01e3059a8ff28c88904ba347a,426ba24854d2adfe40b1392bcdbec608893fc9ec..f793ff0578a9de81258fc18d7fd8173634b99c6d
  
  #include "http/one/Parser.h"
  #include "http/RequestMethod.h"
 -#include "http/StatusCode.h"
  
+ namespace Parser {
+ class Tokenizer;
+ }
  namespace Http {
  namespace One {
  
Simple merge
Simple merge
index f3126ca1092b581f7647b6cee66e3187df56428b,dfe6cbe084d8e93bd09aeb284e8aecfada4143eb..82077c8bfb6748e0aa83b07da3f56e2b6662f6b2
@@@ -67,24 -60,21 +60,21 @@@ testResults(int line, const SBuf &input
      printf("TEST @%d, in=%u: " SQUIDSBUFPH "\n", line, input.length(), SQUIDSBUFPRINT(input));
  #endif
  
+     // runs the parse
      CPPUNIT_ASSERT_EQUAL(expect.parsed, output.parse(input));
-     CPPUNIT_ASSERT_EQUAL(expect.needsMore, output.needsMoreData());
-     if (output.needsMoreData())
-         CPPUNIT_ASSERT_EQUAL(expect.parserState, output.parsingStage_);
-     CPPUNIT_ASSERT_EQUAL(expect.status, output.parseStatusCode);
-     CPPUNIT_ASSERT_EQUAL(expect.msgStart, output.req.start);
-     CPPUNIT_ASSERT_EQUAL(expect.msgEnd, output.req.end);
-     CPPUNIT_ASSERT_EQUAL(expect.suffixSz, output.buf_.length());
-     CPPUNIT_ASSERT_EQUAL(expect.methodStart, output.req.m_start);
-     CPPUNIT_ASSERT_EQUAL(expect.methodEnd, output.req.m_end);
+     // check easily visible field outputs
      CPPUNIT_ASSERT_EQUAL(expect.method, output.method_);
-     CPPUNIT_ASSERT_EQUAL(expect.uriStart, output.req.u_start);
-     CPPUNIT_ASSERT_EQUAL(expect.uriEnd, output.req.u_end);
      if (expect.uri != NULL)
          CPPUNIT_ASSERT_EQUAL(0, output.uri_.cmp(expect.uri));
-     CPPUNIT_ASSERT_EQUAL(expect.versionStart, output.req.v_start);
-     CPPUNIT_ASSERT_EQUAL(expect.versionEnd, output.req.v_end);
      CPPUNIT_ASSERT_EQUAL(expect.version, output.msgProtocol_);
 -    CPPUNIT_ASSERT_EQUAL(expect.status, output.request_parse_status);
++    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());
  }
  #endif /* __cplusplus >= 200103L */
  
@@@ -96,18 -86,10 +86,10 @@@ testHttp1Parser::testParserConstruct(
          Http1::RequestParser output;
          CPPUNIT_ASSERT_EQUAL(true, output.needsMoreData());
          CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_NONE, output.parsingStage_);
 -        CPPUNIT_ASSERT_EQUAL(Http::scNone, output.request_parse_status);
 +        CPPUNIT_ASSERT_EQUAL(Http::scNone, output.parseStatusCode); // XXX: clear() not being called.
-         CPPUNIT_ASSERT_EQUAL(-1, output.req.start);
-         CPPUNIT_ASSERT_EQUAL(-1, output.req.end);
          CPPUNIT_ASSERT(output.buf_.isEmpty());
-         CPPUNIT_ASSERT_EQUAL(-1, output.req.m_start);
-         CPPUNIT_ASSERT_EQUAL(-1, output.req.m_end);
          CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_NONE), output.method_);
-         CPPUNIT_ASSERT_EQUAL(-1, output.req.u_start);
-         CPPUNIT_ASSERT_EQUAL(-1, output.req.u_end);
          CPPUNIT_ASSERT(output.uri_.isEmpty());
-         CPPUNIT_ASSERT_EQUAL(-1, output.req.v_start);
-         CPPUNIT_ASSERT_EQUAL(-1, output.req.v_end);
          CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output.msgProtocol_);
      }
  
          Http1::RequestParser *output = new Http1::RequestParser;
          CPPUNIT_ASSERT_EQUAL(true, output->needsMoreData());
          CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_NONE, output->parsingStage_);
 -        CPPUNIT_ASSERT_EQUAL(Http::scNone, output->request_parse_status);
 +        CPPUNIT_ASSERT_EQUAL(Http::scNone, output->parseStatusCode);
-         CPPUNIT_ASSERT_EQUAL(-1, output->req.start);
-         CPPUNIT_ASSERT_EQUAL(-1, output->req.end);
          CPPUNIT_ASSERT(output->buf_.isEmpty());
-         CPPUNIT_ASSERT_EQUAL(-1, output->req.m_start);
-         CPPUNIT_ASSERT_EQUAL(-1, output->req.m_end);
          CPPUNIT_ASSERT_EQUAL(HttpRequestMethod(Http::METHOD_NONE), output->method_);
-         CPPUNIT_ASSERT_EQUAL(-1, output->req.u_start);
-         CPPUNIT_ASSERT_EQUAL(-1, output->req.u_end);
          CPPUNIT_ASSERT(output->uri_.isEmpty());
-         CPPUNIT_ASSERT_EQUAL(-1, output->req.v_start);
-         CPPUNIT_ASSERT_EQUAL(-1, output->req.v_end);
          CPPUNIT_ASSERT_EQUAL(AnyP::ProtocolVersion(), output->msgProtocol_);
          delete output;
      }