]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Merge from trunk rev.13584
authorAmos Jeffries <squid3@treenet.co.nz>
Sun, 14 Sep 2014 12:43:00 +0000 (05:43 -0700)
committerAmos Jeffries <squid3@treenet.co.nz>
Sun, 14 Sep 2014 12:43:00 +0000 (05:43 -0700)
Also, re-remove the connNoteUseOfBuffer logics pulled back in by earlier
trunk merge.

39 files changed:
1  2 
configure.ac
src/AccessLogEntry.h
src/HttpHeader.cc
src/HttpHeader.h
src/HttpHeaderTools.cc
src/HttpHeaderTools.h
src/HttpMsg.cc
src/HttpMsg.h
src/HttpRequest.cc
src/HttpRequest.h
src/Makefile.am
src/MemObject.h
src/Store.h
src/acl/Method.h
src/acl/MethodData.cc
src/acl/MethodData.h
src/cache_cf.cc
src/client_side.cc
src/client_side.h
src/htcp.cc
src/htcp.h
src/http/Makefile.am
src/http/RequestMethod.cc
src/http/RequestMethod.h
src/http/one/Parser.cc
src/http/one/Parser.h
src/mgr/ActionParams.cc
src/mgr/ActionParams.h
src/mgr/ActionWriter.h
src/mgr/Filler.h
src/mime.cc
src/mime_header.cc
src/mime_header.h
src/servers/FtpServer.cc
src/servers/FtpServer.h
src/servers/HttpServer.cc
src/tests/testHttp1Parser.cc
src/tests/testHttp1Parser.h
src/tests/testHttpRequestMethod.cc

diff --cc configure.ac
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
diff --cc src/HttpMsg.cc
Simple merge
diff --cc src/HttpMsg.h
Simple merge
Simple merge
Simple merge
diff --cc src/Makefile.am
Simple merge
diff --cc src/MemObject.h
Simple merge
diff --cc src/Store.h
Simple merge
Simple merge
Simple merge
Simple merge
diff --cc src/cache_cf.cc
Simple merge
index 0805774bfb91b08033cac571c8b9f6f4a93ea454,8524aad6e6c5691b0c7d91a6f173f15d6b303cd6..6d6a787f38274bf464df1189ba521672f208042f
@@@ -217,7 -196,7 +197,6 @@@ static void ClientSocketContextPushDefe
  static void clientUpdateSocketStats(LogTags logType, size_t size);
  
  char *skipLeadingSpace(char *aString);
--static void connNoteUseOfBuffer(ConnStateData* conn, size_t byteCount);
  
  clientStreamNode *
  ClientSocketContext::getTail() const
@@@ -2355,13 -2388,13 +2336,6 @@@ ConnStateData::consumeInput(const size_
      debugs(33, 5, "in.buf has " << in.buf.length() << " unused bytes");
  }
  
