]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Do not assert on native FTP ERR_TOO_BIG. Do not check for ERR_TOO_BIG twice.
authorAlex Rousskov <rousskov@measurement-factory.com>
Tue, 19 Aug 2014 18:09:50 +0000 (12:09 -0600)
committerAlex Rousskov <rousskov@measurement-factory.com>
Tue, 19 Aug 2014 18:09:50 +0000 (12:09 -0600)
The assertion occurred because both the FTP request parser and the generic
ConnStateData::checkHeaderLimits() code would try to write their own error
message to the user. Reworked all error reporting code in the FTP parser to
avoid writing early responses (that were bypassing the overall transaction
flow with various negative side effects such as lack of logging).

Removed ConnStateData::checkHeaderLimits(): We already have protocol-specific
checks for huge HTTP and FTP requests. There is no point in duplicating them.
Centralizing them sounds like a good idea, but a general checkHeaderLimits()
cannot produce protocol-specific errors messages that we need, so it hurts
more than it helps. Moreover, checkHeaderLimits() was handling errors
differently than protocol parsing code, making the code more complex overall.

All that remains from the checkHeaderLimits() code now is a single Must(),
checking that the protocol parsers did what they were supposed to do: Return
NULL to request more data after checking any applicable limits. If parsers do
not (a Squid bug!), the ConnStateData job gets killed (and connection gets
closed) as the last resort.

Added clientReplyContext::setReplyToReply() and
StoreEntry::storeErrorResponse() to handle storing of a response to an FTP
command parsing error. The old code was using ErrorState to store parsing
errors, but ErrorState is still HTTP-specific and cannot relay the right FTP
codes/reasons to the user. The setReplyToReply() sounds silly but it matches
the existing setReplyTo*() naming scheme well.

Make sure parsed native FTP command tokens are not even close to the String
buffer limit. These checks are not a firm guarantee, but are better than
nothing until we replace String.

Handle ClientSocketContext registration centrally because all parsers need it.

Call quitAfterError() on fatal native FTP errors. Probably not necessary due
to fssError handling code that closes the FTP control connection, but adds
helpful debugging and brings us closer to the HTTP error handling code.

Described ConnStateData::clientParseRequests().

src/Store.h
src/client_side.cc
src/client_side.h
src/client_side_reply.cc
src/client_side_reply.h
src/errorpage.cc
src/servers/FtpServer.cc
src/servers/FtpServer.h
src/servers/HttpServer.cc
src/store.cc
src/tests/stub_client_side.cc

index f30d83a9f8e41f6252d8387d6fb5080d90636096..f3b669647334209549f4853f20348764076558e2 100644 (file)
@@ -97,6 +97,8 @@ public:
     virtual void complete();
     virtual store_client_t storeClientType() const;
     virtual char const *getSerialisedMetaData();
+    /// Store a prepared error response. MemObject locks the reply object.
+    void storeErrorResponse(HttpReply *reply);
     void replaceHttpReply(HttpReply *, bool andStartWriting = true);
     void startWriting(); ///< pack and write reply headers and, maybe, body
     /// whether we may start writing to disk (now or in the future)
index d8966a220e7384796eb036c43a761139eac1052f..b801ed6d06b57804b8642e343e67858c3b72442b 100644 (file)
@@ -197,7 +197,6 @@ static IOACB httpAccept;
 static IOACB httpsAccept;
 #endif
 static CTCB clientLifetimeTimeout;
-static ClientSocketContext *parseHttpRequestAbort(ConnStateData * conn, const char *uri);
 #if USE_IDENT
 static IDCB clientIdentDone;
 #endif
@@ -1906,17 +1905,15 @@ ClientSocketContext::writeComplete(const Comm::ConnectionPointer &conn, char *bu
     }
 }
 
