From: Alex Rousskov Date: Tue, 19 Aug 2014 18:09:50 +0000 (-0600) Subject: Do not assert on native FTP ERR_TOO_BIG. Do not check for ERR_TOO_BIG twice. X-Git-Tag: SQUID_3_5_0_1~110 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=eacfca83ef8fa0e610644539cee10dbdc150bcff;p=thirdparty%2Fsquid.git Do not assert on native FTP ERR_TOO_BIG. Do not check for ERR_TOO_BIG twice. 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(). --- diff --git a/src/Store.h b/src/Store.h index f30d83a9f8..f3b6696473 100644 --- a/src/Store.h +++ b/src/Store.h @@ -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) diff --git a/src/client_side.cc b/src/client_side.cc index d8966a220e..b801ed6d06 100644 --- a/src/client_side.cc +++ b/src/client_side.cc @@ -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(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; } } diff --git a/src/client_side.h b/src/client_side.h index 238c684cc1..45f257b961 100644 --- a/src/client_side.h +++ b/src/client_side.h @@ -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 diff --git a/src/client_side_reply.cc b/src/client_side_reply.cc index 6cddeae4f8..fbbaf7997d 100644 --- a/src/client_side_reply.cc +++ b/src/client_side_reply.cc @@ -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) diff --git a/src/client_side_reply.h b/src/client_side_reply.h index e83ae20f26..33e4149824 100644 --- a/src/client_side_reply.h +++ b/src/client_side_reply.h @@ -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); diff --git a/src/errorpage.cc b/src/errorpage.cc index 44d6d18d8f..e219f7b00b 100644 --- a/src/errorpage.cc +++ b/src/errorpage.cc @@ -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; } diff --git a/src/servers/FtpServer.cc b/src/servers/FtpServer.cc index 3de0dbea1c..1df6be884d 100644 --- a/src/servers/FtpServer.cc +++ b/src/servers/FtpServer.cc @@ -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 ¶ms = 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(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(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 [ RWS ] 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(32*1024), // conservative + static_cast(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 ¶ms) +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 ¶ms = 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 ¶ms) } /// 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 ¶ms) { - 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 ¶ms) resetLogin("URI reset"); } - return true; + return NULL; // no early errors } bool diff --git a/src/servers/FtpServer.h b/src/servers/FtpServer.h index 0b30178e7b..3be163491a 100644 --- a/src/servers/FtpServer.h +++ b/src/servers/FtpServer.h @@ -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 ¶ms); + ClientSocketContext *handleUserRequest(const SBuf &cmd, SBuf ¶ms); 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 ¶ms); + ClientSocketContext *earlyError(const EarlyErrorKind eek); + bool handleRequest(HttpRequest *); void setDataCommand(); bool checkDataConnPre(); diff --git a/src/servers/HttpServer.cc b/src/servers/HttpServer.cc index 1f3dab3cca..3d9a82724f 100644 --- a/src/servers/HttpServer.cc +++ b/src/servers/HttpServer.cc @@ -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); } diff --git a/src/store.cc b/src/store.cc index a52fe2ad48..b403c81706 100644 --- a/src/store.cc +++ b/src/store.cc @@ -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. diff --git a/src/tests/stub_client_side.cc b/src/tests/stub_client_side.cc index ec8f1dc48c..7aa6a0cb8d 100644 --- a/src/tests/stub_client_side.cc +++ b/src/tests/stub_client_side.cc @@ -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