--// TODO: Remove when renaming ConnStateData
--void
--connNoteUseOfBuffer(ConnStateData* conn, size_t byteCount)
--{
--    conn->consumeInput(byteCount);
--}
--
  void
  ConnStateData::clientAfterReadingRequests()
  {
@@@ -2474,8 -2507,25 +2448,24 @@@ bool ConnStateData::serveDelayedError(C
  }
  #endif // USE_OPENSSL
  
 -     * be freed and the above connNoteUseOfBuffer() would hit an
 -     * assertion, not to mention that we were accessing freed memory.
+ static void
+ clientProcessRequestFinished(ConnStateData *conn, const HttpRequest::Pointer &request)
+ {
+     /*
+      * DPW 2007-05-18
+      * Moved the TCP_RESET feature from clientReplyContext::sendMoreData
+      * to here because calling comm_reset_close() causes http to
++     * be freed before accessing.
+      */
+     if (request != NULL && request->flags.resetTcp && Comm::IsConnOpen(conn->clientConnection)) {
+         debugs(33, 3, HERE << "Sending TCP RST on " << conn->clientConnection);
+         conn->flags.readMore = false;
+         comm_reset_close(conn->clientConnection);
+     }
+ }
  void
 -clientProcessRequest(ConnStateData *conn, HttpParser *hp, ClientSocketContext *context, const HttpRequestMethod& method, Http::ProtocolVersion http_ver)
 +clientProcessRequest(ConnStateData *conn, const Http1::RequestParserPointer &hp, ClientSocketContext *context)
  {
      ClientHttpRequest *http = context->http;
      HttpRequest::Pointer request;
          // only need to go through the final body/conn setup to doCallouts().
          assert(http->request);
          request = http->request;
 -        notedUseOfBuffer = true;
      } else {
  
-     if (context->flags.parsed_ok == 0) {
-         clientStreamNode *node = context->getClientReplyContext();
-         debugs(33, 2, "Invalid Request");
-         conn->quitAfterError(NULL);
-         // setLogUri should called before repContext->setReplyToError
-         setLogUri(http, http->uri,  true);
-         clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
-         assert (repContext);
-         switch (hp->request_parse_status) {
-         case Http::scHeaderTooLarge:
-             repContext->setReplyToError(ERR_TOO_BIG, Http::scBadRequest, hp->method(), http->uri,
-                                         conn->clientConnection->remote, NULL, conn->in.buf.c_str(), NULL);
-             break;
-         case Http::scMethodNotAllowed:
-             repContext->setReplyToError(ERR_UNSUP_REQ, Http::scMethodNotAllowed, hp->method(), http->uri,
-                                         conn->clientConnection->remote, NULL, conn->in.buf.c_str(), NULL);
-             break;
-         case Http::scHttpVersionNotSupported:
-             repContext->setReplyToError(ERR_UNSUP_HTTPVERSION, Http::scHttpVersionNotSupported, hp->method(), http->uri,
-                                         conn->clientConnection->remote, NULL, conn->in.buf.c_str(), NULL);
-             break;
-         default:
-             repContext->setReplyToError(ERR_INVALID_REQ, hp->request_parse_status, hp->method(), http->uri,
-                                         conn->clientConnection->remote, NULL, conn->in.buf.c_str(), NULL);
+         if (context->flags.parsed_ok == 0) {
+             clientStreamNode *node = context->getClientReplyContext();
 -            debugs(33, 2, "clientProcessRequest: Invalid Request");
++            debugs(33, 2, "Invalid Request");
+             conn->quitAfterError(NULL);
+             // setLogUri should called before repContext->setReplyToError
 -            setLogUri(http, http->uri,  true);
++            setLogUri(http, http->uri, true);
+             clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
 -            assert (repContext);
++            assert(repContext);
+             switch (hp->request_parse_status) {
+             case Http::scHeaderTooLarge:
+                 repContext->setReplyToError(ERR_TOO_BIG, Http::scBadRequest, method, http->uri, conn->clientConnection->remote, NULL, conn->in.buf.c_str(), NULL);
+                 break;
+             case Http::scMethodNotAllowed:
+                 repContext->setReplyToError(ERR_UNSUP_REQ, Http::scMethodNotAllowed, method, http->uri,
+                                             conn->clientConnection->remote, NULL, conn->in.buf.c_str(), NULL);
+                 break;
++            case Http::scHttpVersionNotSupported:
++                repContext->setReplyToError(ERR_UNSUP_HTTPVERSION, Http::scHttpVersionNotSupported, hp->method(), http->uri,
++                                            conn->clientConnection->remote, NULL, conn->in.buf.c_str(), NULL);
++                break;
+             default:
+                 repContext->setReplyToError(ERR_INVALID_REQ, hp->request_parse_status, method, http->uri,
+                                             conn->clientConnection->remote, NULL, conn->in.buf.c_str(), NULL);
+             }
+             assert(context->http->out.offset == 0);
+             context->pullData();
 -            connNoteUseOfBuffer(conn, http->req_sz);
+             return;
          }
-         assert(context->http->out.offset == 0);
-         context->pullData();
-         goto finish;
-     }
  
-     if ((request = HttpRequest::CreateFromUrlAndMethod(http->uri, hp->method())) == NULL) {
-         clientStreamNode *node = context->getClientReplyContext();
-         debugs(33, 5, "Invalid URL: " << http->uri);
-         conn->quitAfterError(request.getRaw());
-         // setLogUri should called before repContext->setReplyToError
-         setLogUri(http, http->uri,  true);
-         clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
-         assert (repContext);
-         repContext->setReplyToError(ERR_INVALID_URL, Http::scBadRequest, hp->method(), http->uri, conn->clientConnection->remote, NULL, NULL, NULL);
-         assert(context->http->out.offset == 0);
-         context->pullData();
-         goto finish;
-     }
 -        if ((request = HttpRequest::CreateFromUrlAndMethod(http->uri, method)) == NULL) {
++        if ((request = HttpRequest::CreateFromUrlAndMethod(http->uri, hp->method())) == NULL) {
+             clientStreamNode *node = context->getClientReplyContext();
+             debugs(33, 5, "Invalid URL: " << http->uri);
+             conn->quitAfterError(request.getRaw());
+             // setLogUri should called before repContext->setReplyToError
 -            setLogUri(http, http->uri,  true);
++            setLogUri(http, http->uri, true);
+             clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
 -            assert (repContext);
 -            repContext->setReplyToError(ERR_INVALID_URL, Http::scBadRequest, method, http->uri, conn->clientConnection->remote, NULL, NULL, NULL);
++            assert(repContext);
++            repContext->setReplyToError(ERR_INVALID_URL, Http::scBadRequest, hp->method(), http->uri, conn->clientConnection->remote, NULL, NULL, NULL);
+             assert(context->http->out.offset == 0);
+             context->pullData();
 -            connNoteUseOfBuffer(conn, http->req_sz);
+             return;
+         }
  
-     /* compile headers */
-     if (hp->messageProtocol().major >= 1 && !request->parseHeader(*hp)) {
-         clientStreamNode *node = context->getClientReplyContext();
-         debugs(33, 5, "Failed to parse request headers:\n" << hp->mimeHeader());
-         conn->quitAfterError(request.getRaw());
-         // setLogUri should called before repContext->setReplyToError
-         setLogUri(http, http->uri,  true);
-         clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
-         assert (repContext);
-         repContext->setReplyToError(ERR_INVALID_REQ, Http::scBadRequest, hp->method(), http->uri, conn->clientConnection->remote, NULL, NULL, NULL);
-         assert(context->http->out.offset == 0);
-         context->pullData();
-         goto finish;
-     }
+         /* RFC 2616 section 10.5.6 : handle unsupported HTTP major versions cleanly. */
+         /* We currently only support 0.9, 1.0, 1.1 properly */
+         /* TODO: move HTTP-specific processing into servers/HttpServer and such */
+         if ( (http_ver.major == 0 && http_ver.minor != 9) ||
+                 (http_ver.major > 1) ) {
+             clientStreamNode *node = context->getClientReplyContext();
+             debugs(33, 5, "Unsupported HTTP version discovered. :\n" << HttpParserHdrBuf(hp));
+             conn->quitAfterError(request.getRaw());
+             // setLogUri should called before repContext->setReplyToError
+             setLogUri(http, http->uri,  true);
+             clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
+             assert (repContext);
+             repContext->setReplyToError(ERR_UNSUP_HTTPVERSION, Http::scHttpVersionNotSupported, method, http->uri,
+                                         conn->clientConnection->remote, NULL, HttpParserHdrBuf(hp), NULL);
+             assert(context->http->out.offset == 0);
+             context->pullData();
 -            connNoteUseOfBuffer(conn, http->req_sz);
+             clientProcessRequestFinished(conn, request);
+             return;
+         }
  
 -        /* we should skip request line! */
 -        /* XXX should actually know the damned buffer size here */
 -        if (http_ver.major >= 1 && !request->parseHeader(HttpParserHdrBuf(hp), HttpParserHdrSz(hp))) {
+         /* compile headers */
 -            debugs(33, 5, "Failed to parse request headers:\n" << HttpParserHdrBuf(hp));
++        if (hp->messageProtocol().major >= 1 && !request->parseHeader(*hp)) {
+             clientStreamNode *node = context->getClientReplyContext();
 -            setLogUri(http, http->uri,  true);
++            debugs(33, 5, "Failed to parse request headers:\n" << hp->mimeHeader());
+             conn->quitAfterError(request.getRaw());
+             // setLogUri should called before repContext->setReplyToError
 -            assert (repContext);
 -            repContext->setReplyToError(ERR_INVALID_REQ, Http::scBadRequest, method, http->uri, conn->clientConnection->remote, NULL, NULL, NULL);
++            setLogUri(http, http->uri, true);
+             clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
 -            connNoteUseOfBuffer(conn, http->req_sz);
++            assert(repContext);
++            repContext->setReplyToError(ERR_INVALID_REQ, Http::scBadRequest, hp->method(), http->uri, conn->clientConnection->remote, NULL, NULL, NULL);
+             assert(context->http->out.offset == 0);
+             context->pullData();
+             clientProcessRequestFinished(conn, request);
+             return;
+         }
      }
  
      // Some blobs below are still HTTP-specific, but we would have to rewrite
                                      conn->clientConnection->remote, request.getRaw(), NULL, NULL);
          assert(context->http->out.offset == 0);
          context->pullData();
-         goto finish;
 -        connNoteUseOfBuffer(conn, http->req_sz);
+         clientProcessRequestFinished(conn, request);
+         return;
      }
  
      if (!chunked && !clientIsContentLengthValid(request.getRaw())) {
                                      conn->clientConnection->remote, request.getRaw(), NULL, NULL);
          assert(context->http->out.offset == 0);
          context->pullData();
-         goto finish;
 -        connNoteUseOfBuffer(conn, http->req_sz);
+         clientProcessRequestFinished(conn, request);
+         return;
      }
  
      if (request->header.has(HDR_EXPECT)) {
                                          conn->clientConnection->remote, request.getRaw(), NULL, NULL);
              assert(context->http->out.offset == 0);
              context->pullData();
-             goto finish;
 -            connNoteUseOfBuffer(conn, http->req_sz);
+             clientProcessRequestFinished(conn, request);
+             return;
          }
      }
  
      }
  
  #if USE_OPENSSL
-     if (conn->switchedToHttps() && conn->serveDelayedError(context))
-         goto finish;
+     if (conn->switchedToHttps() && conn->serveDelayedError(context)) {
 -        if (!notedUseOfBuffer)
 -            connNoteUseOfBuffer(conn, http->req_sz);
+         clientProcessRequestFinished(conn, request);
+         return;
+     }
  #endif
  
      /* Do we expect a request-body? */
  
      http->doCallouts();
  
- finish:
-     /*
-      * DPW 2007-05-18
-      * Moved the TCP_RESET feature from clientReplyContext::sendMoreData
-      * to here because calling comm_reset_close() causes http to
-      * be freed and the above connNoteUseOfBuffer() would hit an
-      * assertion, not to mention that we were accessing freed memory.
-      */
-     if (request != NULL && request->flags.resetTcp && Comm::IsConnOpen(conn->clientConnection)) {
-         debugs(33, 3, HERE << "Sending TCP RST on " << conn->clientConnection);
-         conn->flags.readMore = false;
-         comm_reset_close(conn->clientConnection);
-     }
 -    if (!notedUseOfBuffer)
 -        connNoteUseOfBuffer(conn, http->req_sz);
 -