-static ClientSocketContext *
-parseHttpRequestAbort(ConnStateData * csd, const char *uri)
+ClientSocketContext *
+ConnStateData::abortRequestParsing(const char *const uri)
 {
-    ClientHttpRequest *http;
-    ClientSocketContext *context;
-    StoreIOBuffer tempBuffer;
-    http = new ClientHttpRequest(csd);
-    http->req_sz = csd->in.buf.length();
+    ClientHttpRequest *http = new ClientHttpRequest(this);
+    http->req_sz = in.buf.length();
     http->uri = xstrdup(uri);
     setLogUri (http, uri);
-    context = new ClientSocketContext(csd->clientConnection, http);
+    ClientSocketContext *context = new ClientSocketContext(clientConnection, http);
+    StoreIOBuffer tempBuffer;
     tempBuffer.data = context->reqbuf;
     tempBuffer.length = HTTP_REQBUF_SZ;
     clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
@@ -2041,7 +2038,7 @@ prepareAcceleratedURL(ConnStateData * conn, ClientHttpRequest *http, char *url,
 
         if (!url) {
             hp->request_parse_status = Http::scBadRequest;
-            return parseHttpRequestAbort(conn, "error:invalid-request");
+            return conn->abortRequestParsing("error:invalid-request");
         }
 #endif
 
@@ -2169,7 +2166,7 @@ parseHttpRequest(ConnStateData *csd, HttpParser *hp, HttpRequestMethod * method_
     } else if ( (size_t)hp->bufsiz >= Config.maxRequestHeaderSize && headersEnd(hp->buf, Config.maxRequestHeaderSize) == 0) {
         debugs(33, 5, "parseHttpRequest: Too large request");
         hp->request_parse_status = Http::scHeaderTooLarge;
-        return parseHttpRequestAbort(csd, "error:request-too-large");
+        return csd->abortRequestParsing("error:request-too-large");
     }
 
     /* Attempt to parse the first line; this'll define the method, url, version and header begin */
@@ -2181,7 +2178,7 @@ parseHttpRequest(ConnStateData *csd, HttpParser *hp, HttpRequestMethod * method_
     }
 
     if (r == -1) {
-        return parseHttpRequestAbort(csd, "error:invalid-request");
+        return csd->abortRequestParsing("error:invalid-request");
     }
 
     /* Request line is valid here .. */
@@ -2214,7 +2211,7 @@ parseHttpRequest(ConnStateData *csd, HttpParser *hp, HttpRequestMethod * method_
     if (req_sz >= Config.maxRequestHeaderSize) {
         debugs(33, 5, "parseHttpRequest: Too large request");
         hp->request_parse_status = Http::scHeaderTooLarge;
-        return parseHttpRequestAbort(csd, "error:request-too-large");
+        return csd->abortRequestParsing("error:request-too-large");
     }
 
     /* Set method_p */
@@ -2226,14 +2223,14 @@ parseHttpRequest(ConnStateData *csd, HttpParser *hp, HttpRequestMethod * method_
         /* XXX need a way to say "this many character length string" */
         debugs(33, DBG_IMPORTANT, "WARNING: for request: " << hp->buf);
         hp->request_parse_status = Http::scMethodNotAllowed;
-        return parseHttpRequestAbort(csd, "error:method-not-allowed");
+        return csd->abortRequestParsing("error:method-not-allowed");
     }
 
     if (*method_p == Http::METHOD_NONE) {
         /* XXX need a way to say "this many character length string" */
         debugs(33, DBG_IMPORTANT, "clientParseRequestMethod: Unsupported method in request '" << hp->buf << "'");
         hp->request_parse_status = Http::scMethodNotAllowed;
-        return parseHttpRequestAbort(csd, "error:unsupported-request-method");
+        return csd->abortRequestParsing("error:unsupported-request-method");
     }
 
     /*
@@ -2415,27 +2412,6 @@ connNoteUseOfBuffer(ConnStateData* conn, size_t byteCount)
     conn->consumeInput(byteCount);
 }
 
-/// respond with ERR_TOO_BIG if request header exceeds request_header_max_size
-void
-ConnStateData::checkHeaderLimits()
-{
-    if (in.buf.length() < Config.maxRequestHeaderSize)
-        return; // can accumulte more header data
-
-    debugs(33, 3, "Request header is too large (" << in.buf.length() << " > " <<
-           Config.maxRequestHeaderSize << " bytes)");
-
-    ClientSocketContext *context = parseHttpRequestAbort(this, "error:request-too-large");
-    clientStreamNode *node = context->getClientReplyContext();
-    clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
-    assert (repContext);
-    repContext->setReplyToError(ERR_TOO_BIG,
-                                Http::scBadRequest, Http::METHOD_NONE, NULL,
-                                clientConnection->remote, NULL, NULL, NULL);
-    context->registerWithConn();
-    context->pullData();
-}
-
 void
 ConnStateData::clientAfterReadingRequests()
 {
@@ -2896,8 +2872,8 @@ ConnStateData::concurrentRequestQueueFilled() const
 
 /**
  * Attempt to parse one or more requests from the input buffer.
- * If a request is successfully parsed, even if the next request
- * is only partially parsed, it will return TRUE.
+ * Returns true after completing parsing of at least one request [header]. That
+ * includes cases where parsing ended with an error (e.g., a huge request).
  */
 bool
 ConnStateData::clientParseRequests()
@@ -2920,23 +2896,14 @@ ConnStateData::clientParseRequests()
             break;
 
         Http::ProtocolVersion http_ver;
-        ClientSocketContext *context = parseOneRequest(http_ver);
-
-        /* partial or incomplete request */
-        if (!context) {
-            // TODO: why parseHttpRequest can just return parseHttpRequestAbort
-            // (which becomes context) but checkHeaderLimits cannot?
-            checkHeaderLimits();
-            break;
-        }
-
-        /* status -1 or 1 */
-        if (context) {
-            debugs(33, 5, HERE << clientConnection << ": parsed a request");
+        if (ClientSocketContext *context = parseOneRequest(http_ver)) {
+            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);
 
+            context->registerWithConn();
+
             processParsedRequest(context, http_ver);
 
             parsed_req = true; // XXX: do we really need to parse everything right NOW ?
@@ -2945,6 +2912,11 @@ ConnStateData::clientParseRequests()
                 debugs(33, 3, HERE << "Not parsing new requests, as this request may need the connection");
                 break;
             }
+        } else {
+            debugs(33, 5, clientConnection << ": not enough request data: " <<
+                   in.buf.length() << " < " << Config.maxRequestHeaderSize);
+            Must(in.buf.length() < Config.maxRequestHeaderSize);
+            break;
         }
     }
 
index 238c684cc1713accf989a7e4f0f4c180b60c7bf0..45f257b961ae0527dffdf9a9d03187c562278532 100644 (file)
@@ -209,7 +209,6 @@ public:
     void addContextToQueue(ClientSocketContext * context);
     int getConcurrentRequestCount() const;
     bool isOpen() const;
-    void checkHeaderLimits();
 
     // HttpControlMsgSink API
     virtual void sendControlMsg(HttpControlMsg msg);
@@ -407,6 +406,11 @@ public:
     /// remove no longer needed leading bytes from the input buffer
     void consumeInput(const size_t byteCount);
 
+    /* TODO: Make the methods below (at least) non-public when possible. */
+
+    /// stop parsing the request and create context for relaying error info
+    ClientSocketContext *abortRequestParsing(const char *const errUri);
+
 protected:
     void startDechunkingRequest();
     void finishDechunkingRequest(bool withSuccess);
@@ -417,6 +421,8 @@ protected:
     void clientPinnedConnectionRead(const CommIoCbParams &io);
 
     /// parse input buffer prefix into a single transfer protocol request
+    /// return NULL to request more header bytes (after checking any limits)
+    /// use abortRequestParsing() to handle parsing errors w/o creating request
     virtual ClientSocketContext *parseOneRequest(Http::ProtocolVersion &ver) = 0;
 
     /// start processing a freshly parsed request
index 6cddeae4f8b939fcb34642a4b20d31a9c1550156..fbbaf7997dc156723ca22b3ad152da7062ce709f 100644 (file)
@@ -141,6 +141,24 @@ void clientReplyContext::setReplyToError(const HttpRequestMethod& method, ErrorS
     /* Now the caller reads to get this */
 }
 
+void
+clientReplyContext::setReplyToReply(HttpReply *futureReply)
+{
+    Must(futureReply);
+    http->al->http.code = futureReply->sline.status();
+
+    HttpRequestMethod method;
+    if (http->request) { // nil on responses to unparsable requests
+        http->request->ignoreRange("responding with a Squid-generated reply");
+        method = http->request->method;
+    }
+
+    createStoreEntry(method, RequestFlags());
+
+    http->storeEntry()->storeErrorResponse(futureReply);
+    /* Now the caller reads to get futureReply */
+}
+
 // Assumes that the entry contains an error response without Content-Range.
 // To use with regular entries, make HTTP Range header removal conditional.
 void clientReplyContext::setReplyToStoreEntry(StoreEntry *entry, const char *reason)
index e83ae20f267561da52bbdf725f44d3040357d94a..33e4149824305058e02dddf9bd5de0645b1eb189 100644 (file)
@@ -79,6 +79,8 @@ public:
 #endif
     /// creates a store entry for the reply and appends err to it
     void setReplyToError(const HttpRequestMethod& method, ErrorState *err);
+    /// creates a store entry for the reply and appends error reply to it
+    void setReplyToReply(HttpReply *reply);
     void createStoreEntry(const HttpRequestMethod& m, RequestFlags flags);
     void removeStoreReference(store_client ** scp, StoreEntry ** ep);
     void removeClientStoreReference(store_client **scp, ClientHttpRequest *http);
index 44d6d18d8fbde66a2b6dec95db1800a6938b8f37..e219f7b00b909b61263adcd53d2fbfbf19a3f640 100644 (file)
@@ -643,14 +643,7 @@ errorAppendEntry(StoreEntry * entry, ErrorState * err)
         }
     }
 
-    entry->lock("errorAppendEntry");
-    entry->buffer();
-    entry->replaceHttpReply( err->BuildHttpReply() );
-    entry->flush();
-    entry->complete();
-    entry->negativeCache();
-    entry->releaseRequest();
-    entry->unlock("errorAppendEntry");
+    entry->storeErrorResponse(err->BuildHttpReply());
     delete err;
 }
 
index 3de0dbea1c2c9ee1768e9dd25db7424cfee455d2..1df6be884d209274dde49cc8fd8b30cc82ae224f 100644 (file)
@@ -121,24 +121,16 @@ Ftp::Server::doProcessRequest()
 
     ClientHttpRequest *const http = context->http;
     assert(http != NULL);
-    HttpRequest *const request = http->request;
-    assert(request != NULL);
-    debugs(33, 9, request);
-
-    HttpHeader &header = request->header;
-    assert(header.has(HDR_FTP_COMMAND));
-    String &cmd = header.findEntry(HDR_FTP_COMMAND)->value;
-    assert(header.has(HDR_FTP_ARGUMENTS));
-    String &params = header.findEntry(HDR_FTP_ARGUMENTS)->value;
 
-    const bool fwd = !http->storeEntry() && handleRequest(cmd, params);
+    HttpRequest *const request = http->request;
+    Must(http->storeEntry() || request);
+    const bool mayForward = !http->storeEntry() && handleRequest(request);
 
     if (http->storeEntry() != NULL) {
         debugs(33, 4, "got an immediate response");
-        assert(http->storeEntry() != NULL);
         clientSetKeepaliveFlag(http);
         context->pullData();
-    } else if (fwd) {
+    } else if (mayForward) {
         debugs(33, 4, "forwarding request to server side");
         assert(http->storeEntry() == NULL);
         clientProcessRequest(this, NULL /*parser*/, context.getRaw(),
@@ -151,6 +143,8 @@ Ftp::Server::doProcessRequest()
 void
 Ftp::Server::processParsedRequest(ClientSocketContext *context, const Http::ProtocolVersion &)
 {
+    Must(getConcurrentRequestCount() == 1);
+
     // Process FTP request asynchronously to make sure FTP
     // data connection accept callback is fired first.
     CallJobHere(33, 4, CbcPointer<Server>(this),
@@ -545,11 +539,81 @@ Ftp::CommandHasPathParameter(const SBuf &cmd)
     return PathedCommands.find(cmd) != PathedCommands.end();
 }
 
+/// creates a context filled with an error message for a given early error
+ClientSocketContext *
+Ftp::Server::earlyError(const EarlyErrorKind eek)
+{
+    /* Default values, to be updated by the switch statement below */
+    int scode = 421;
+    const char *reason = "Internal error";
+    const char *errUri = "error:ftp-internal-early-error";
+
+    switch (eek) {
+    case eekHugeRequest:
+        scode = 421;
+        reason = "Huge request";
+        errUri = "error:ftp-huge-request";
+        break;
+
+    case eekMissingLogin:
+        scode = 530;
+        reason = "Must login first";
+        errUri = "error:ftp-must-login-first";
+        break;
+
+    case eekMissingUsername:
+        scode = 501;
+        reason = "Missing username";
+        errUri = "error:ftp-missing-username";
+        break;
+
+    case eekMissingHost:
+        scode = 501;
+        reason = "Missing host";
+        errUri = "error:ftp-missing-host";
+        break;
+
+    case eekUnsupportedCommand:
+        scode = 502;
+        reason = "Unknown or unsupported command";
+        errUri = "error:ftp-unsupported-command";
+        break;
+
+    case eekInvalidUri:
+        scode = 501;
+        reason = "Invalid URI";
+        errUri = "error:ftp-invalid-uri";
+        break;
+
+    case eekMalformedCommand:
+        scode = 421;
+        reason = "Malformed command";
+        errUri = "error:ftp-malformed-command";
+        break;
+
+    // no default so that a compiler can check that we have covered all cases
+    }
+
+    ClientSocketContext *context = abortRequestParsing(errUri);
+    clientStreamNode *node = context->getClientReplyContext();
+    Must(node);
+    clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
+
+    // We cannot relay FTP scode/reason via HTTP-specific ErrorState.
+    // TODO: When/if ErrorState can handle native FTP errors, use it instead.
+    HttpReply *reply = Ftp::HttpReplyWrapper(scode, reason, Http::scBadRequest, -1);
+    repContext->setReplyToReply(reply);
+    return context;
+}
+
 /// Parses a single FTP request on the control connection.
-/// Returns NULL on errors and incomplete requests.
+/// Returns a new ClientSocketContext on valid requests and all errors.
+/// Returns NULL on incomplete requests that may still succeed given more data.
 ClientSocketContext *
 Ftp::Server::parseOneRequest(Http::ProtocolVersion &ver)
 {
+    flags.readMore = false; // common for all but one case below
+
     // OWS <command> [ RWS <parameter> ] OWS LF
 
     // InlineSpaceChars are isspace(3) or RFC 959 Section 3.1.1.5.2, except
@@ -580,14 +644,26 @@ Ftp::Server::parseOneRequest(Http::ProtocolVersion &ver)
         params.trim(bufWhiteSpace, false, true);
     }
 
+    // Why limit command line and parameters size? Did not we just parse them?
+    // XXX: Our good old String cannot handle very long strings.
+    const SBuf::size_type tokenMax = min(
+        static_cast<SBuf::size_type>(32*1024), // conservative
+        static_cast<SBuf::size_type>(Config.maxRequestHeaderSize));
+    if (cmd.length() > tokenMax || params.length() > tokenMax) {
+        changeState(fssError, "huge req token");
+        quitAfterError(NULL);
+        return earlyError(eekHugeRequest);
+    }
+
     // technically, we may skip multiple NLs below, but that is OK
     if (!parsed || !tok.skipAll(CharacterSet::LF)) { // did not find terminating LF yet
         // we need more data, but can we buffer more?
         if (in.buf.length() >= Config.maxRequestHeaderSize) {
             changeState(fssError, "huge req");
-            writeEarlyReply(421, "Huge request");
-            return NULL;
+            quitAfterError(NULL);
+            return earlyError(eekHugeRequest);
         } else {
+            flags.readMore = true;
             debugs(33, 5, "Waiting for more, up to " <<
                    (Config.maxRequestHeaderSize - in.buf.length()));
             return NULL;
@@ -605,21 +681,19 @@ Ftp::Server::parseOneRequest(Http::ProtocolVersion &ver)
     if (!transparent()) {
         if (!master->clientReadGreeting) {
             // the first command must be USER
-            if (!pinning.pinned && cmd != cmdUser()) {
-                writeEarlyReply(530, "Must login first");
-                return NULL;
-            }
+            if (!pinning.pinned && cmd != cmdUser())
+                return earlyError(eekMissingLogin);
         }
 
         // process USER request now because it sets FTP peer host name
-        if (cmd == cmdUser() && !handleUserRequest(cmd, params))
-            return NULL;
+        if (cmd == cmdUser()) {
+            if (ClientSocketContext *errCtx = handleUserRequest(cmd, params))
+                return errCtx;
+        }
     }
 
-    if (!Ftp::SupportedCommand(cmd)) {
-        writeEarlyReply(502, "Unknown or unsupported command");
-        return NULL;
-    }
+    if (!Ftp::SupportedCommand(cmd))
+        return earlyError(eekUnsupportedCommand);
 
     const HttpRequestMethod method =
         cmd == cmdAppe() || cmd == cmdStor() || cmd == cmdStou() ?
@@ -632,10 +706,9 @@ Ftp::Server::parseOneRequest(Http::ProtocolVersion &ver)
     HttpRequest *const request = HttpRequest::CreateFromUrlAndMethod(newUri, method);
     if (!request) {
         debugs(33, 5, "Invalid FTP URL: " << uri);
-        writeEarlyReply(501, "Invalid host");
         uri.clear();
         safe_free(newUri);
-        return NULL;
+        return earlyError(eekInvalidUri);
     }
 
     ver = Http::ProtocolVersion(Ftp::ProtocolVersion().major, Ftp::ProtocolVersion().minor);
@@ -672,10 +745,7 @@ Ftp::Server::parseOneRequest(Http::ProtocolVersion &ver)
                      clientReplyStatus, newServer, clientSocketRecipient,
                      clientSocketDetach, newClient, tempBuffer);
 
-    Must(!getConcurrentRequestCount());
-    result->registerWithConn();
     result->flags.parsed_ok = 1;
-    flags.readMore = false;
     return result;
 }
 
@@ -1041,6 +1111,7 @@ Ftp::Server::writeErrorReply(const HttpReply *reply, const int scode)
 }
 
 /// writes FTP response based on HTTP reply that is not an FTP-response wrapper
+/// for example, internally-generated Squid "errorpages" end up here (for now)
 void
 Ftp::Server::writeForwardedForeign(const HttpReply *reply)
 {
@@ -1196,11 +1267,17 @@ Ftp::Server::wroteReply(const CommIoCbParams &io)
 }
 
 bool
-Ftp::Server::handleRequest(String &cmd, String &params)
+Ftp::Server::handleRequest(HttpRequest *request)
 {
-    HttpRequest *request = getCurrentContext()->http->request;
+    debugs(33, 9, request);
     Must(request);
 
+    HttpHeader &header = request->header;
+    Must(header.has(HDR_FTP_COMMAND));
+    String &cmd = header.findEntry(HDR_FTP_COMMAND)->value;
+    Must(header.has(HDR_FTP_ARGUMENTS));
+    String &params = header.findEntry(HDR_FTP_ARGUMENTS)->value;
+
     if (do_debug(9, 2)) {
         MemBuf mb;
         Packer p;
@@ -1250,21 +1327,17 @@ Ftp::Server::handleRequest(String &cmd, String &params)
 }
 
 /// Called to parse USER command, which is required to create an HTTP request
-/// wrapper. Thus, errors are handled with writeEarlyReply() here.
-bool
+/// wrapper. W/o request, the errors are handled by returning earlyError().
+ClientSocketContext *
 Ftp::Server::handleUserRequest(const SBuf &cmd, SBuf &params)
 {
-    if (params.isEmpty()) {
-        writeEarlyReply(501, "Missing username");
-        return false;
-    }
+    if (params.isEmpty())
+        return earlyError(eekMissingUsername);
 
     // find the [end of] user name
     const SBuf::size_type eou = params.rfind('@');
-    if (eou == SBuf::npos || eou + 1 >= params.length()) {
-        writeEarlyReply(501, "Missing host");
-        return false;
-    }
+    if (eou == SBuf::npos || eou + 1 >= params.length())
+        return earlyError(eekMissingHost);
 
     // Determine the intended destination.
     host = params.substr(eou + 1, params.length());
@@ -1300,7 +1373,7 @@ Ftp::Server::handleUserRequest(const SBuf &cmd, SBuf &params)
         resetLogin("URI reset");
     }
 
-    return true;
+    return NULL; // no early errors
 }
 
 bool
index 0b30178e7bc0ad423122c84fdb3bdfb43fd0a933..3be163491a9f6ef34b1d2acd2d12ad706bf04e86 100644 (file)
@@ -57,6 +57,17 @@ public:
 protected:
     friend void StartListening();
 
+    // errors detected before it is possible to create an HTTP request wrapper
+    typedef enum {
+        eekHugeRequest,
+        eekMissingLogin,
+        eekMissingUsername,
+        eekMissingHost,
+        eekUnsupportedCommand,
+        eekInvalidUri,
+        eekMalformedCommand
+    } EarlyErrorKind;
+
     /* ConnStateData API */
     virtual ClientSocketContext *parseOneRequest(Http::ProtocolVersion &ver);
     virtual void processParsedRequest(ClientSocketContext *context, const Http::ProtocolVersion &ver);
@@ -89,7 +100,7 @@ protected:
 
     void calcUri(const SBuf *file);
     void changeState(const Ftp::ServerState newState, const char *reason);
-    bool handleUserRequest(const SBuf &cmd, SBuf &params);
+    ClientSocketContext *handleUserRequest(const SBuf &cmd, SBuf &params);
     bool checkDataConnPost() const;
     void replyDataWritingCheckpoint();
     void maybeReadUploadData();
@@ -103,7 +114,8 @@ protected:
     void writeForwardedReplyAndCall(const HttpReply *reply, AsyncCall::Pointer &call);
     void writeReply(MemBuf &mb);
 
-    bool handleRequest(String &cmd, String &params);
+    ClientSocketContext *earlyError(const EarlyErrorKind eek);
+    bool handleRequest(HttpRequest *);
     void setDataCommand();
     bool checkDataConnPre();
 
index 1f3dab3cca0c526427b6e0d5a97d064b44bcb035..3d9a82724fefb7926492c78e4bcd0532c1e0c60b 100644 (file)
@@ -116,9 +116,6 @@ Http::Server::parseOneRequest(Http::ProtocolVersion &ver)
 void
 Http::Server::processParsedRequest(ClientSocketContext *context, const Http::ProtocolVersion &ver)
 {
-    /* We have an initial client stream in place should it be needed */
-    /* setup our private context */
-    context->registerWithConn();
     clientProcessRequest(this, &parser_, context, method_, ver);
 }
 
index a52fe2ad48f978950c06279bb4fa60db5c9b505b..b403c81706a6676faa89c544f713a541ae15a339 100644 (file)
@@ -1846,6 +1846,19 @@ storeSwapFileNumberSet(StoreEntry * e, sfileno filn)
 
 #endif
 
+void
+StoreEntry::storeErrorResponse(HttpReply *reply)
+{
+    lock("StoreEntry::storeErrorResponse");
+    buffer();
+    replaceHttpReply(reply);
+    flush();
+    complete();
+    negativeCache();
+    releaseRequest();
+    unlock("StoreEntry::storeErrorResponse");
+}
+
 /*
  * Replace a store entry with
  * a new reply. This eats the reply.
index ec8f1dc48c74b18d3842a60309c29ab0beaa6c4c..7aa6a0cb8d1eed2ca3f6787fb5c3668a73bf800e 100644 (file)
@@ -37,7 +37,6 @@ void ConnStateData::readNextRequest() STUB
 void ConnStateData::addContextToQueue(ClientSocketContext * context) STUB
 int ConnStateData::getConcurrentRequestCount() const STUB_RETVAL(0)
 bool ConnStateData::isOpen() const STUB_RETVAL(false)
-void ConnStateData::checkHeaderLimits() STUB
 void ConnStateData::sendControlMsg(HttpControlMsg msg) STUB
 int64_t ConnStateData::mayNeedToReadMoreBody() const STUB_RETVAL(0)
 #if USE_AUTH