+     clientProcessRequestFinished(conn, request);
  }
  
 -static void
 -connStripBufferWhitespace (ConnStateData * conn)
 -{
 -    // XXX: kill this whole function.
 -    while (!conn->in.buf.isEmpty() && xisspace(conn->in.buf.at(0))) {
 -        conn->in.buf.consume(1);
 -    }
 -}
 -
  int
  ConnStateData::pipelinePrefetchMax() const
  {
@@@ -2811,9 -3166,13 +3083,18 @@@ ConnStateData::clientParseRequests(
          if (concurrentRequestQueueFilled())
              break;
  
++<<<<<<< TREE
 +        if (ClientSocketContext *context = parseOneRequest()) {
++=======
+         // try to parse the PROXY protocol header magic bytes
+         if (needProxyProtocolHeader_ && !parseProxyProtocolHeader())
+             break;
+         Http::ProtocolVersion http_ver;
+         if (ClientSocketContext *context = parseOneRequest(http_ver)) {
++>>>>>>> MERGE-SOURCE
              debugs(33, 5, clientConnection << ": done parsing a request");
 +
              AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "clientLifetimeTimeout",
                                               CommTimeoutCbPtrFun(clientLifetimeTimeout, context->http));
              commSetConnTimeout(clientConnection, Config.Timeout.lifetime, timeoutCall);
@@@ -2984,7 -3341,7 +3265,7 @@@ ConnStateData::handleRequestBodyData(
      }
  
      if (putSize > 0)
--        connNoteUseOfBuffer(this, putSize);
++        consumeInput(putSize);
  
      if (!bodyPipe) {
          debugs(33,5, HERE << "produced entire request body for " << clientConnection);
Simple merge
diff --cc src/htcp.cc
Simple merge
diff --cc src/htcp.h
Simple merge
Simple merge
index 6eb1048aaa9093e232522469f19102b5d369b863,74f9c77cbc307792d8ddb2f7c1c147edb8d9951d..02703a8e7aed71846a50281e32b9d43149f1da01
@@@ -1,9 -1,15 +1,15 @@@
  /*
-  * DEBUG: section 73    HTTP Request
+  * Copyright (C) 1996-2014 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.
   */
  
+ /* DEBUG: section 73    HTTP Request */
  #include "squid.h"
 -#include "HttpRequestMethod.h"
 +#include "http/RequestMethod.h"
  #include "SquidConfig.h"
  #include "wordlist.h"
  
Simple merge
index 42e3ad18a27dae614b41b43cdb46c64c358baca7,0000000000000000000000000000000000000000..48585d438889d1acd2f095cf612858e91961e685
mode 100644,000000..100644
--- /dev/null
@@@ -1,72 -1,0 +1,80 @@@
++/*
++ * Copyright (C) 1996-2014 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 "Debug.h"
 +#include "http/one/Parser.h"
 +#include "parser/Tokenizer.h"
 +
 +/// RFC 7230 section 2.6 - 7 magic octets
 +const SBuf Http::One::Parser::Http1magic("HTTP/1.");
 +
 +void
 +Http::One::Parser::clear()
 +{
 +    parsingStage_ = HTTP_PARSE_NONE;
 +    buf_ = NULL;
 +    msgProtocol_ = AnyP::ProtocolVersion();
 +    mimeHeaderBlock_.clear();
 +}
 +
 +// arbitrary maximum-length for headers which can be found by Http1Parser::getHeaderField()
 +#define GET_HDR_SZ    1024
 +
 +// BUG: returns only the first header line with given name,
 +//      ignores multi-line headers and obs-fold headers
 +char *
 +Http::One::Parser::getHeaderField(const char *name)
 +{
 +    if (!headerBlockSize() || !name)
 +        return NULL;
 +
 +    LOCAL_ARRAY(char, header, GET_HDR_SZ);
 +    const int namelen = name ? strlen(name) : 0;
 +
 +    debugs(25, 5, "looking for " << name);
 +
 +    // while we can find more LF in the SBuf
 +    static CharacterSet iso8859Line = CharacterSet("non-LF",'\0','\n'-1) + CharacterSet(NULL, '\n'+1, (unsigned char)0xFF);
 +    ::Parser::Tokenizer tok(mimeHeaderBlock_);
 +    SBuf p;
 +    static const SBuf crlf("\r\n");
 +
 +    while (tok.prefix(p, iso8859Line)) {
 +        tok.skipOne(CharacterSet::LF); // move tokenizer past the LF
 +
 +        // header lines must start with the name (case insensitive)
 +        if (p.substr(0, namelen).caseCmp(name, namelen))
 +            continue;
 +
 +        // then a COLON
 +        if (p[namelen] != ':')
 +            continue;
 +
 +        // drop any trailing *CR sequence
 +        p.trim(crlf, false, true);
 +
 +        debugs(25, 5, "checking " << p);
 +        p.consume(namelen + 1);
 +
 +        // TODO: optimize SBuf::trim to take CharacterSet directly
 +        ::Parser::Tokenizer t(p);
 +        t.skipAll(CharacterSet::WSP);
 +        p = t.remaining();
 +
 +        // prevent buffer overrun on char header[];
 +        p.chop(0, sizeof(header)-1);
 +
 +        // return the header field-value
 +        xstrncpy(header, p.rawContent(), p.length());
 +        debugs(25, 5, "returning " << header);
 +        return header;
 +    }
 +
 +    return NULL;
 +}
index e6ae3f7259944c6ddd3ab7bc7c459ff2aa58c41a,0000000000000000000000000000000000000000..63c9318c18ed69c8d2166452a702a984621cf610
mode 100644,000000..100644
--- /dev/null
@@@ -1,101 -1,0 +1,109 @@@
++/*
++ * Copyright (C) 1996-2014 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.
++ */
++
 +#ifndef _SQUID_SRC_HTTP_ONE_PARSER_H
 +#define _SQUID_SRC_HTTP_ONE_PARSER_H
 +
 +#include "anyp/ProtocolVersion.h"
 +#include "http/one/forward.h"
 +#include "SBuf.h"
 +
 +namespace Http {
 +namespace One {
 +
 +// Parser states
 +enum ParseState {
 +    HTTP_PARSE_NONE,     ///< initialized, but nothing usefully parsed yet
 +    HTTP_PARSE_FIRST,    ///< HTTP/1 message first-line
 +    HTTP_PARSE_MIME,     ///< HTTP/1 mime-header block
 +    HTTP_PARSE_DONE      ///< parsed a message header, or reached a terminal syntax error
 +};
 +
 +/** HTTP/1.x protocol parser
 + *
 + * Works on a raw character I/O buffer and tokenizes the content into
 + * the major CRLF delimited segments of an HTTP/1 procotol message:
 + *
 + * \item first-line (request-line / simple-request / status-line)
 + * \item mime-header 0*( header-name ':' SP field-value CRLF)
 + */
 +class Parser : public RefCountable
 +{
 +public:
 +    typedef SBuf::size_type size_type;
 +
 +    Parser() : parsingStage_(HTTP_PARSE_NONE) {}
 +
 +    /// Set this parser back to a default state.
 +    /// Will DROP any reference to a buffer (does not free).
 +    virtual void clear();
 +
 +    /// attempt to parse a message from the buffer
 +    /// \retval true if a full message was found and parsed
 +    /// \retval false if incomplete, invalid or no message was found
 +    virtual bool parse(const SBuf &aBuf) = 0;
 +
 +    /** Whether the parser is waiting on more data to complete parsing a message.
 +     * Use to distinguish between incomplete data and error results
 +     * when parse() returns false.
 +     */
 +    bool needsMoreData() const {return parsingStage_!=HTTP_PARSE_DONE;}
 +
 +    /// size in bytes of the first line including CRLF terminator
 +    virtual size_type firstLineSize() const = 0;
 +
 +    /// size in bytes of the message headers including CRLF terminator(s)
 +    /// but excluding first-line bytes
 +    size_type headerBlockSize() const {return mimeHeaderBlock_.length();}
 +
 +    /// size in bytes of HTTP message block, includes first-line and mime headers
 +    /// excludes any body/entity/payload bytes
 +    /// excludes any garbage prefix before the first-line
 +    size_type messageHeaderSize() const {return firstLineSize() + headerBlockSize();}
 +
 +    /// buffer containing HTTP mime headers, excluding message first-line.
 +    SBuf mimeHeader() const {return mimeHeaderBlock_;}
 +
 +    /// the protocol label for this message
 +    const AnyP::ProtocolVersion & messageProtocol() const {return msgProtocol_;}
 +
 +    /**
 +     * Scan the mime header block (badly) for a header with teh given name.
 +     *
 +     * BUG: omits lines when searching for headers with obs-fold or multiple entries.
 +     *
 +     * BUG: limits output to just 1KB when Squid accepts up to 64KB line length.
 +     *
 +     * \return A pointer to a field-value of the first matching field-name, or NULL.
 +     */
 +    char *getHeaderField(const char *name);
 +
 +    /// the remaining unprocessed section of buffer
 +    const SBuf &remaining() const {return buf_;}
 +
 +protected:
 +    /// RFC 7230 section 2.6 - 7 magic octets
 +    static const SBuf Http1magic;
 +
 +    /// bytes remaining to be parsed
 +    SBuf buf_;
 +
 +    /// what stage the parser is currently up to
 +    ParseState parsingStage_;
 +
 +    /// what protocol label has been found in the first line (if any)
 +    AnyP::ProtocolVersion msgProtocol_;
 +
 +    /// buffer holding the mime headers (if any)
 +    SBuf mimeHeaderBlock_;
 +};
 +
 +} // namespace One
 +} // namespace Http
 +
 +#endif /*  _SQUID_SRC_HTTP_ONE_PARSER_H */
Simple merge
Simple merge
Simple merge
Simple merge
diff --cc src/mime.cc
Simple merge
index 2a40576485e0d393d4abbc8b2d78fe1efc9da0a1,92706de16c6a2c75b7513929a4b57a0fd27ee039..4d6f5d15ac63056a93bdd9fa5337c2a71ea2e2be
@@@ -1,36 -1,16 +1,14 @@@
  /*
-  * DEBUG: section 25    MiME Header Parsing
-  * AUTHOR: Harvest Derived
-  *
-  * SQUID Web Proxy Cache          http://www.squid-cache.org/
-  * ----------------------------------------------------------
-  *
-  *  Squid is the result of efforts by numerous individuals from
-  *  the Internet community; see the CONTRIBUTORS file for full
-  *  details.   Many organizations have provided support for Squid's
-  *  development; see the SPONSORS file for full details.  Squid is
-  *  Copyrighted (C) 2001 by the Regents of the University of
-  *  California; see the COPYRIGHT file for full details.  Squid
-  *  incorporates software developed and/or copyrighted by other
-  *  sources; see the CREDITS file for full details.
-  *
-  *  This program is free software; you can redistribute it and/or modify
-  *  it under the terms of the GNU General Public License as published by
-  *  the Free Software Foundation; either version 2 of the License, or
-  *  (at your option) any later version.
-  *
-  *  This program is distributed in the hope that it will be useful,
-  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  *  GNU General Public License for more details.
-  *
-  *  You should have received a copy of the GNU General Public License
-  *  along with this program; if not, write to the Free Software
-  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+  * Copyright (C) 1996-2014 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.
   */
  
+ /* DEBUG: section 25    MiME Header Parsing */
  #include "squid.h"
 -
 -#define GET_HDR_SZ 1024
  #include "Debug.h"
  #include "profiler/Profiler.h"
  
Simple merge
Simple merge
Simple merge
Simple merge
index 4ca81c0188f9ffee9a85a8d305b95b1061aebfcc,0000000000000000000000000000000000000000..742bab8039a37c27a42c017e2e02e8e013a39c4d
mode 100644,000000..100644
--- /dev/null
@@@ -1,1435 -1,0 +1,1442 @@@
- #define SQUID_UNIT_TEST 1
++/*
++ * Copyright (C) 1996-2014 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/one/RequestParser.h"
 +#include "http/RequestMethod.h"
 +#include "Mem.h"
 +#include "MemBuf.h"
 +#include "SquidConfig.h"
 +#include "testHttp1Parser.h"
 +
 +CPPUNIT_TEST_SUITE_REGISTRATION( testHttp1Parser );
 +
 +void
 +testHttp1Parser::globalSetup()
 +{
 +    static bool setup_done = false;
 +    if (setup_done)
 +        return;
 +
 +    Mem::Init();
 +    setup_done = true;
 +
 +    // default to strict parser. set for loose parsing specifically where behaviour differs.
 +    Config.onoff.relaxed_header_parser = 0;
 +
 +    Config.maxRequestHeaderSize = 1024; // XXX: unit test the RequestParser handling of this limit
 +}
 +
 +struct resultSet {
 +    bool parsed;
 +    bool needsMore;
 +    Http1::ParseState parserState;
 +    Http::StatusCode status;
 +    int msgStart;
 +    int msgEnd;
 +    SBuf::size_type suffixSz;
 +    int methodStart;
 +    int methodEnd;
 +    HttpRequestMethod method;
 +    int uriStart;
 +    int uriEnd;
 +    const char *uri;
 +    int versionStart;
 +    int versionEnd;
 +    AnyP::ProtocolVersion version;
 +};
 +
 +static void
 +testResults(int line, const SBuf &input, Http1::RequestParser &output, struct resultSet &expect)
 +{
 +#if WHEN_TEST_DEBUG_IS_NEEDED
 +    printf("TEST @%d, in=%u: " SQUIDSBUFPH "\n", line, input.length(), SQUIDSBUFPRINT(input));
 +#endif
 +
 +    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.request_parse_status);
 +    CPPUNIT_ASSERT_EQUAL(expect.msgStart, output.req.start);
 +    CPPUNIT_ASSERT_EQUAL(expect.msgEnd, output.req.end);
 +    CPPUNIT_ASSERT_EQUAL(expect.suffixSz, output.buf_.length());
 +    CPPUNIT_ASSERT_EQUAL(expect.methodStart, output.req.m_start);
 +    CPPUNIT_ASSERT_EQUAL(expect.methodEnd, output.req.m_end);
 +    CPPUNIT_ASSERT_EQUAL(expect.method, output.method_);
 +    CPPUNIT_ASSERT_EQUAL(expect.uriStart, output.req.u_start);
 +    CPPUNIT_ASSERT_EQUAL(expect.uriEnd, output.req.u_end);
 +    if (expect.uri != NULL)
 +        CPPUNIT_ASSERT_EQUAL(0, output.uri_.cmp(expect.uri));
 +    CPPUNIT_ASSERT_EQUAL(expect.versionStart, output.req.v_start);
 +    CPPUNIT_ASSERT_EQUAL(expect.versionEnd, output.req.v_end);
 +    CPPUNIT_ASSERT_EQUAL(expect.version, output.msgProtocol_);
 +}
 +
 +void
 +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.request_parse_status); // 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_);
 +    }
 +
 +    // 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->request_parse_status);
 +        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;
 +    }
 +}
 +
 +void
 +testHttp1Parser::testParseRequestLineProtocols()
 +{
 +    // ensure MemPools etc exist
 +    globalSetup();
 +
 +    SBuf input;
 +    Http1::RequestParser output;
 +
 +    // TEST: Do we comply with RFC 1945 section 5.1 ?
 +    // TEST: Do we comply with RFC 2616 section 5.1 ?
 +
 +    // RFC 1945 : HTTP/0.9 simple-request
 +    {
 +        input.append("GET /\r\n", 7);
 +        struct resultSet expect = {
 +            .parsed = true,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_DONE,
 +            .status = Http::scOkay,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = 0,
 +            .methodStart = 0,
 +            .methodEnd = 2,
 +            .method = HttpRequestMethod(Http::METHOD_GET),
 +            .uriStart = 4,
 +            .uriEnd = 4,
 +            .uri = "/",
 +            .versionStart = -1,
 +            .versionEnd = -1,
 +            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9)
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    // RFC 1945 : invalid HTTP/0.9 simple-request (only GET is valid)
 +#if WHEN_RFC_COMPLIANT
 +    {
 +        input.append("POST /\r\n", 7);
 +        struct resultSet expect = {
 +            .parsed = true,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_DONE,
 +            .status = Http::scOkay,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = 0,
 +            .methodStart = 0,
 +            .methodEnd = 3,
 +            .method = HttpRequestMethod(Http::METHOD_POST),
 +            .uriStart = 5,
 +            .uriEnd = 5,
 +            .uri = "/",
 +            .versionStart = -1,
 +            .versionEnd = -1,
 +            .version = AnyP::ProtocolVersion()
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +#endif
 +    // RFC 1945 and 2616 : HTTP/1.0 request
 +    {
 +        input.append("GET / HTTP/1.0\r\n", 16);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = true,
 +            .parserState = Http1::HTTP_PARSE_MIME,
 +            .status = Http::scOkay,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = 0,
 +            .methodStart = 0,
 +            .methodEnd = 2,
 +            .method = HttpRequestMethod(Http::METHOD_GET),
 +            .uriStart = 4,
 +            .uriEnd = 4,
 +            .uri = "/",
 +            .versionStart = 6,
 +            .versionEnd = 13,
 +            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,0)
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    // RFC 2616 : HTTP/1.1 request
 +    {
 +        input.append("GET / HTTP/1.1\r\n", 16);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = true,
 +            .parserState = Http1::HTTP_PARSE_MIME,
 +            .status = Http::scOkay,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = 0,
 +            .methodStart = 0,
 +            .methodEnd = 2,
 +            .method = HttpRequestMethod(Http::METHOD_GET),
 +            .uriStart = 4,
 +            .uriEnd = 4,
 +            .uri = "/",
 +            .versionStart = 6,
 +            .versionEnd = 13,
 +            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    // RFC 2616 : future version full-request
 +    {
 +        input.append("GET / HTTP/1.2\r\n", 16);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = true,
 +            .parserState = Http1::HTTP_PARSE_MIME,
 +            .status = Http::scOkay,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = 0,
 +            .methodStart = 0,
 +            .methodEnd = 2,
 +            .method = HttpRequestMethod(Http::METHOD_GET),
 +            .uriStart = 4,
 +            .uriEnd = 4,
 +            .uri = "/",
 +            .versionStart = 6,
 +            .versionEnd = 13,
 +            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,2)
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    // RFC 7230 : future versions do not use request-line syntax
 +    {
 +        input.append("GET / HTTP/10.12\r\n", 18);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_MIME,
 +            .status = Http::scHttpVersionNotSupported,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = input.length(),
 +            .methodStart = 0,
 +            .methodEnd = 2,
 +            .method = HttpRequestMethod(Http::METHOD_GET),
 +            .uriStart = 4,
 +            .uriEnd = 4,
 +            .uri = "/",
 +            .versionStart = 6,
 +            .versionEnd = 15,
 +            .version = AnyP::ProtocolVersion()
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    // unknown non-HTTP protocol names
 +    {
 +        input.append("GET / FOO/1.0\n", 14);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_DONE,
 +            .status = Http::scHttpVersionNotSupported,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = input.length(),
 +            .methodStart = 0,
 +            .methodEnd = 2,
 +            .method = HttpRequestMethod(Http::METHOD_GET),
 +            .uriStart = 4,
 +            .uriEnd = 4,
 +            .uri = "/",
 +            .versionStart = 6,
 +            .versionEnd = 12,
 +            .version = AnyP::ProtocolVersion()
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    // no version
 +    {
 +        input.append("GET / HTTP/\n", 12);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_DONE,
 +            .status = Http::scHttpVersionNotSupported,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = input.length(),
 +            .methodStart = 0,
 +            .methodEnd = 2,
 +            .method = HttpRequestMethod(Http::METHOD_GET),
 +            .uriStart = 4,
 +            .uriEnd = 4,
 +            .uri = "/",
 +            .versionStart = 6,
 +            .versionEnd = 10,
 +            .version = AnyP::ProtocolVersion()
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    // no major version
 +    {
 +        input.append("GET / HTTP/.1\n", 14);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_DONE,
 +            .status = Http::scHttpVersionNotSupported,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = input.length(),
 +            .methodStart = 0,
 +            .methodEnd = 2,
 +            .method = HttpRequestMethod(Http::METHOD_GET),
 +            .uriStart = 4,
 +            .uriEnd = 4,
 +            .uri = "/",
 +            .versionStart = 6,
 +            .versionEnd = 12,
 +            .version = AnyP::ProtocolVersion()
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    // no version dot
 +    {
 +        input.append("GET / HTTP/11\n", 14);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_DONE,
 +            .status = Http::scHttpVersionNotSupported,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = input.length(),
 +            .methodStart = 0,
 +            .methodEnd = 2,
 +            .method = HttpRequestMethod(Http::METHOD_GET),
 +            .uriStart = 4,
 +            .uriEnd = 4,
 +            .uri = "/",
 +            .versionStart = 6,
 +            .versionEnd = 12,
 +            .version = AnyP::ProtocolVersion()
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    // negative major version (bug 3062)
 +    {
 +        input.append("GET / HTTP/-999999.1\n", 21);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_DONE,
 +            .status = Http::scHttpVersionNotSupported,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = input.length(),
 +            .methodStart = 0,
 +            .methodEnd = 2,
 +            .method = HttpRequestMethod(Http::METHOD_GET),
 +            .uriStart = 4,
 +            .uriEnd = 4,
 +            .uri = "/",
 +            .versionStart = 6,
 +            .versionEnd = 19,
 +            .version = AnyP::ProtocolVersion()
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    // no minor version
 +    {
 +        input.append("GET / HTTP/1.\n", 14);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_DONE,
 +            .status = Http::scHttpVersionNotSupported,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = input.length(),
 +            .methodStart = 0,
 +            .methodEnd = 2,
 +            .method = HttpRequestMethod(Http::METHOD_GET),
 +            .uriStart = 4,
 +            .uriEnd = 4,
 +            .uri = "/",
 +            .versionStart = 6,
 +            .versionEnd = 12,
 +            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,0)
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    // negative major version (bug 3062 corollary)
 +    {
 +        input.append("GET / HTTP/1.-999999\n", 21);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_DONE,
 +            .status = Http::scHttpVersionNotSupported,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = input.length(),
 +            .methodStart = 0,
 +            .methodEnd = 2,
 +            .method = HttpRequestMethod(Http::METHOD_GET),
 +            .uriStart = 4,
 +            .uriEnd = 4,
 +            .uri = "/",
 +            .versionStart = 6,
 +            .versionEnd = 19,
 +            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,0)
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +}
 +
 +void
 +testHttp1Parser::testParseRequestLineStrange()
 +{
 +    // ensure MemPools etc exist
 +    globalSetup();
 +
 +    SBuf input;
 +    Http1::RequestParser output;
 +
 +    // space padded URL
 +    {
 +        input.append("GET  /     HTTP/1.1\r\n", 21);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = true,
 +            .parserState = Http1::HTTP_PARSE_MIME,
 +            .status = Http::scOkay,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = 0,
 +            .methodStart = 0,
 +            .methodEnd = 2,
 +            .method = HttpRequestMethod(Http::METHOD_GET),
 +            .uriStart = 5,
 +            .uriEnd = 5,
 +            .uri = "/",
 +            .versionStart = 11,
 +            .versionEnd = 18,
 +            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    // whitespace inside URI. (nasty but happens)
 +    // XXX: depends on tolerant parser...
 +    {
 +        input.append("GET /fo o/ HTTP/1.1\n", 20);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = true,
 +            .parserState = Http1::HTTP_PARSE_MIME,
 +            .status = Http::scOkay,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = 0,
 +            .methodStart = 0,
 +            .methodEnd = 2,
 +            .method = HttpRequestMethod(Http::METHOD_GET),
 +            .uriStart = 4,
 +            .uriEnd = 9,
 +            .uri = "/fo o/",
 +            .versionStart = 11,
 +            .versionEnd = 18,
 +            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    // additional data in buffer
 +    {
 +        input.append("GET /     HTTP/1.1\nboo!", 23);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = true,
 +            .parserState = Http1::HTTP_PARSE_MIME,
 +            .status = Http::scOkay,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-5,
 +            .suffixSz = 4, // strlen("boo!")
 +            .methodStart = 0,
 +            .methodEnd = 2,
 +            .method = HttpRequestMethod(Http::METHOD_GET),
 +            .uriStart = 4,
 +            .uriEnd = 4,
 +            .uri = "/",
 +            .versionStart = 10,
 +            .versionEnd = 17,
 +            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +}
 +
 +void
 +testHttp1Parser::testParseRequestLineTerminators()
 +{
 +    // ensure MemPools etc exist
 +    globalSetup();
 +
 +    SBuf input;
 +    Http1::RequestParser output;
 +
 +    // alternative EOL sequence: NL-only
 +    {
 +        input.append("GET / HTTP/1.1\n", 15);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = true,
 +            .parserState = Http1::HTTP_PARSE_MIME,
 +            .status = Http::scOkay,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = 0,
 +            .methodStart = 0,
 +            .methodEnd = 2,
 +            .method = HttpRequestMethod(Http::METHOD_GET),
 +            .uriStart = 4,
 +            .uriEnd = 4,
 +            .uri = "/",
 +            .versionStart = 6,
 +            .versionEnd = 13,
 +            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    // alternative EOL sequence: double-NL-only
 +    {
 +        input.append("GET / HTTP/1.1\n\n", 16);
 +        struct resultSet expect = {
 +            .parsed = true,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_DONE,
 +            .status = Http::scOkay,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-2,
 +            .suffixSz = 0,
 +            .methodStart = 0,
 +            .methodEnd = 2,
 +            .method = HttpRequestMethod(Http::METHOD_GET),
 +            .uriStart = 4,
 +            .uriEnd = 4,
 +            .uri = "/",
 +            .versionStart = 6,
 +            .versionEnd = 13,
 +            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    // alternative EOL sequence: multi-CR-NL
 +    {
 +        input.append("GET / HTTP/1.1\r\r\r\n", 18);
 +        // Being tolerant we can ignore and elide these apparently benign CR
 +        Config.onoff.relaxed_header_parser = 1;
 +        struct resultSet expectRelaxed = {
 +            .parsed = false,
 +            .needsMore = true,
 +            .parserState = Http1::HTTP_PARSE_MIME,
 +            .status = Http::scOkay,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = 0,
 +            .methodStart = 0,
 +            .methodEnd = 2,
 +            .method = HttpRequestMethod(Http::METHOD_GET),
 +            .uriStart = 4,
 +            .uriEnd = 4,
 +            .uri = "/",
 +            .versionStart = 6,
 +            .versionEnd = 13,
 +            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expectRelaxed);
 +
 +        // strict mode treats these as several bare-CR in the request line which is explicitly invalid.
 +        Config.onoff.relaxed_header_parser = 0;
 +        struct resultSet expectStrict = {
 +            .parsed = false,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_MIME,
 +            .status = Http::scBadRequest,
 +            .msgStart = 0,
 +            .msgEnd = -1,
 +            .suffixSz = input.length(),
 +            .methodStart =-1,
 +            .methodEnd = -1,
 +            .method = HttpRequestMethod(),
 +            .uriStart = -1,
 +            .uriEnd = -1,
 +            .uri = NULL,
 +            .versionStart = -1,
 +            .versionEnd = -1,
 +            .version = AnyP::ProtocolVersion()
 +        };
 +        output.clear();
 +        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);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_DONE,
 +            .status = Http::scBadRequest,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = input.length(),
 +            .methodStart = 0,
 +            .methodEnd = 2,
 +            .method = HttpRequestMethod(Http::METHOD_GET),
 +            .uriStart = 4,
 +            .uriEnd = 13,
 +            .uri = "/ HTTP/1.1",
 +            .versionStart = -1,
 +            .versionEnd = -1,
 +            .version = AnyP::ProtocolVersion()
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +}
 +
 +void
 +testHttp1Parser::testParseRequestLineMethods()
 +{
 +    // ensure MemPools etc exist
 +    globalSetup();
 +
 +    SBuf input;
 +    Http1::RequestParser output;
 +
 +    // RFC 2616 : . method
 +    {
 +        input.append(". / HTTP/1.1\n", 13);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = true,
 +            .parserState = Http1::HTTP_PARSE_MIME,
 +            .status = Http::scOkay,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = 0,
 +            .methodStart = 0,
 +            .methodEnd = 0,
 +            .method = HttpRequestMethod("."),
 +            .uriStart = 2,
 +            .uriEnd = 2,
 +            .uri = "/",
 +            .versionStart = 4,
 +            .versionEnd = 11,
 +            .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);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = true,
 +            .parserState = Http1::HTTP_PARSE_MIME,
 +            .status = Http::scOkay,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = 0,
 +            .methodStart = 0,
 +            .methodEnd = 6,
 +            .method = HttpRequestMethod(Http::METHOD_OPTIONS),
 +            .uriStart = 8,
 +            .uriEnd = 8,
 +            .uri = "*",
 +            .versionStart = 10,
 +            .versionEnd = 17,
 +            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    // unknown method
 +    {
 +        input.append("HELLOWORLD / HTTP/1.1\n", 22);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = true,
 +            .parserState = Http1::HTTP_PARSE_MIME,
 +            .status = Http::scOkay,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = 0,
 +            .methodStart = 0,
 +            .methodEnd = 9,
 +            .method = HttpRequestMethod("HELLOWORLD"),
 +            .uriStart = 11,
 +            .uriEnd = 11,
 +            .uri = "/",
 +            .versionStart = 13,
 +            .versionEnd = 20,
 +            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    // method-only
 +    {
 +        input.append("A\n", 2);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_DONE,
 +            .status = Http::scBadRequest,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = input.length(),
 +            .methodStart = 0,
 +            .methodEnd = -1,
 +            .method = HttpRequestMethod(),
 +            .uriStart = -1,
 +            .uriEnd = -1,
 +            .uri = NULL,
 +            .versionStart = -1,
 +            .versionEnd = -1,
 +            .version = AnyP::ProtocolVersion()
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    {
 +        input.append("GET\n", 4);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_DONE,
 +            .status = Http::scBadRequest,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = input.length(),
 +            .methodStart = 0,
 +            .methodEnd = -1,
 +            .method = HttpRequestMethod(),
 +            .uriStart = -1,
 +            .uriEnd = -1,
 +            .uri = NULL,
 +            .versionStart = -1,
 +            .versionEnd = -1,
 +            .version = AnyP::ProtocolVersion()
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    // space padded method (in strict mode SP is reserved so invalid as a method byte)
 +    {
 +        input.append(" GET / HTTP/1.1\n", 16);
 +        // RELAXED mode Squid custom tolerance ignores SP
 +#if USE_HTTP_VIOLATIONS
 +        Config.onoff.relaxed_header_parser = 1;
 +        struct resultSet expectRelaxed = {
 +            .parsed = false,
 +            .needsMore = true,
 +            .parserState = Http1::HTTP_PARSE_MIME,
 +            .status = Http::scOkay,
 +            .msgStart = 0, // garbage collection consumes the SP
 +            .msgEnd = (int)input.length()-2,
 +            .suffixSz = 0,
 +            .methodStart = 0,
 +            .methodEnd = 2,
 +            .method = HttpRequestMethod(Http::METHOD_GET),
 +            .uriStart = 4,
 +            .uriEnd = 4,
 +            .uri = "/",
 +            .versionStart = 6,
 +            .versionEnd = 13,
 +            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expectRelaxed);
 +#endif
 +
 +        // STRICT mode obeys RFC syntax
 +        Config.onoff.relaxed_header_parser = 0;
 +        struct resultSet expectStrict = {
 +            .parsed = false,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_DONE,
 +            .status = Http::scBadRequest,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = input.length(),
 +            .methodStart = 0,
 +            .methodEnd = -1,
 +            .method = HttpRequestMethod(),
 +            .uriStart = -1,
 +            .uriEnd = -1,
 +            .uri = NULL,
 +            .versionStart = -1,
 +            .versionEnd = -1,
 +            .version = AnyP::ProtocolVersion()
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expectStrict);
 +        input.clear();
 +    }
 +
 +    // RFC 2616 defined tolerance: ignore empty line(s) prefix on messages
 +#if WHEN_RFC_COMPLIANT
 +    {
 +        input.append("\r\n\r\n\nGET / HTTP/1.1\r\n", 21);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_MIME,
 +            .status = Http::scOkay,
 +            .msgStart = 5,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = 5,
 +            .methodStart = 0,
 +            .methodEnd = 2,
 +            .method = HttpRequestMethod(Http::METHOD_GET),
 +            .uriStart = 4,
 +            .uriEnd = 4,
 +            .uri = "/",
 +            .versionStart = 6,
 +            .versionEnd = 13,
 +            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +#endif
 +
 +    // tab padded method (NP: tab is not SP so treated as any other binary)
 +    {
 +        input.append("\tGET / HTTP/1.1\n", 16);
 +#if WHEN_RFC_COMPLIANT
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_DONE,
 +            .status = Http::scBadRequest,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = input.length(),
 +            .methodStart = -1,
 +            .methodEnd = -1,
 +            .method = HttpRequestMethod(),
 +            .uriStart = -1,
 +            .uriEnd = -1,
 +            .uri = NULL,
 +            .versionStart = -1,
 +            .versionEnd = -1,
 +            .version = AnyP::ProtocolVersion()
 +        };
 +#else // XXX: currently broken
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = true,
 +            .parserState = Http1::HTTP_PARSE_MIME,
 +            .status = Http::scOkay,
 +            .msgStart = 0, // garbage collection consumes the SP
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = 0,
 +            .methodStart = 0,
 +            .methodEnd = 3,
 +            .method = HttpRequestMethod(SBuf("\tGET")),
 +            .uriStart = 5,
 +            .uriEnd = 5,
 +            .uri = "/",
 +            .versionStart = 7,
 +            .versionEnd = 14,
 +            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
 +        };
 +#endif
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +}
 +
 +void
 +testHttp1Parser::testParseRequestLineInvalid()
 +{
 +    // ensure MemPools etc exist
 +    globalSetup();
 +
 +    SBuf input;
 +    Http1::RequestParser output;
 +
 +    // no method (but in a form which is ambiguous with HTTP/0.9 simple-request)
 +    {
 +        // XXX: HTTP/0.9 requires method to be "GET"
 +        input.append("/ HTTP/1.0\n", 11);
 +        struct resultSet expect = {
 +            .parsed = true,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_MIME,
 +            .status = Http::scOkay,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = 0,
 +            .methodStart = 0,
 +            .methodEnd = 0,
 +            .method = HttpRequestMethod("/"),
 +            .uriStart = 2,
 +            .uriEnd = 9,
 +            .uri = "HTTP/1.0",
 +            .versionStart = -1,
 +            .versionEnd = -1,
 +            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9)
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    // no method (an invalid format)
 +    {
 +        input.append(" / HTTP/1.0\n", 12);
 +
 +        // XXX: squid custom tolerance consumes initial SP.
 +        Config.onoff.relaxed_header_parser = 1;
 +        struct resultSet expectRelaxed = {
 +            .parsed = true,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_MIME,
 +            .status = Http::scOkay,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-2,
 +            .suffixSz = 0,
 +            .methodStart = 0,
 +            .methodEnd = 0,
 +            .method = HttpRequestMethod("/"),
 +            .uriStart = 2,
 +            .uriEnd = 9,
 +            .uri = "HTTP/1.0",
 +            .versionStart = -1,
 +            .versionEnd = -1,
 +            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9)
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expectRelaxed);
 +
 +        // STRICT detect as invalid
 +        Config.onoff.relaxed_header_parser = 0;
 +#if WHEN_RFC_COMPLIANT
 +        // XXX: except Squid does not
 +        struct resultSet expectStrict = {
 +            .parsed = false,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_DONE,
 +            .status = Http::scBadRequest,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = 0,
 +            .methodStart = 0,
 +            .methodEnd = -1,
 +            .method = HttpRequestMethod(),
 +            .uriStart = -1,
 +            .uriEnd = -1,
 +            .uri = NULL,
 +            .versionStart = -1,
 +            .versionEnd = -1,
 +            .version = AnyP::ProtocolVersion()
 +        };
 +#else
 +        struct resultSet expectStrict = {
 +            .parsed = false,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_DONE,
 +            .status = Http::scBadRequest,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = input.length(),
 +            .methodStart = 0,
 +            .methodEnd = -1,
 +            .method = HttpRequestMethod(),
 +            .uriStart = -1,
 +            .uriEnd = -1,
 +            .uri = NULL,
 +            .versionStart = -1,
 +            .versionEnd = -1,
 +            .version = AnyP::ProtocolVersion()
 +        };
 +#endif
 +        output.clear();
 +        testResults(__LINE__, input, output, expectStrict);
 +        input.clear();
 +    }
 +
 +    // binary code in method (invalid)
 +    {
 +        input.append("GET\x0B / HTTP/1.1\n", 16);
 +#if WHEN_RFC_COMPLIANT
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_DONE,
 +            .status = Http::scBadRequest,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = input.length(),
 +            .methodStart = -1,
 +            .methodEnd = -1,
 +            .method = HttpRequestMethod(),
 +            .uriStart = -1,
 +            .uriEnd = -1,
 +            .uri = NULL,
 +            .versionStart = -1,
 +            .versionEnd = -1,
 +            .version = AnyP::ProtocolVersion()
 +        };
 +#else
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = true,
 +            .parserState = Http1::HTTP_PARSE_MIME,
 +            .status = Http::scOkay,
 +            .msgStart = 0, // garbage collection consumes the SP
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = 0,
 +            .methodStart = 0,
 +            .methodEnd = 3,
 +            .method = HttpRequestMethod(SBuf("GET\x0B")),
 +            .uriStart = 5,
 +            .uriEnd = 5,
 +            .uri = "/",
 +            .versionStart = 7,
 +            .versionEnd = 14,
 +            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
 +        };
 +#endif
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    // CR in method
 +    {
 +        // RFC 2616 sec 5.1 prohibits CR other than in terminator.
 +        input.append("GET\r / HTTP/1.1\r\n", 16);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_DONE,
 +            .status = Http::scBadRequest,
 +            .msgStart = 0,
 +            .msgEnd = -1, // halt at the first \r
 +            .suffixSz = input.length(),
 +            .methodStart = -1,
 +            .methodEnd = -1,
 +            .method = HttpRequestMethod(),
 +            .uriStart = -1,
 +            .uriEnd = -1,
 +            .uri = NULL,
 +            .versionStart = -1,
 +            .versionEnd = -1,
 +            .version = AnyP::ProtocolVersion()
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    // binary code NUL! in method (strange but ...)
 +    {
 +        input.append("GET\0 / HTTP/1.1\n", 16);
 +#if WHEN_RFC_COMPLIANT
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_DONE,
 +            .status = Http::scBadRequest,
 +            .msgStart = 0,
 +            .msgEnd = -1, // halt at the \0
 +            .suffixSz = input.length(),
 +            .methodStart = -1,
 +            .methodEnd = -1,
 +            .method = HttpRequestMethod(),
 +            .uriStart = -1,
 +            .uriEnd = -1,
 +            .uri = NULL,
 +            .versionStart = -1,
 +            .versionEnd = -1,
 +            .version = AnyP::ProtocolVersion()
 +        };
 +#else
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = true,
 +            .parserState = Http1::HTTP_PARSE_MIME,
 +            .status = Http::scOkay,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = 0,
 +            .methodStart = 0,
 +            .methodEnd = 3,
 +            .method = HttpRequestMethod(SBuf("GET\0",4)),
 +            .uriStart = 5,
 +            .uriEnd = 5,
 +            .uri = "/",
 +            .versionStart = 7,
 +            .versionEnd = 14,
 +            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1)
 +        };
 +#endif
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    // no URL (grammer invalid, ambiguous with RFC 1945 HTTP/0.9 simple-request)
 +    {
 +        input.append("GET  HTTP/1.1\n", 14);
 +        struct resultSet expect = {
 +            .parsed = true,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_DONE,
 +            .status = Http::scOkay,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = 0,
 +            .methodStart = 0,
 +            .methodEnd = 2,
 +            .method = HttpRequestMethod(Http::METHOD_GET),
 +            .uriStart = 5,
 +            .uriEnd = 12,
 +            .uri = "HTTP/1.1",
 +            .versionStart = -1,
 +            .versionEnd = -1,
 +            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9)
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    // no URL (grammer invalid, ambiguous with RFC 1945 HTTP/0.9 simple-request)
 +    {
 +        input.append("GET HTTP/1.1\n", 13);
 +        struct resultSet expect = {
 +            .parsed = true,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_DONE,
 +            .status = Http::scOkay,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = 0,
 +            .methodStart = 0,
 +            .methodEnd = 2,
 +            .method = HttpRequestMethod(Http::METHOD_GET),
 +            .uriStart = 4,
 +            .uriEnd = 11,
 +            .uri = "HTTP/1.1",
 +            .versionStart = -1,
 +            .versionEnd = -1,
 +            .version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,0,9)
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    // binary line
 +    {
 +        input.append("\xB\xC\xE\xF\n", 5);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_DONE,
 +            .status = Http::scBadRequest,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = input.length(),
 +            .methodStart = 0,
 +            .methodEnd = -1,
 +            .method = HttpRequestMethod(),
 +            .uriStart = -1,
 +            .uriEnd = -1,
 +            .uri = NULL,
 +            .versionStart = -1,
 +            .versionEnd = -1,
 +            .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);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_DONE,
 +            .status = Http::scBadRequest,
 +            .msgStart = 0,
 +            .msgEnd = (int)input.length()-1,
 +            .suffixSz = input.length(),
 +            .methodStart = 0,
 +            .methodEnd = 0,
 +            .method = HttpRequestMethod(SBuf("\t")),
 +            .uriStart = -1,
 +            .uriEnd = -1,
 +            .uri = NULL,
 +            .versionStart = -1,
 +            .versionEnd = -1,
 +            .version = AnyP::ProtocolVersion()
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +
 +    // mixed whitespace line with CR middle
 +    {
 +        // 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);
 +        struct resultSet expect = {
 +            .parsed = false,
 +            .needsMore = false,
 +            .parserState = Http1::HTTP_PARSE_DONE,
 +            .status = Http::scBadRequest,
 +            .msgStart = 0,
 +            .msgEnd = -1, // halt on the \r
 +            .suffixSz = input.length(),
 +            .methodStart = -1,
 +            .methodEnd = -1,
 +            .method = HttpRequestMethod(),
 +            .uriStart = -1,
 +            .uriEnd = -1,
 +            .uri = NULL,
 +            .versionStart = -1,
 +            .versionEnd = -1,
 +            .version = AnyP::ProtocolVersion()
 +        };
 +        output.clear();
 +        testResults(__LINE__, input, output, expect);
 +        input.clear();
 +    }
 +}
 +
 +void
 +testHttp1Parser::testDripFeed()
 +{
 +    // Simulate a client drip-feeding Squid a few bytes at a time.
 +    // extend the size of the buffer from 0 bytes to full request length
 +    // calling the parser repeatedly as visible data grows.
 +
 +    SBuf data;
 +    data.append("            ", 12);
 +    SBuf::size_type garbageEnd = data.length();
 +    data.append("GET http://example.com/ HTTP/1.1\r\n", 34);
 +    SBuf::size_type reqLineEnd = data.length() - 1;
 +    data.append("Host: example.com\r\n\r\n", 21);
 +    SBuf::size_type mimeEnd = data.length() - 1;
 +    data.append("...", 3); // trailer to catch mime EOS errors.
 +
 +    SBuf ioBuf; // begins empty
 +    Http1::RequestParser hp;
 +
 +    // only relaxed parser accepts the garbage whitespace
 +    Config.onoff.relaxed_header_parser = 1;
 +
 +    // state of things we expect right now
 +    struct resultSet expect = {
 +        .parsed = false,
 +        .needsMore = true,
 +        .parserState = Http1::HTTP_PARSE_NONE,
 +        .status = Http::scNone,
 +        .msgStart = -1,
 +        .msgEnd = -1,
 +        .suffixSz = 0,
 +        .methodStart = -1,
 +        .methodEnd = -1,
 +        .method = HttpRequestMethod(),
 +        .uriStart = -1,
 +        .uriEnd = -1,
 +        .uri = NULL,
 +        .versionStart = -1,
 +        .versionEnd = -1,
 +        .version = AnyP::ProtocolVersion()
 +    };
 +
 +    Config.maxRequestHeaderSize = 1024; // large enough to hold the test data.
 +
 +    for (SBuf::size_type pos = 0; pos <= data.length(); ++pos) {
 +
 +        // simulate reading one more byte
 +        ioBuf.append(data.substr(pos,1));
 +
 +        // when the garbage is passed we expect to start seeing first-line bytes
 +        if (pos == garbageEnd) {
 +            expect.parserState = Http1::HTTP_PARSE_FIRST;
 +            expect.msgStart = 0;
 +        }
 +
 +        // all points after garbage start to see accumulated bytes looking for end of current section
 +        if (pos >= garbageEnd)
 +            expect.suffixSz = ioBuf.length();
 +
 +        // at end of request line expect to see method, URI, version details
 +        // and switch to seeking Mime header section
 +        if (pos == reqLineEnd) {
 +            expect.parserState = Http1::HTTP_PARSE_MIME;
 +            expect.suffixSz = 0;
 +            expect.msgEnd = reqLineEnd-garbageEnd;
 +            expect.status = Http::scOkay;
 +            expect.methodStart = 0;
 +            expect.methodEnd = 2;
 +            expect.method = HttpRequestMethod(Http::METHOD_GET);
 +            expect.uriStart = 4;
 +            expect.uriEnd = 22;
 +            expect.uri = "http://example.com/";
 +            expect.versionStart = 24;
 +            expect.versionEnd = 31;
 +            expect.version = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1);
 +        }
 +
 +        // one mime header is done we are expectign a new request
 +        // parse results say true and initial data is all gone from the buffer
 +        if (pos == mimeEnd) {
 +            expect.parsed = true;
 +            expect.needsMore = false;
 +            expect.suffixSz = 0;
 +        }
 +
 +        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;
 +    }
 +}
index fae1ff4e2ef4b874bb95dbd5e3233cea6a0dcc4a,9afbd84437b2a5ecfc5f189c9252e755927dfa84..f8448272317de14e6b1aae4147cc1c9e477bd1b2
@@@ -1,5 -1,13 +1,13 @@@
 -#ifndef SQUID_SRC_TESTS_TESTHTTPPARSER_H
 -#define SQUID_SRC_TESTS_TESTHTTPPARSER_H
+ /*
+  * Copyright (C) 1996-2014 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.
+  */
 +#ifndef SQUID_SRC_TESTS_TESTHTTP1PARSER_H
 +#define SQUID_SRC_TESTS_TESTHTTP1PARSER_H
  
  #include <cppunit/extensions/HelperMacros.h>
  
Simple merge