#define SQUID_CACHEMANAGER_H
#include "comm/forward.h"
+#include "log/forward.h"
#include "mgr/Action.h"
#include "mgr/ActionProfile.h"
#include "mgr/Command.h"
Mgr::Action::Pointer createRequestedAction(const Mgr::ActionParams &);
const Menu& menu() const { return menu_; }
- void Start(const Comm::ConnectionPointer &client, HttpRequest * request, StoreEntry * entry);
+ void start(const Comm::ConnectionPointer &client, HttpRequest *request, StoreEntry *entry, const AccessLogEntryPointer &ale);
static CacheManager* GetInstance();
const char *ActionProtection(const Mgr::ActionProfilePointer &profile);
*/
#include "squid.h"
+#include "base/Here.h"
#include "cache_cf.h"
#include "ConfigParser.h"
#include "Debug.h"
#include "fatal.h"
#include "globals.h"
+#include "sbuf/Stream.h"
bool ConfigParser::RecognizeQuotedValues = true;
bool ConfigParser::StrictMode = true;
}
}
+SBuf
+ConfigParser::CurrentLocation()
+{
+ return ToSBuf(SourceLocation(cfg_directive, cfg_filename, config_lineno));
+}
+
char *
ConfigParser::TokenParse(const char * &nextToken, ConfigParser::TokenType &type)
{
#define SQUID_CONFIGPARSER_H
#include "SquidString.h"
+#include "sbuf/forward.h"
#include <queue>
#include <stack>
#include <string>
class wordlist;
+
/**
* Limit to how long any given config line may be.
* This affects squid.conf and all included files.
/// Do not allow %macros inside quoted strings
static void DisableMacros() {AllowMacros_ = false;}
+ static SBuf CurrentLocation();
+
/// configuration_includes_quoted_values in squid.conf
static bool RecognizeQuotedValues;
if (entry->store_status == STORE_PENDING) {
if (entry->isEmpty()) {
if (!err) // we quit (e.g., fd closed) before an error or content
- fail(new ErrorState(ERR_READ_ERROR, Http::scBadGateway, request));
+ fail(new ErrorState(ERR_READ_ERROR, Http::scBadGateway, request, al));
assert(err);
errorAppendEntry(entry, err);
err = NULL;
if (page_id == ERR_NONE)
page_id = ERR_FORWARDING_DENIED;
- ErrorState *anErr = new ErrorState(page_id, Http::scForbidden, request);
+ const auto anErr = new ErrorState(page_id, Http::scForbidden, request, al);
errorAppendEntry(entry, anErr); // frees anErr
return;
}
if (shutting_down) {
/* more yuck */
- ErrorState *anErr = new ErrorState(ERR_SHUTTING_DOWN, Http::scServiceUnavailable, request);
+ const auto anErr = new ErrorState(ERR_SHUTTING_DOWN, Http::scServiceUnavailable, request, al);
errorAppendEntry(entry, anErr); // frees anErr
return;
}
if (request->flags.internal) {
debugs(17, 2, "calling internalStart() due to request flag");
- internalStart(clientConn, request, entry);
+ internalStart(clientConn, request, entry, al);
return;
}
case AnyP::PROTO_CACHE_OBJECT:
debugs(17, 2, "calling CacheManager due to request scheme " << request->url.getScheme());
- CacheManager::GetInstance()->Start(clientConn, request, entry);
+ CacheManager::GetInstance()->start(clientConn, request, entry, al);
return;
case AnyP::PROTO_URN:
debugs(17, 3, HERE << "Connection failed: " << entry->url());
if (!err) {
- ErrorState *anErr = new ErrorState(ERR_CANNOT_FORWARD, Http::scInternalServerError, request);
+ const auto anErr = new ErrorState(ERR_CANNOT_FORWARD, Http::scInternalServerError, request, al);
fail(anErr);
} // else use actual error from last connection attempt
request->hier.stopPeerClock(false);
if (self != NULL && !err && shutting_down && entry->isEmpty()) {
- ErrorState *anErr = new ErrorState(ERR_SHUTTING_DOWN, Http::scServiceUnavailable, request);
+ const auto anErr = new ErrorState(ERR_SHUTTING_DOWN, Http::scServiceUnavailable, request, al);
errorAppendEntry(entry, anErr);
}
assert(fd == serverDestinations[0]->fd);
if (entry->isEmpty()) {
- ErrorState *anErr = new ErrorState(ERR_CONNECT_FAIL, Http::scGatewayTimeout, request);
+ const auto anErr = new ErrorState(ERR_CONNECT_FAIL, Http::scGatewayTimeout, request, al);
anErr->xerrno = ETIMEDOUT;
fail(anErr);
// origin server
if (serverDestinations[0]->getPeer() && !serverDestinations[0]->getPeer()->options.originserver && request->flags.sslBumped) {
debugs(50, 4, "fwdConnectStart: Ssl bumped connections through parent proxy are not allowed");
- ErrorState *anErr = new ErrorState(ERR_CANNOT_FORWARD, Http::scServiceUnavailable, request);
+ const auto anErr = new ErrorState(ERR_CANNOT_FORWARD, Http::scServiceUnavailable, request, al);
fail(anErr);
stopAndDestroy("SslBump misconfiguration");
return;
if (!Comm::IsConnOpen(temp)) {
syncHierNote(temp, connManager ? connManager->pinning.host : request->url.host());
serverConn = nullptr;
- const auto anErr = new ErrorState(ERR_ZERO_SIZE_OBJECT, Http::scServiceUnavailable, request);
+ const auto anErr = new ErrorState(ERR_ZERO_SIZE_OBJECT, Http::scServiceUnavailable, request, al);
fail(anErr);
// Connection managers monitor their idle pinned to-server
// connections and close from-client connections upon seeing
default:
debugs(17, DBG_IMPORTANT, "WARNING: Cannot retrieve '" << entry->url() << "'.");
- ErrorState *anErr = new ErrorState(ERR_UNSUP_REQ, Http::scBadRequest, request);
+ const auto anErr = new ErrorState(ERR_UNSUP_REQ, Http::scBadRequest, request, al);
fail(anErr);
// Set the dont_retry flag because this is not a transient (network) error.
flags.dont_retry = true;
FwdState::makeConnectingError(const err_type type) const
{
return new ErrorState(type, request->flags.needValidation ?
- Http::scGatewayTimeout : Http::scServiceUnavailable, request);
+ Http::scGatewayTimeout : Http::scServiceUnavailable, request, al);
}
static void
/* DEBUG: section 56 HTTP Message Body */
#include "squid.h"
+#include "base/Packable.h"
#include "HttpBody.h"
-#include "MemBuf.h"
-
-HttpBody::HttpBody() : mb(new MemBuf)
-{}
-
-HttpBody::~HttpBody()
-{
- delete mb;
-}
-
-void
-HttpBody::clear()
-{
- mb->clean();
-}
-
-/* set body by absorbing mb */
-void
-HttpBody::setMb(MemBuf * mb_)
-{
- delete mb;
- /* note: protection against assign-to-self is not needed
- * as MemBuf doesn't have a copy-constructor. If such a constructor
- * is ever added, add such protection here.
- */
- mb = mb_; /* absorb */
-}
void
HttpBody::packInto(Packable * p) const
{
assert(p);
-
- if (mb->contentSize())
- p->append(mb->content(), mb->contentSize());
+ if (const auto size = contentSize())
+ p->append(content(), size);
}
#ifndef HTTPBODY_H_
#define HTTPBODY_H_
-#include "MemBuf.h"
+#include "sbuf/SBuf.h"
+
+class Packable; // TODO: Add and use base/forward.h.
/** Representation of a short predetermined message
*
class HttpBody
{
public:
- HttpBody();
- ~HttpBody();
- /** absorb the MemBuf, discarding anything currently stored
- *
- * After this call the lifetime of the passed MemBuf is managed
- * by the HttpBody.
- */
- void setMb(MemBuf *);
+ HttpBody() {}
+
+ void set(const SBuf &newContent) { raw_ = newContent; }
/** output the HttpBody contents into the supplied container
*
void packInto(Packable *) const;
/// clear the HttpBody content
- void clear();
+ void clear() { raw_.clear(); }
/// \return true if there is any content in the HttpBody
- bool hasContent() const { return (mb->contentSize()>0); }
+ bool hasContent() const { return raw_.length() > 0; }
/// \return size of the HttpBody's message content
- mb_size_t contentSize() const { return mb->contentSize(); }
+ size_t contentSize() const { return raw_.length(); }
+
+ /// \return body bytes (possibly not nil-terminated)
+ const char *content() const { return raw_.rawContent(); }
- /// \return pointer to the storage of the HttpBody
- char *content() const { return mb->content(); }
private:
HttpBody& operator=(const HttpBody&); //not implemented
HttpBody(const HttpBody&); // not implemented
- MemBuf *mb;
+
+ SBuf raw_; // body bytes
};
#endif /* HTTPBODY_H_ */
#include "err_type.h"
#include "errorpage.h"
#include "mem/forward.h"
+#include "sbuf/forward.h"
/// deny_info representation. Currently a POD.
class AclDenyInfoList
MEMPROXY_CLASS(AclDenyInfoList);
public:
- AclDenyInfoList(const char *t) {
+ AclDenyInfoList(const char *t, const SBuf &aCfgLocation) {
err_page_name = xstrdup(t);
- err_page_id = errorReservePageId(t);
+ err_page_id = errorReservePageId(t, aCfgLocation);
}
~AclDenyInfoList() {
xfree(err_page_name);
#include "errorpage.h"
#include "globals.h"
#include "HttpRequest.h"
+#include "src/sbuf/Stream.h"
#include <set>
#include <algorithm>
return;
}
- AclDenyInfoList *A = new AclDenyInfoList(t);
+ const auto A = new AclDenyInfoList(t, ConfigParser::CurrentLocation());
/* next expect a list of ACL names */
while ((t = ConfigParser::NextToken())) {
/* DEBUG: section 16 Cache Manager Objects */
#include "squid.h"
+#include "AccessLogEntry.h"
#include "base/TextException.h"
#include "CacheManager.h"
#include "comm/Connection.h"
* all needed internal work and renders the response.
*/
void
-CacheManager::Start(const Comm::ConnectionPointer &client, HttpRequest * request, StoreEntry * entry)
+CacheManager::start(const Comm::ConnectionPointer &client, HttpRequest *request, StoreEntry *entry, const AccessLogEntry::Pointer &ale)
{
debugs(16, 3, "CacheManager::Start: '" << entry->url() << "'" );
Mgr::Command::Pointer cmd = ParseUrl(entry->url());
if (!cmd) {
- ErrorState *err = new ErrorState(ERR_INVALID_URL, Http::scNotFound, request);
+ const auto err = new ErrorState(ERR_INVALID_URL, Http::scNotFound, request, ale);
err->url = xstrdup(entry->url());
errorAppendEntry(entry, err);
entry->expires = squid_curtime;
if (CheckPassword(*cmd) != 0) {
/* build error message */
- ErrorState errState(ERR_CACHE_MGR_ACCESS_DENIED, Http::scUnauthorized, request);
+ ErrorState errState(ERR_CACHE_MGR_ACCESS_DENIED, Http::scUnauthorized, request, ale);
/* warn if user specified incorrect password */
if (cmd->params.password.size()) {
// special case: /squid-internal-mgr/ index page
if (!strcmp(cmd->profile->name, "index")) {
- ErrorState err(MGR_INDEX, Http::scOkay, request);
+ ErrorState err(MGR_INDEX, Http::scOkay, request, ale);
err.url = xstrdup(entry->url());
HttpReply *rep = err.BuildHttpReply();
if (strncmp(rep->body.content(),"Internal Error:", 15) == 0)
if (UsingSmp() && IamWorkerProcess()) {
// is client the right connection to pass here?
- AsyncJob::Start(new Mgr::Forwarder(client, cmd->params, request, entry));
+ AsyncJob::Start(new Mgr::Forwarder(client, cmd->params, request, entry, ale));
return;
}
request->hier = sslServerBump->request->hier;
// Create an error object and fill it
- ErrorState *err = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, request);
+ const auto err = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, request, http->al);
err->src_addr = clientConnection->remote;
Ssl::ErrorDetail *errDetail = new Ssl::ErrorDetail(
SQUID_X509_V_ERR_DOMAIN_MISMATCH,
/* Local functions */
extern "C" CSS clientReplyStatus;
-ErrorState *clientBuildError(err_type, Http::StatusCode, char const *, Ip::Address &, HttpRequest *);
+ErrorState *clientBuildError(err_type, Http::StatusCode, char const *, Ip::Address &, HttpRequest *, const AccessLogEntry::Pointer &);
/* privates */
#endif
)
{
- ErrorState *errstate = clientBuildError(err, status, uri, addr, failedrequest);
+ auto errstate = clientBuildError(err, status, uri, addr, failedrequest, http->al);
if (unparsedrequest)
errstate->request_hdrs = xstrdup(unparsedrequest);
http->al->http.code = Http::scForbidden;
Ip::Address tmp_noaddr;
tmp_noaddr.setNoAddr();
- err = clientBuildError(ERR_ACCESS_DENIED, Http::scForbidden, nullptr, conn ? conn->remote : tmp_noaddr, http->request);
+ err = clientBuildError(ERR_ACCESS_DENIED, Http::scForbidden, nullptr, conn ? conn->remote : tmp_noaddr, http->request, http->al);
createStoreEntry(r->method, RequestFlags());
errorAppendEntry(http->storeEntry(), err);
triggerInitialStoreRead();
tmp_noaddr.setNoAddr();
ErrorState *err = clientBuildError(ERR_ONLY_IF_CACHED_MISS, Http::scGatewayTimeout, NULL,
http->getConn() ? http->getConn()->clientConnection->remote : tmp_noaddr,
- http->request);
+ http->request, http->al);
removeClientStoreReference(&sc, http);
startError(err);
}
tmp_noaddr.setNoAddr(); // TODO: make a global const
ErrorState *err = clientBuildError(ERR_ACCESS_DENIED, Http::scForbidden, NULL,
http->getConn() ? http->getConn()->clientConnection->remote : tmp_noaddr,
- http->request);
+ http->request, http->al);
startError(err);
return; // XXX: leaking unused entry if some store does not keep it
}
Ip::Address tmp_noaddr;
tmp_noaddr.setNoAddr();
ErrorState *err = clientBuildError(ERR_ACCESS_DENIED, Http::scForbidden, NULL,
- http->getConn() ? http->getConn()->clientConnection->remote : tmp_noaddr, http->request);
+ http->getConn() ? http->getConn()->clientConnection->remote : tmp_noaddr, http->request, http->al);
startError(err);
return;
}
http->logType.update(LOG_TCP_DENIED_REPLY);
ErrorState *err = clientBuildError(ERR_TOO_BIG, Http::scForbidden, NULL,
http->getConn() != NULL ? http->getConn()->clientConnection->remote : tmp_noaddr,
- http->request);
+ http->request, http->al);
removeClientStoreReference(&(sc), http);
HTTPMSGUNLOCK(reply);
startError(err);
tmp_noaddr.setNoAddr();
ErrorState *const err =
clientBuildError(ERR_PRECONDITION_FAILED, Http::scPreconditionFailed,
- NULL, http->getConn() ? http->getConn()->clientConnection->remote : tmp_noaddr, http->request);
+ NULL, http->getConn() ? http->getConn()->clientConnection->remote : tmp_noaddr, http->request, http->al);
removeClientStoreReference(&sc, http);
HTTPMSGUNLOCK(reply);
startError(err);
tmp_noaddr.setNoAddr();
err = clientBuildError(page_id, Http::scForbidden, NULL,
http->getConn() != NULL ? http->getConn()->clientConnection->remote : tmp_noaddr,
- http->request);
+ http->request, http->al);
removeClientStoreReference(&sc, http);
ErrorState *
clientBuildError(err_type page_id, Http::StatusCode status, char const *url,
- Ip::Address &src_addr, HttpRequest * request)
+ Ip::Address &src_addr, HttpRequest * request, const AccessLogEntry::Pointer &al)
{
- ErrorState *err = new ErrorState(page_id, status, request);
+ const auto err = new ErrorState(page_id, status, request, al);
err->src_addr = src_addr;
if (url)
static void clientFollowXForwardedForCheck(allow_t answer, void *data);
#endif /* FOLLOW_X_FORWARDED_FOR */
-ErrorState *clientBuildError(err_type, Http::StatusCode, char const *url, Ip::Address &, HttpRequest *);
+ErrorState *clientBuildError(err_type, Http::StatusCode, char const *url, Ip::Address &, HttpRequest *, const AccessLogEntry::Pointer &);
CBDATA_CLASS_INIT(ClientRequestContext);
error = clientBuildError(page_id, status,
NULL,
http->getConn() != NULL ? http->getConn()->clientConnection->remote : tmpnoaddr,
- http->request
+ http->request, http->al
);
#if USE_AUTH
calloutContext->error = clientBuildError(error, Http::scInternalServerError,
NULL,
c != NULL ? c->clientConnection->remote : noAddr,
- request
+ request,
+ al
);
#if USE_AUTH
calloutContext->error->auth_user_request =
if (io.flag) {
debugs(11, DBG_IMPORTANT, "sentRequestBody error: FD " << io.fd << ": " << xstrerr(io.xerrno));
ErrorState *err;
- err = new ErrorState(ERR_WRITE_ERROR, Http::scBadGateway, fwd->request);
+ err = new ErrorState(ERR_WRITE_ERROR, Http::scBadGateway, fwd->request, fwd->al);
err->xerrno = io.xerrno;
fwd->fail(err);
abortOnData("I/O error while sending request body");
{
if (entry->isEmpty()) {
debugs(11,8, "adaptation failure with an empty entry: " << *entry);
- ErrorState *err = new ErrorState(ERR_ICAP_FAILURE, Http::scInternalServerError, request.getRaw());
+ const auto err = new ErrorState(ERR_ICAP_FAILURE, Http::scInternalServerError, request.getRaw(), fwd->al);
err->detailError(ERR_DETAIL_ICAP_RESPMOD_EARLY);
fwd->fail(err);
fwd->dontRetry(true);
if (page_id == ERR_NONE)
page_id = ERR_ACCESS_DENIED;
- ErrorState *err = new ErrorState(page_id, Http::scForbidden, request.getRaw());
+ const auto err = new ErrorState(page_id, Http::scForbidden, request.getRaw(), fwd->al);
err->detailError(ERR_DETAIL_RESPMOD_BLOCK_EARLY);
fwd->fail(err);
fwd->dontRetry(true);
void
Client::sendBodyIsTooLargeError()
{
- ErrorState *err = new ErrorState(ERR_TOO_BIG, Http::scForbidden, request.getRaw());
+ const auto err = new ErrorState(ERR_TOO_BIG, Http::scForbidden, request.getRaw(), fwd->al);
fwd->fail(err);
fwd->dontRetry(true);
abortOnData("Virgin body too large.");
ftperr = err;
} else {
Http::StatusCode httpStatus = failedHttpStatus(error);
- ftperr = new ErrorState(error, httpStatus, fwd->request);
+ ftperr = new ErrorState(error, httpStatus, fwd->request, fwd->al);
}
ftperr->xerrno = xerrno;
virtual void timeout(const CommTimeoutCbParams &io);
void ftpAcceptDataConnection(const CommAcceptCbParams &io);
- static HttpReply *ftpAuthRequired(HttpRequest * request, SBuf &realm);
+ static HttpReply *ftpAuthRequired(HttpRequest * request, SBuf &realm, AccessLogEntry::Pointer &);
SBuf ftpRealm();
void loginFailed(void);
if (!checkAuth(&request->header)) {
/* create appropriate reply */
SBuf realm(ftpRealm()); // local copy so SBuf will not disappear too early
- HttpReply *reply = ftpAuthRequired(request.getRaw(), realm);
+ const auto reply = ftpAuthRequired(request.getRaw(), realm, fwd->al);
entry->replaceHttpReply(reply);
serverComplete();
return;
if ((state == SENT_USER || state == SENT_PASS) && ctrl.replycode >= 400) {
if (ctrl.replycode == 421 || ctrl.replycode == 426) {
// 421/426 - Service Overload - retry permitted.
- err = new ErrorState(ERR_FTP_UNAVAILABLE, Http::scServiceUnavailable, fwd->request);
+ err = new ErrorState(ERR_FTP_UNAVAILABLE, Http::scServiceUnavailable, fwd->request, fwd->al);
} else if (ctrl.replycode >= 430 && ctrl.replycode <= 439) {
// 43x - Invalid or Credential Error - retry challenge required.
- err = new ErrorState(ERR_FTP_FORBIDDEN, Http::scUnauthorized, fwd->request);
+ err = new ErrorState(ERR_FTP_FORBIDDEN, Http::scUnauthorized, fwd->request, fwd->al);
} else if (ctrl.replycode >= 530 && ctrl.replycode <= 539) {
// 53x - Credentials Missing - retry challenge required
if (password_url) // but they were in the URI! major fail.
- err = new ErrorState(ERR_FTP_FORBIDDEN, Http::scForbidden, fwd->request);
+ err = new ErrorState(ERR_FTP_FORBIDDEN, Http::scForbidden, fwd->request, fwd->al);
else
- err = new ErrorState(ERR_FTP_FORBIDDEN, Http::scUnauthorized, fwd->request);
+ err = new ErrorState(ERR_FTP_FORBIDDEN, Http::scUnauthorized, fwd->request, fwd->al);
}
}
{
assert(entry);
entry->lock("Ftp::Gateway");
- ErrorState ferr(ERR_DIR_LISTING, Http::scOkay, request.getRaw());
+ ErrorState ferr(ERR_DIR_LISTING, Http::scOkay, request.getRaw(), fwd->al);
ferr.ftp.listing = &listing;
ferr.ftp.cwd_msg = xstrdup(cwd_message.size()? cwd_message.termedBuf() : "");
ferr.ftp.server_msg = ctrl.message;
}
Http::StatusCode sc = ftpState->failedHttpStatus(error_code);
- ErrorState *ftperr = new ErrorState(error_code, sc, ftpState->fwd->request);
+ const auto ftperr = new ErrorState(error_code, sc, ftpState->fwd->request, ftpState->fwd->al);
ftpState->failed(error_code, code, ftperr);
ftperr->detailError(code);
HttpReply *newrep = ftperr->BuildHttpReply();
http_code = Http::scInternalServerError;
}
- ErrorState err(err_code, http_code, ftpState->request.getRaw());
+ ErrorState err(err_code, http_code, ftpState->request.getRaw(), ftpState->fwd->al);
if (ftpState->old_request)
err.ftp.request = xstrdup(ftpState->old_request);
}
HttpReply *
-Ftp::Gateway::ftpAuthRequired(HttpRequest * request, SBuf &realm)
+Ftp::Gateway::ftpAuthRequired(HttpRequest * request, SBuf &realm, AccessLogEntry::Pointer &ale)
{
- ErrorState err(ERR_CACHE_ACCESS_DENIED, Http::scUnauthorized, request);
+ ErrorState err(ERR_CACHE_ACCESS_DENIED, Http::scUnauthorized, request, ale);
HttpReply *newrep = err.BuildHttpReply();
#if HAVE_AUTH_MODULE_BASIC
/* add Authenticate header */
/* DEBUG: section 04 Error Generation */
#include "squid.h"
+#include "AccessLogEntry.h"
#include "cache_cf.h"
#include "clients/forward.h"
#include "comm/Connection.h"
#include "err_detail_type.h"
#include "errorpage.h"
#include "fde.h"
+#include "format/Format.h"
#include "fs_io.h"
#include "html_quote.h"
#include "HttpHeaderTools.h"
#include "MemBuf.h"
#include "MemObject.h"
#include "rfc1738.h"
+#include "sbuf/Stream.h"
#include "SquidConfig.h"
+#include "SquidTime.h"
#include "Store.h"
#include "tools.h"
#include "wordlist.h"
#if USE_AUTH
#include "auth/UserRequest.h"
#endif
-#include "SquidTime.h"
#if USE_OPENSSL
#include "ssl/ErrorDetailManager.h"
#endif
/// \ingroup ErrorPageInternal
CBDATA_CLASS_INIT(ErrorState);
+const SBuf ErrorState::LogformatMagic("@Squid{");
+
/* local types */
-/// \ingroup ErrorPageInternal
-typedef struct {
+/// an error page created from admin-configurable metadata (e.g. deny_info)
+class ErrorDynamicPageInfo {
+public:
+ ErrorDynamicPageInfo(const int anId, const char *aName, const SBuf &aCfgLocation);
+ ~ErrorDynamicPageInfo() { xfree(page_name); }
+
+ /// error_text[] index for response body (unused in redirection responses)
int id;
+
+ /// Primary deny_info parameter:
+ /// * May start with an HTTP status code.
+ /// * Either a well-known error page name, a filename, or a redirect URL.
char *page_name;
+
+ /// admin-configured HTTP Location header value for redirection responses
+ const char *uri;
+
+ /// admin-configured name for the error page template (custom or standard)
+ const char *filename;
+
+ /// deny_info directive position in squid.conf (for reporting)
+ SBuf cfgLocation;
+
+ // XXX: Misnamed. Not just for redirects.
+ /// admin-configured HTTP status code
Http::StatusCode page_redirect;
-} ErrorDynamicPageInfo;
+
+private:
+ // no copying of any kind
+ ErrorDynamicPageInfo(ErrorDynamicPageInfo &&) = delete;
+};
+
+namespace ErrorPage {
+
+/// state and parameters shared by several ErrorState::compile*() methods
+class Build
+{
+public:
+ SBuf output; ///< compilation result
+ const char *input = nullptr; ///< template bytes that need to be compiled
+ bool building_deny_info_url = false; ///< whether we compile deny_info URI
+ bool allowRecursion = false; ///< whether top-level compile() calls are OK
+};
+
+/// pretty-prints error page/deny_info building error
+class BuildErrorPrinter
+{
+public:
+ BuildErrorPrinter(const SBuf &anInputLocation, int aPage, const char *aMsg, const char *aNear): inputLocation(anInputLocation), page_id(aPage), msg(aMsg), near(aNear) {}
+
+ /// reports error details (for admin-visible exceptions and debugging)
+ std::ostream &print(std::ostream &) const;
+
+ /// print() helper to report where the error was found
+ std::ostream &printLocation(std::ostream &os) const;
+
+ /* saved constructor parameters */
+ const SBuf &inputLocation;
+ const int page_id;
+ const char *msg;
+ const char *near;
+};
+
+static inline std::ostream &
+operator <<(std::ostream &os, const BuildErrorPrinter &context)
+{
+ return context.print(os);
+}
+
+static const char *IsDenyInfoUri(const int page_id);
+
+static void ImportStaticErrorText(const int page_id, const char *text, const SBuf &inputLocation);
+static void ValidateStaticError(const int page_id, const SBuf &inputLocation);
+
+} // namespace ErrorPage
/* local constant and vars */
{
TCP_RESET,
"reset"
+ },
+ {
+ ERR_SECURE_ACCEPT_FAIL,
+ "secure accept fail"
+ },
+ {
+ ERR_REQUEST_START_TIMEOUT,
+ "request start timedout"
+ },
+ {
+ MGR_INDEX,
+ "mgr_index"
}
};
static MemBuf error_stylesheet;
static const char *errorFindHardText(err_type type);
-static ErrorDynamicPageInfo *errorDynamicPageInfoCreate(int id, const char *page_name);
-static void errorDynamicPageInfoDestroy(ErrorDynamicPageInfo * info);
static IOCB errorSendComplete;
/// \ingroup ErrorPageInternal
class ErrorPageFile: public TemplateFile
{
public:
- ErrorPageFile(const char *name, const err_type code) : TemplateFile(name,code) {textBuf.init();}
+ ErrorPageFile(const char *name, const err_type code) : TemplateFile(name, code) {}
/// The template text data read from disk
- const char *text() { return textBuf.content(); }
+ const char *text() { return template_.c_str(); }
-private:
- /// stores the data read from disk to a local buffer
- virtual bool parse(const char *buf, int len, bool) {
- if (len)
- textBuf.append(buf, len);
- return true;
+protected:
+ virtual void setDefault() override {
+ template_ = "Internal Error: Missing Template ";
+ template_.append(templateName.termedBuf());
}
-
- MemBuf textBuf; ///< A buffer to store the error page
};
/// \ingroup ErrorPageInternal
return (int)anErr - (int)anErr2;
}
+/// \return deny_info URL if the given page is a deny_info page with a URL
+/// \return nullptr otherwise
+static const char *
+ErrorPage::IsDenyInfoUri(const int page_id)
+{
+ if (ERR_MAX <= page_id && page_id < error_page_count)
+ return ErrorDynamicPages.at(page_id - ERR_MAX)->uri; // may be nil
+ return nullptr;
+}
+
void
errorInitialize(void)
{
+ using ErrorPage::ImportStaticErrorText;
+
err_type i;
const char *text;
error_page_count = ERR_MAX + ErrorDynamicPages.size();
/**\par
* Index any hard-coded error text into defaults.
*/
- error_text[i] = xstrdup(text);
+ static const SBuf builtIn("built-in");
+ ImportStaticErrorText(i, text, builtIn);
} else if (i < ERR_MAX) {
/**\par
* (b) admin specified custom directory (error_directory)
*/
ErrorPageFile errTmpl(err_type_str[i], i);
- error_text[i] = errTmpl.loadDefault() ? xstrdup(errTmpl.text()) : NULL;
+ errTmpl.loadDefault();
+ ImportStaticErrorText(i, errTmpl.text(), errTmpl.filename);
} else {
/** \par
* Index any unknown file names used by deny_info.
ErrorDynamicPageInfo *info = ErrorDynamicPages.at(i - ERR_MAX);
assert(info && info->id == i && info->page_name);
- const char *pg = info->page_name;
- if (info->page_redirect != Http::scNone)
- pg = info->page_name +4;
-
- if (strchr(pg, ':') == NULL) {
+ if (info->filename) {
/** But only if they are not redirection URL. */
- ErrorPageFile errTmpl(pg, ERR_MAX);
- error_text[i] = errTmpl.loadDefault() ? xstrdup(errTmpl.text()) : NULL;
+ ErrorPageFile errTmpl(info->filename, ERR_MAX);
+ errTmpl.loadDefault();
+ ImportStaticErrorText(i, errTmpl.text(), errTmpl.filename);
+ } else {
+ assert(info->uri);
+ ErrorPage::ValidateStaticError(i, info->cfgLocation);
}
}
}
}
while (!ErrorDynamicPages.empty()) {
- errorDynamicPageInfoDestroy(ErrorDynamicPages.back());
+ delete ErrorDynamicPages.back();
ErrorDynamicPages.pop_back();
}
assert(name);
}
-bool
+void
TemplateFile::loadDefault()
{
if (loaded()) // already loaded?
- return true;
+ return;
/** test error_directory configured location */
if (Config.errorDirectory) {
/* giving up if failed */
if (!loaded()) {
debugs(1, (templateCode < TCP_RESET ? DBG_CRITICAL : 3), "WARNING: failed to find or read error text file " << templateName);
- parse("Internal Error: Missing Template ", 33, '\0');
- parse(templateName.termedBuf(), templateName.size(), '\0');
+ template_.clear();
+ setDefault();
+ wasLoaded = true;
}
-
- return true;
}
bool
return wasLoaded;
}
+ template_.clear();
while ((len = FD_READ_METHOD(fd, buf, sizeof(buf))) > 0) {
- if (!parse(buf, len, false)) {
- debugs(4, DBG_CRITICAL, "ERROR: parsing error in template file: " << path);
- wasLoaded = false;
- return wasLoaded;
- }
+ template_.append(buf, len);
}
- parse(buf, 0, true);
if (len < 0) {
int xerrno = errno;
+ file_close(fd);
debugs(4, DBG_CRITICAL, MYNAME << "ERROR: failed to fully read: '" << path << "': " << xstrerr(xerrno));
+ wasLoaded = false;
+ return false;
}
file_close(fd);
+ filename = SBuf(path);
+
+ if (!parse()) {
+ debugs(4, DBG_CRITICAL, "ERROR: parsing error in template file: " << path);
+ wasLoaded = false;
+ return false;
+ }
+
wasLoaded = true;
return wasLoaded;
}
return loaded();
}
-/// \ingroup ErrorPageInternal
-static ErrorDynamicPageInfo *
-errorDynamicPageInfoCreate(int id, const char *page_name)
+ErrorDynamicPageInfo::ErrorDynamicPageInfo(const int anId, const char *aName, const SBuf &aCfgLocation):
+ id(anId),
+ page_name(xstrdup(aName)),
+ uri(nullptr),
+ filename(nullptr),
+ cfgLocation(aCfgLocation),
+ page_redirect(static_cast<Http::StatusCode>(atoi(page_name)))
{
- ErrorDynamicPageInfo *info = new ErrorDynamicPageInfo;
- info->id = id;
- info->page_name = xstrdup(page_name);
- info->page_redirect = static_cast<Http::StatusCode>(atoi(page_name));
+ const char *filenameOrUri = nullptr;
+ if (xisdigit(*page_name)) {
+ if (const char *statusCodeEnd = strchr(page_name, ':'))
+ filenameOrUri = statusCodeEnd + 1;
+ } else {
+ assert(!page_redirect);
+ filenameOrUri = page_name;
+ }
+
+ // Guessed uri, filename, or both values may be nil or malformed.
+ // They are validated later.
+ if (!page_redirect) {
+ if (filenameOrUri && strchr(filenameOrUri, ':')) // looks like a URL
+ uri = filenameOrUri;
+ else
+ filename = filenameOrUri;
+ }
+ else if (page_redirect/100 == 3) {
+ // redirects imply a URL
+ uri = filenameOrUri;
+ } else {
+ // non-redirects imply an error page name
+ filename = filenameOrUri;
+ }
+
+ const auto info = this; // source code change reduction hack
+ // TODO: Move and refactor to avoid self_destruct()s in reconfigure.
/* WARNING on redirection status:
* 2xx are permitted, but not documented officially.
self_destruct();
}
// else okay.
-
- return info;
-}
-
-/// \ingroup ErrorPageInternal
-static void
-errorDynamicPageInfoDestroy(ErrorDynamicPageInfo * info)
-{
- assert(info);
- safe_free(info->page_name);
- delete info;
}
/// \ingroup ErrorPageInternal
}
err_type
-errorReservePageId(const char *page_name)
+errorReservePageId(const char *page_name, const SBuf &cfgLocation)
{
- ErrorDynamicPageInfo *info;
int id = errorPageId(page_name);
if (id == ERR_NONE) {
- info = errorDynamicPageInfoCreate(ERR_MAX + ErrorDynamicPages.size(), page_name);
+ id = ERR_MAX + ErrorDynamicPages.size();
+ const auto info = new ErrorDynamicPageInfo(id, page_name, cfgLocation);
ErrorDynamicPages.push_back(info);
- id = info->id;
}
return (err_type)id;
}
ErrorState *
-ErrorState::NewForwarding(err_type type, HttpRequestPointer &request)
+ErrorState::NewForwarding(err_type type, HttpRequestPointer &request, const AccessLogEntry::Pointer &ale)
{
const Http::StatusCode status = (request && request->flags.needValidation) ?
Http::scGatewayTimeout : Http::scServiceUnavailable;
- return new ErrorState(type, status, request.getRaw());
+ return new ErrorState(type, status, request.getRaw(), ale);
}
-ErrorState::ErrorState(err_type t, Http::StatusCode status, HttpRequest * req) :
+ErrorState::ErrorState(err_type t, Http::StatusCode status, HttpRequest * req, const AccessLogEntry::Pointer &anAle) :
type(t),
page_id(t),
httpStatus(status),
- callback(nullptr)
+ callback(nullptr),
+ ale(anAle)
{
if (page_id >= ERR_MAX && ErrorDynamicPages[page_id - ERR_MAX]->page_redirect != Http::scNone)
httpStatus = ErrorDynamicPages[page_id - ERR_MAX]->page_redirect;
/// \ingroup ErrorPageInternal
#define CVT_BUF_SZ 512
-const char *
-ErrorState::Convert(char token, bool building_deny_info_url, bool allowRecursion)
+void
+ErrorState::compileLogformatCode(Build &build)
+{
+ assert(LogformatMagic.cmp(build.input, LogformatMagic.length()) == 0);
+
+ try {
+ const auto logformat = build.input + LogformatMagic.length();
+
+ // Logformat supports undocumented "external" encoding specifications
+ // like [%>h] or "%<a". To preserve the possibility of extending
+ // @Squid{} syntax to non-logformat sequences, we require logformat
+ // sequences to start with '%'. This restriction does not limit
+ // logformat quoting abilities. TODO: Deprecate "external" encoding?
+ if (*logformat != '%')
+ throw TexcHere("logformat expressions that do not start with % are not supported");
+
+ static MemBuf result;
+ result.reset();
+ const auto logformatLen = Format::AssembleOne(logformat, result, ale);
+ assert(logformatLen > 0);
+ const auto closure = logformat + logformatLen;
+ if (*closure != '}')
+ throw TexcHere("Missing closing brace (})");
+ build.output.append(result.content(), result.contentSize());
+ build.input = closure + 1;
+ return;
+ } catch (...) {
+ noteBuildError("Bad @Squid{logformat} sequence", build.input);
+ }
+
+ // we cannot recover reliably so stop interpreting the rest of input
+ const auto remainingSize = strlen(build.input);
+ build.output.append(build.input, remainingSize);
+ build.input += remainingSize;
+}
+
+void
+ErrorState::compileLegacyCode(Build &build)
{
static MemBuf mb;
const char *p = NULL; /* takes priority over mb if set */
mb.reset();
- switch (token) {
+ const auto &building_deny_info_url = build.building_deny_info_url; // a change reduction hack
+
+ const auto letter = build.input[1];
+
+ switch (letter) {
case 'a':
#if USE_AUTH
break;
case 'D':
- if (!allowRecursion)
+ if (!build.allowRecursion)
p = "%D"; // if recursion is not allowed, do not convert
#if USE_OPENSSL
// currently only SSL error details implemented
detail->useRequest(request.getRaw());
const String &errDetail = detail->toString();
if (errDetail.size() > 0) {
- MemBuf *detail_mb = ConvertText(errDetail.termedBuf(), false);
- mb.append(detail_mb->content(), detail_mb->contentSize());
- delete detail_mb;
+ const auto compiledDetail = compileBody(errDetail.termedBuf(), false);
+ mb.append(compiledDetail.rawContent(), compiledDetail.length());
do_quote = 0;
}
}
if (page_id != ERR_SQUID_SIGNATURE) {
const int saved_id = page_id;
page_id = ERR_SQUID_SIGNATURE;
- MemBuf *sign_mb = BuildContent();
- mb.append(sign_mb->content(), sign_mb->contentSize());
- sign_mb->clean();
- delete sign_mb;
+ const auto signature = buildBody();
+ mb.append(signature.rawContent(), signature.length());
page_id = saved_id;
do_quote = 0;
} else {
break;
default:
- mb.appendf("%%%c", token);
+ if (building_deny_info_url)
+ bypassBuildErrorXXX("Unsupported deny_info %code", build.input);
+ else if (letter != ';')
+ bypassBuildErrorXXX("Unsupported error page %code", build.input);
+ // else too many "font-size: 100%;" template errors to report
+
+ mb.append(build.input, 2);
do_quote = 0;
break;
}
assert(p);
- debugs(4, 3, "errorConvert: %%" << token << " --> '" << p << "'" );
+ debugs(4, 3, "%" << letter << " --> '" << p << "'" );
if (do_quote)
p = html_quote(p);
if (building_deny_info_url && !no_urlescape)
p = rfc1738_escape_part(p);
- return p;
+ // TODO: Optimize by replacing mb with direct build.output usage.
+ build.output.append(p, strlen(p));
+ build.input += 2;
}
void
-ErrorState::DenyInfoLocation(const char *name, HttpRequest *, MemBuf &result)
+ErrorState::validate()
{
- char const *m = name;
- char const *p = m;
- char const *t;
-
- if (m[0] == '3')
- m += 4; // skip "3xx:"
-
- while ((p = strchr(m, '%'))) {
- result.append(m, p - m); /* copy */
- t = Convert(*++p, true, true); /* convert */
- result.appendf("%s", t); /* copy */
- m = p + 1; /* advance */
+ if (const auto urlTemplate = ErrorPage::IsDenyInfoUri(page_id)) {
+ (void)compile(urlTemplate, true, true);
+ } else {
+ assert(page_id > ERR_NONE);
+ assert(page_id < error_page_count);
+ (void)compileBody(error_text[page_id], true);
}
-
- if (*m)
- result.appendf("%s", m); /* copy tail */
-
- assert((size_t)result.contentSize() == strlen(result.content()));
}
HttpReply *
const char *name = errorPageName(page_id);
/* no LMT for error pages; error pages expire immediately */
- if (name[0] == '3' || (name[0] != '2' && name[0] != '4' && name[0] != '5' && strchr(name, ':'))) {
+ if (const auto urlTemplate = ErrorPage::IsDenyInfoUri(page_id)) {
/* Redirection */
Http::StatusCode status = Http::scFound;
// Use configured 3xx reply status if set.
rep->setHeaders(status, NULL, "text/html;charset=utf-8", 0, 0, -1);
if (request) {
- MemBuf redirect_location;
- redirect_location.init();
- DenyInfoLocation(name, request.getRaw(), redirect_location);
- httpHeaderPutStrf(&rep->header, Http::HdrType::LOCATION, "%s", redirect_location.content() );
+ auto location = compile(urlTemplate, true, true);
+ rep->header.putStr(Http::HdrType::LOCATION, location.c_str());
}
httpHeaderPutStrf(&rep->header, Http::HdrType::X_SQUID_ERROR, "%d %s", httpStatus, "Access Denied");
} else {
- MemBuf *content = BuildContent();
- rep->setHeaders(httpStatus, NULL, "text/html;charset=utf-8", content->contentSize(), 0, -1);
+ const auto body = buildBody();
+ rep->setHeaders(httpStatus, NULL, "text/html;charset=utf-8", body.length(), 0, -1);
/*
* include some information for downstream caches. Implicit
* replaceable content. This isn't quite sufficient. xerrno is not
rep->header.putStr(Http::HdrType::CONTENT_LANGUAGE, "en");
}
- rep->body.setMb(content);
- /* do not memBufClean() or delete the content, it was absorbed by httpBody */
+ rep->body.set(body);
}
// Make sure error codes get back to the client side for logging and
return rep;
}
-MemBuf *
-ErrorState::BuildContent()
+SBuf
+ErrorState::buildBody()
{
- const char *m = NULL;
-
assert(page_id > ERR_NONE && page_id < error_page_count);
#if USE_ERR_LOCALES
- ErrorPageFile *localeTmpl = NULL;
-
/** error_directory option in squid.conf overrides translations.
* Custom errors are always found either in error_directory or the templates directory.
* Otherwise locate the Accept-Language header
if (err_language && err_language != Config.errorDefaultLanguage)
safe_free(err_language);
- localeTmpl = new ErrorPageFile(err_type_str[page_id], static_cast<err_type>(page_id));
- if (localeTmpl->loadFor(request.getRaw())) {
- m = localeTmpl->text();
- assert(localeTmpl->language());
- err_language = xstrdup(localeTmpl->language());
+ ErrorPageFile localeTmpl(err_type_str[page_id], static_cast<err_type>(page_id));
+ if (localeTmpl.loadFor(request.getRaw())) {
+ inputLocation = localeTmpl.filename;
+ assert(localeTmpl.language());
+ err_language = xstrdup(localeTmpl.language());
+ return compileBody(localeTmpl.text(), true);
}
}
#endif /* USE_ERR_LOCALES */
* If client-specific error templates are not enabled or available.
* fall back to the old style squid.conf settings.
*/
- if (!m) {
- m = error_text[page_id];
#if USE_ERR_LOCALES
- if (!Config.errorDirectory)
- err_language = Config.errorDefaultLanguage;
+ if (!Config.errorDirectory)
+ err_language = Config.errorDefaultLanguage;
#endif
- debugs(4, 2, HERE << "No existing error page language negotiated for " << errorPageName(page_id) << ". Using default error file.");
- }
+ debugs(4, 2, "No existing error page language negotiated for " << errorPageName(page_id) << ". Using default error file.");
+ return compileBody(error_text[page_id], true);
+}
- MemBuf *result = ConvertText(m, true);
-#if USE_ERR_LOCALES
- if (localeTmpl)
- delete localeTmpl;
-#endif
- return result;
+SBuf
+ErrorState::compileBody(const char *input, bool allowRecursion)
+{
+ return compile(input, false, allowRecursion);
}
-MemBuf *ErrorState::ConvertText(const char *text, bool allowRecursion)
+SBuf
+ErrorState::compile(const char *input, bool building_deny_info_url, bool allowRecursion)
{
- MemBuf *content = new MemBuf;
- const char *p;
- const char *m = text;
- assert(m);
- content->init();
-
- while ((p = strchr(m, '%'))) {
- content->append(m, p - m); /* copy */
- const char *t = Convert(*++p, false, allowRecursion); /* convert */
- content->appendf("%s", t); /* copy */
- m = p + 1; /* advance */
+ assert(input);
+
+ Build build;
+ build.building_deny_info_url = building_deny_info_url;
+ build.allowRecursion = allowRecursion;
+ build.input = input;
+
+ auto blockStart = build.input;
+ while (const auto letter = *build.input) {
+ if (letter == '%') {
+ build.output.append(blockStart, build.input - blockStart);
+ compileLegacyCode(build);
+ blockStart = build.input;
+ }
+ else if (letter == '@' && LogformatMagic.cmp(build.input, LogformatMagic.length()) == 0) {
+ build.output.append(blockStart, build.input - blockStart);
+ compileLogformatCode(build);
+ blockStart = build.input;
+ } else {
+ ++build.input;
+ }
+ }
+ build.output.append(blockStart, build.input - blockStart);
+ return build.output;
+}
+
+/// react to a compile() error
+/// \param msg description of what went wrong
+/// \param near approximate start of the problematic input
+/// \param forceBypass whether detection of this error was introduced late,
+/// after old configurations containing this error could have been
+/// successfully validated and deployed (i.e. the admin may not be
+/// able to fix this newly detected but old problem quickly)
+void
+ErrorState::noteBuildError_(const char *msg, const char *near, const bool forceBypass)
+{
+ using ErrorPage::BuildErrorPrinter;
+ const auto runtime = !starting_up;
+ if (runtime || forceBypass) {
+ // swallow this problem because the admin may not be (and/or the page
+ // building code is not) ready to handle throwing consequences
+
+ static unsigned int seenErrors = 0;
+ ++seenErrors;
+
+ const auto debugLevel =
+ (seenErrors > 100) ? DBG_DATA:
+ (starting_up || reconfiguring) ? DBG_CRITICAL:
+ 3; // most other errors have been reported as configuration errors
+
+ // Error fatality depends on the error context: Reconfiguration errors
+ // are, like startup ones, DBG_CRITICAL but will never become FATAL.
+ if (starting_up && seenErrors <= 10)
+ debugs(4, debugLevel, "WARNING: The following configuration error will be fatal in future Squid versions");
+
+ debugs(4, debugLevel, "ERROR: " << BuildErrorPrinter(inputLocation, page_id, msg, near));
+ } else {
+ throw TexcHere(ToSBuf(BuildErrorPrinter(inputLocation, page_id, msg, near)));
+ }
+}
+
+/* ErrorPage::BuildErrorPrinter */
+
+std::ostream &
+ErrorPage::BuildErrorPrinter::printLocation(std::ostream &os) const {
+ if (!inputLocation.isEmpty())
+ return os << inputLocation;
+
+ if (page_id < ERR_NONE || page_id >= error_page_count)
+ return os << "[error page " << page_id << "]"; // should not happen
+
+ if (page_id < ERR_MAX)
+ return os << err_type_str[page_id];
+
+ return os << "deny_info " << ErrorDynamicPages.at(page_id - ERR_MAX)->page_name;
+}
+
+std::ostream &
+ErrorPage::BuildErrorPrinter::print(std::ostream &os) const {
+ printLocation(os) << ": " << msg << " near ";
+
+ // TODO: Add support for prefix printing to Raw
+ const size_t maxContextLength = 15; // plus "..."
+ if (strlen(near) > maxContextLength) {
+ os.write(near, maxContextLength);
+ os << "...";
+ } else {
+ os << near;
}
- if (*m)
- content->appendf("%s", m); /* copy tail */
+ // XXX: We should not be converting (inner) exception to text if we are
+ // going to throw again. See "add arbitrary (re)thrower-supplied details"
+ // TODO in TextException.h for a long-term in-catcher solution.
+ if (std::current_exception())
+ os << "\n additional info: " << CurrentException;
- content->terminate();
+ return os;
+}
- assert((size_t)content->contentSize() == strlen(content->content()));
+/// add error page template to the global index
+static void
+ErrorPage::ImportStaticErrorText(const int page_id, const char *text, const SBuf &inputLocation)
+{
+ assert(!error_text[page_id]);
+ error_text[page_id] = xstrdup(text);
+ ValidateStaticError(page_id, inputLocation);
+}
- return content;
+/// validate static error page
+static void
+ErrorPage::ValidateStaticError(const int page_id, const SBuf &inputLocation)
+{
+ // Supplying nil ALE pointer limits validation to logformat %code
+ // recognition by Format::Token::parse(). This is probably desirable
+ // because actual %code assembly is slow and should not affect validation
+ // when our ALE cannot have any real data (this code is not associated
+ // with any real transaction).
+ ErrorState anErr(err_type(page_id), Http::scNone, nullptr, nullptr);
+ anErr.inputLocation = inputLocation;
+ anErr.validate();
}
#include "http/forward.h"
#include "http/StatusCode.h"
#include "ip/Address.h"
+#include "log/forward.h"
+#include "sbuf/SBuf.h"
#include "SquidString.h"
/* auth/UserRequest.h is empty unless USE_AUTH is defined */
#include "auth/UserRequest.h"
z - dns server error message x
Z - Preformatted error message x
\endverbatim
+ *
+ * Plus logformat %codes embedded using @Squid{%logformat_code} syntax.
*/
class MemBuf;
class StoreEntry;
class wordlist;
+namespace ErrorPage {
+
+class Build;
+
+} // namespace ErrorPage
+
/// \ingroup ErrorPageAPI
class ErrorState
{
CBDATA_CLASS(ErrorState);
public:
- ErrorState(err_type type, Http::StatusCode, HttpRequest * request);
+ ErrorState(err_type type, Http::StatusCode, HttpRequest * request, const AccessLogEntryPointer &al);
ErrorState() = delete; // not implemented.
~ErrorState();
/// Creates a general request forwarding error with the right http_status.
- static ErrorState *NewForwarding(err_type, HttpRequestPointer &);
+ static ErrorState *NewForwarding(err_type, HttpRequestPointer &, const AccessLogEntryPointer &);
/**
* Allocates and initializes an error response
/// set error type-specific detail code
void detailError(int dCode) {detailCode = dCode;}
-private:
- /**
- * Locates error page template to be used for this error
- * and constructs the HTML page content from it.
- */
- MemBuf *BuildContent(void);
+ /// ensures that a future BuildHttpReply() is likely to succeed
+ void validate();
- /**
- * Convert the given template string into textual output
- *
- * \param text The string to be converted
- * \param allowRecursion Whether to convert codes which output may contain codes
- */
- MemBuf *ConvertText(const char *text, bool allowRecursion);
-
- /**
- * Generates the Location: header value for a deny_info error page
- * to be used for this error.
- */
- void DenyInfoLocation(const char *name, HttpRequest *request, MemBuf &result);
+ /// the source of the error template (for reporting purposes)
+ SBuf inputLocation;
- /**
- * Map the Error page and deny_info template % codes into textual output.
- *
- * Several of the codes produce blocks of non-URL compatible results.
- * When processing the deny_info location URL they will be skipped.
- *
- * \param token The token following % which need to be converted
- * \param building_deny_info_url Perform special deny_info actions, such as URL-encoding and token skipping.
- * \ allowRecursion True if the codes which do recursions should converted
- */
- const char *Convert(char token, bool building_deny_info_url, bool allowRecursion);
+private:
+ typedef ErrorPage::Build Build;
+
+ /// locates the right error page template for this error and compiles it
+ SBuf buildBody();
+
+ /// compiles error page or error detail template (i.e. anything but deny_url)
+ /// \param input the template text to be compiled
+ /// \param allowRecursion whether to compile %codes which produce %codes
+ SBuf compileBody(const char *text, bool allowRecursion);
+
+ /// compile a single-letter %code like %D
+ void compileLegacyCode(Build &build);
+
+ /// compile @Squid{%code} sequence containing a single logformat %code
+ void compileLogformatCode(Build &build);
+
+ /// replaces all legacy and logformat %codes in the given input
+ /// \param input the template text to be converted
+ /// \param building_deny_info_url whether input is a deny_info URL parameter
+ /// \param allowRecursion whether to compile %codes which produce %codes
+ /// \returns the given input with all %codes substituted
+ SBuf compile(const char *input, bool building_deny_info_url, bool allowRecursion);
+
+ /// React to a compile() error, throwing if buildContext allows.
+ /// \param msg description of what went wrong
+ /// \param near approximate start of the problematic input
+ void noteBuildError(const char *msg, const char *near) {
+ noteBuildError_(msg, near, false);
+ }
+
+ /// Note a compile() error but do not throw for backwards
+ /// compatibility with older configurations that may have such errors.
+ /// Should eventually be replaced with noteBuildError().
+ /// \param msg description of what went wrong
+ /// \param near approximate start of the problematic input
+ void bypassBuildErrorXXX(const char *msg, const char *near) {
+ noteBuildError_(msg, near, true);
+ }
/**
* CacheManager / Debug dump of the ErrorState object.
char *request_hdrs = nullptr;
char *err_msg = nullptr; /* Preformatted error message from the cache */
+ AccessLogEntryPointer ale; ///< transaction details (or nil)
+
#if USE_OPENSSL
Ssl::ErrorDetail *detail = nullptr;
#endif
/// type-specific detail about the transaction error;
/// overwrites xerrno; overwritten by detail, if any.
int detailCode = ERR_DETAIL_NONE;
+
+private:
+ void noteBuildError_(const char *msg, const char *near, const bool forceBypass);
+
+ static const SBuf LogformatMagic; ///< marks each embedded logformat entry
};
/**
*/
void errorAppendEntry(StoreEntry *entry, ErrorState *err);
-/// \ingroup ErrorPageAPI
-err_type errorReservePageId(const char *page_name);
+/// allocates a new slot for the error page
+err_type errorReservePageId(const char *page_name, const SBuf &cfgLocation);
const char *errorPageName(int pageId); ///< error ID to string
* (a) admin specified custom directory (error_directory)
* (b) default language translation directory (error_default_language)
* (c) English sub-directory where errors should ALWAYS exist
+ * If all of the above fail, setDefault() is called.
*/
- bool loadDefault();
+ void loadDefault();
/**
* Load an error template for a given HTTP request. This function examines the
/// The language used for the template
const char *language() {return errLanguage.termedBuf();}
+ SBuf filename; ///< where the template was loaded from
+
bool silent; ///< Whether to print error messages on cache.log file or not. It is user defined.
protected:
- /// Used to parse (if parsing required) the template data .
- virtual bool parse(const char *buf, int len, bool eof) = 0;
+ /// post-process the loaded template
+ virtual bool parse() { return true; }
+
+ /// recover from loadDefault() failure to load or parse() a template
+ virtual void setDefault() {}
/**
* Try to load the "page_name" template for a given language "lang"
*/
bool tryLoadTemplate(const char *lang);
+ SBuf template_; ///< raw template contents
bool wasLoaded; ///< True if the template data read from disk without any problem
String errLanguage; ///< The error language of the template.
String templateName; ///< The name of the template
/* don't touch incoming, it's a pointer into buffered anyway */
}
-ErrorState *clientBuildError (err_type, Http::StatusCode, char const *, Ip::Address &, HttpRequest *);
+ErrorState *clientBuildError(err_type, Http::StatusCode, char const *, Ip::Address &, HttpRequest *, const AccessLogEntry::Pointer &);
/* This can ONLY be used before we have sent *any* data to the client */
void
flags.error = 1;
/* create an error object */
// XXX: with the in-direction on remote IP. does the http->getConn()->clientConnection exist?
- ErrorState * err = clientBuildError(errorpage, errorstatus, NULL, http->getConn()->clientConnection->remote, http->request);
+ const auto err = clientBuildError(errorpage, errorstatus, nullptr, http->getConn()->clientConnection->remote, http->request, http->al);
err->err_msg = errormessage;
errormessage = NULL;
rep = err->BuildHttpReply();
+ // XXX: Leaking err!
assert (rep->body.hasContent());
size_t errorprogress = rep->body.contentSize();
/* Tell esiSend where to start sending from */
return true;
}
+size_t
+Format::AssembleOne(const char *token, MemBuf &mb, const AccessLogEntryPointer &ale)
+{
+ Token tkn;
+ enum Quoting quote = LOG_QUOTE_NONE;
+ const auto tokenSize = tkn.parse(token, "e);
+ assert(tokenSize > 0);
+ if (ale != nullptr) {
+ Format fmt("SimpleToken");
+ fmt.format = &tkn;
+ fmt.assemble(mb, ale, 0);
+ fmt.format = nullptr;
+ } else
+ mb.append("-", 1);
+ return static_cast<size_t>(tokenSize);
+}
+
void
Format::Format::dump(StoreEntry * entry, const char *directiveName, bool eol) const
{
Format *next;
};
+/// Compiles a single logformat %code expression into the given buffer.
+/// Ignores any input characters after the expression.
+/// \param start where the logformat expression begins
+/// \return the length of the parsed %code expression
+size_t AssembleOne(const char *start, MemBuf &buf, const AccessLogEntryPointer &ale);
+
} // namespace Format
#endif /* _SQUID_FORMAT_FORMAT_H */
}
}
- if (type == LFT_NONE) {
- fatalf("Can't parse configuration token: '%s'\n", def);
- }
+ if (type == LFT_NONE)
+ throw TexcHere(ToSBuf("Unsupported %code: '", def, "'"));
// when {arg} field is after the token (old external_acl_type token syntax)
// but accept only if there was none before the token
GopherStateData *gopherState = static_cast<GopherStateData *>(io.data);
debugs(10, 4, HERE << io.conn << ": '" << gopherState->entry->url() << "'" );
- gopherState->fwd->fail(new ErrorState(ERR_READ_TIMEOUT, Http::scGatewayTimeout, gopherState->fwd->request));
+ gopherState->fwd->fail(new ErrorState(ERR_READ_TIMEOUT, Http::scGatewayTimeout, gopherState->fwd->request, gopherState->fwd->al));
if (Comm::IsConnOpen(io.conn))
io.conn->close();
CommIoCbPtrFun(gopherReadReply, gopherState));
comm_read(conn, buf, read_sz, call);
} else {
- ErrorState *err = new ErrorState(ERR_READ_ERROR, Http::scInternalServerError, gopherState->fwd->request);
+ const auto err = new ErrorState(ERR_READ_ERROR, Http::scInternalServerError, gopherState->fwd->request, gopherState->fwd->al);
err->xerrno = xerrno;
gopherState->fwd->fail(err);
gopherState->serverConn->close();
}
} else if (len == 0 && entry->isEmpty()) {
- gopherState->fwd->fail(new ErrorState(ERR_ZERO_SIZE_OBJECT, Http::scServiceUnavailable, gopherState->fwd->request));
+ gopherState->fwd->fail(new ErrorState(ERR_ZERO_SIZE_OBJECT, Http::scServiceUnavailable, gopherState->fwd->request, gopherState->fwd->al));
gopherState->serverConn->close();
} else if (len == 0) {
/* Connection closed; retrieval done. */
}
if (errflag) {
- ErrorState *err;
- err = new ErrorState(ERR_WRITE_ERROR, Http::scServiceUnavailable, gopherState->fwd->request);
+ const auto err = new ErrorState(ERR_WRITE_ERROR, Http::scServiceUnavailable, gopherState->fwd->request, gopherState->fwd->al);
err->xerrno = xerrno;
err->port = gopherState->fwd->request->url.port();
err->url = xstrdup(entry->url());
debugs(11, 4, serverConnection << ": '" << entry->url() << "'");
if (entry->store_status == STORE_PENDING) {
- fwd->fail(new ErrorState(ERR_READ_TIMEOUT, Http::scGatewayTimeout, fwd->request));
+ fwd->fail(new ErrorState(ERR_READ_TIMEOUT, Http::scGatewayTimeout, fwd->request, fwd->al));
}
closeServer();
// case Comm::COMM_ERROR:
default: // no other flags should ever occur
debugs(11, 2, io.conn << ": read failure: " << xstrerr(rd.xerrno));
- ErrorState *err = new ErrorState(ERR_READ_ERROR, Http::scBadGateway, fwd->request);
+ const auto err = new ErrorState(ERR_READ_ERROR, Http::scBadGateway, fwd->request, fwd->al);
err->xerrno = rd.xerrno;
fwd->fail(err);
flags.do_next_read = false;
assert(error != ERR_NONE);
entry->reset();
- fwd->fail(new ErrorState(error, Http::scBadGateway, fwd->request));
+ fwd->fail(new ErrorState(error, Http::scBadGateway, fwd->request, fwd->al));
flags.do_next_read = false;
closeServer();
mustStop("HttpStateData::continueAfterParsingHeader");
request->hier.notePeerWrite();
if (io.flag) {
- ErrorState *err = new ErrorState(ERR_WRITE_ERROR, Http::scBadGateway, fwd->request);
+ const auto err = new ErrorState(ERR_WRITE_ERROR, Http::scBadGateway, fwd->request, fwd->al);
err->xerrno = io.xerrno;
fwd->fail(err);
closeServer();
// We might also get here if client-side aborts, but then our response
// should not matter because either client-side will provide its own or
// there will be no response at all (e.g., if the the client has left).
- ErrorState *err = new ErrorState(ERR_ICAP_FAILURE, Http::scInternalServerError, fwd->request);
+ const auto err = new ErrorState(ERR_ICAP_FAILURE, Http::scInternalServerError, fwd->request, fwd->al);
err->detailError(ERR_DETAIL_SRV_REQMOD_REQ_BODY);
fwd->fail(err);
}
/* DEBUG: section 76 Internal Squid Object handling */
#include "squid.h"
+#include "AccessLogEntry.h"
#include "CacheManager.h"
#include "comm/Connection.h"
#include "errorpage.h"
* return Http::scNotFound for others
*/
void
-internalStart(const Comm::ConnectionPointer &clientConn, HttpRequest * request, StoreEntry * entry)
+internalStart(const Comm::ConnectionPointer &clientConn, HttpRequest * request, StoreEntry * entry, const AccessLogEntry::Pointer &ale)
{
ErrorState *err;
const SBuf upath = request->url.path();
entry->complete();
} else if (upath.startsWith(mgrPfx)) {
debugs(17, 2, "calling CacheManager due to URL-path " << mgrPfx);
- CacheManager::GetInstance()->Start(clientConn, request, entry);
+ CacheManager::GetInstance()->start(clientConn, request, entry, ale);
} else {
debugObj(76, 1, "internalStart: unknown request:\n",
request, (ObjPackMethod) & httpRequestPack);
- err = new ErrorState(ERR_INVALID_REQ, Http::scNotFound, request);
+ err = new ErrorState(ERR_INVALID_REQ, Http::scNotFound, request, ale);
errorAppendEntry(entry, err);
}
}
#define SQUID_INTERNAL_H_
#include "comm/forward.h"
+#include "log/forward.h"
#include "sbuf/forward.h"
class HttpRequest;
class StoreEntry;
-void internalStart(const Comm::ConnectionPointer &clientConn, HttpRequest *, StoreEntry *);
+void internalStart(const Comm::ConnectionPointer &clientConn, HttpRequest *, StoreEntry *, const AccessLogEntryPointer &);
bool internalCheck(const SBuf &urlPath);
bool internalStaticCheck(const SBuf &urlPath);
char *internalLocalUri(const char *dir, const SBuf &name);
FormatSquidNative.cc \
FormatSquidReferer.cc \
FormatSquidUseragent.cc \
+ forward.h \
ModDaemon.cc \
ModDaemon.h \
ModStdio.cc \
--- /dev/null
+/*
+ * Copyright (C) 1996-2017 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_FORMAT_FORWARD_H
+#define SQUID_FORMAT_FORWARD_H
+
+#include "base/RefCount.h"
+
+class AccessLogEntry;
+typedef RefCount<AccessLogEntry> AccessLogEntryPointer;
+
+#endif /* SQUID_FORMAT_FORWARD_H */
+
/* DEBUG: section 16 Cache Manager API */
#include "squid.h"
+#include "AccessLogEntry.h"
#include "base/AsyncJobCalls.h"
#include "base/TextException.h"
#include "comm/Connection.h"
CBDATA_NAMESPACED_CLASS_INIT(Mgr, Forwarder);
Mgr::Forwarder::Forwarder(const Comm::ConnectionPointer &aConn, const ActionParams &aParams,
- HttpRequest* aRequest, StoreEntry* anEntry):
+ HttpRequest* aRequest, StoreEntry* anEntry, const AccessLogEntryPointer &anAle):
Ipc::Forwarder(new Request(KidIdentifier, 0, aConn, aParams), 10),
- httpRequest(aRequest), entry(anEntry), conn(aConn)
+ httpRequest(aRequest), entry(anEntry), conn(aConn), ale(anAle)
{
debugs(16, 5, HERE << conn);
Must(Comm::IsConnOpen(conn));
Mgr::Forwarder::handleError()
{
debugs(16, DBG_CRITICAL, "ERROR: uri " << entry->url() << " exceeds buffer size");
- sendError(new ErrorState(ERR_INVALID_URL, Http::scUriTooLong, httpRequest));
+ sendError(new ErrorState(ERR_INVALID_URL, Http::scUriTooLong, httpRequest, ale));
mustStop("long URI");
}
void
Mgr::Forwarder::handleTimeout()
{
- sendError(new ErrorState(ERR_LIFETIME_EXP, Http::scRequestTimeout, httpRequest));
+ sendError(new ErrorState(ERR_LIFETIME_EXP, Http::scRequestTimeout, httpRequest, ale));
Ipc::Forwarder::handleTimeout();
}
Mgr::Forwarder::handleException(const std::exception &e)
{
if (entry != NULL && httpRequest != NULL && Comm::IsConnOpen(conn))
- sendError(new ErrorState(ERR_INVALID_RESP, Http::scInternalServerError, httpRequest));
+ sendError(new ErrorState(ERR_INVALID_RESP, Http::scInternalServerError, httpRequest, ale));
Ipc::Forwarder::handleException(e);
}
#include "comm/forward.h"
#include "ipc/Forwarder.h"
+#include "log/forward.h"
#include "mgr/ActionParams.h"
class CommCloseCbParams;
public:
Forwarder(const Comm::ConnectionPointer &aConn, const ActionParams &aParams, HttpRequest* aRequest,
- StoreEntry* anEntry);
+ StoreEntry* anEntry, const AccessLogEntryPointer &anAle);
virtual ~Forwarder();
protected:
StoreEntry* entry; ///< Store entry expecting the response
Comm::ConnectionPointer conn; ///< HTTP client connection descriptor
AsyncCall::Pointer closer; ///< comm_close handler for the HTTP connection
+ AccessLogEntryPointer ale; ///< more transaction details
};
} // namespace Mgr
/* DEBUG: section 16 Cache Manager API */
#include "squid.h"
+#include "AccessLogEntry.h"
#include "base/TextException.h"
#include "comm.h"
#include "comm/Connection.h"
const char *url = aggrAction->command().params.httpUri.termedBuf();
const MasterXaction::Pointer mx = new MasterXaction(XactionInitiator::initIpc);
HttpRequest *req = HttpRequest::FromUrl(url, mx);
- ErrorState err(ERR_INVALID_URL, Http::scNotFound, req);
+ ErrorState err(ERR_INVALID_URL, Http::scNotFound, req, nullptr);
std::unique_ptr<HttpReply> reply(err.BuildHttpReply());
replyBuf.reset(reply->pack());
} else {
delete lastError;
lastError = NULL;
if (fs->code == HIER_DIRECT) {
- lastError = new ErrorState(ERR_DNS_FAIL, Http::scServiceUnavailable, request);
+ lastError = new ErrorState(ERR_DNS_FAIL, Http::scServiceUnavailable, request, al);
lastError->dnsError = details.error;
}
}
if (!ctx) {
debugs(83, DBG_IMPORTANT, "Error initializing TLS connection: No security context.");
} // else CreateClientSession() did the appropriate debugs() already
- ErrorState *anErr = new ErrorState(ERR_SOCKET_FAILURE, Http::scInternalServerError, request.getRaw());
+ const auto anErr = new ErrorState(ERR_SOCKET_FAILURE, Http::scInternalServerError, request.getRaw(), al);
anErr->xerrno = xerrno;
noteNegotiationDone(anErr);
bail(anErr);
" certificate: " << e.what() << "; will now block to " <<
"validate that certificate.");
// fall through to do blocking in-process generation.
- ErrorState *anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw());
+ const auto anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw(), al);
noteNegotiationDone(anErr);
bail(anErr);
ErrorState *anErr = NULL;
if (validatorFailed) {
- anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw());
+ anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw(), al);
} else {
- anErr = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, request.getRaw());
+ anErr = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, request.getRaw(), al);
anErr->detail = errDetails;
/*anErr->xerrno= Should preserved*/
}
": " << Security::ErrorString(ssl_lib_error) << " (" <<
ssl_error << "/" << ret << "/" << xerr << ")");
- ErrorState *anErr = ErrorState::NewForwarding(ERR_SECURE_CONNECT_FAIL, request);
+ const auto anErr = ErrorState::NewForwarding(ERR_SECURE_CONNECT_FAIL, request, al);
anErr->xerrno = sysErrNo;
#if USE_OPENSSL
AsyncJob::swanSong();
if (callback != NULL) { // paranoid: we have left the caller waiting
debugs(83, DBG_IMPORTANT, "BUG: Unexpected state while connecting to a cache_peer or origin server");
- ErrorState *anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw());
+ const auto anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw(), al);
bail(anErr);
assert(!callback);
return;
return len;
}
}
+ // TODO: Support logformat %codes.
return 0;
}
{
public:
explicit ErrorDetailFile(ErrorDetailsList::Pointer const details): TemplateFile("error-details.txt", ERR_NONE) {
- buf.init();
theDetails = details;
}
private:
- MemBuf buf;
ErrorDetailsList::Pointer theDetails;
- virtual bool parse(const char *buf, int len, bool eof);
+ virtual bool parse() override;
};
}// namespace Ssl
inline size_t detailEntryEnd(const char *s, size_t len) {return headersEnd(s, len);}
bool
-Ssl::ErrorDetailFile::parse(const char *buffer, int len, bool eof)
+Ssl::ErrorDetailFile::parse()
{
if (!theDetails)
return false;
- if (len) {
- buf.append(buffer, len);
- }
-
- if (eof)
- buf.append("\n\n", 1);
+ auto buf = template_;
+ buf.append("\n\n"); // ensure detailEntryEnd() finds the last entry
- while (size_t size = detailEntryEnd(buf.content(), buf.contentSize())) {
- const char *e = buf.content() + size;
+ while (const auto size = detailEntryEnd(buf.rawContent(), buf.length())) {
+ auto *s = buf.c_str();
+ const auto e = s + size;
//ignore spaces, new lines and comment lines (starting with #) at the beggining
- const char *s;
- for (s = buf.content(); (*s == '\n' || *s == ' ' || *s == '\t' || *s == '#') && s < e; ++s) {
+ for (; (*s == '\n' || *s == ' ' || *s == '\t' || *s == '#') && s < e; ++s) {
if (*s == '#')
while (s<e && *s != '\n')
++s; // skip untill the end of line
const int detailsParseOk = httpHeaderParseQuotedString(tmp.termedBuf(), tmp.size(), &entry.detail);
tmp = parser.getByName("descr");
const int descrParseOk = httpHeaderParseQuotedString(tmp.termedBuf(), tmp.size(), &entry.descr);
+ // TODO: Validate "descr" and "detail" field values.
if (!detailsParseOk || !descrParseOk) {
debugs(83, DBG_IMPORTANT, HERE <<
buf.consume(size);
}
- debugs(83, 9, HERE << " Remain size: " << buf.contentSize() << " Content: " << buf.content());
+ debugs(83, 9, Raw("unparsed data", buf.rawContent(), buf.length()));
return true;
}
#include "tests/STUB.h"
Mgr::Action::Pointer CacheManager::createNamedAction(char const* action) STUB_RETVAL(NULL)
-void CacheManager::Start(const Comm::ConnectionPointer &conn, HttpRequest * request, StoreEntry * entry)
+void CacheManager::start(const Comm::ConnectionPointer &, HttpRequest *, StoreEntry *, const AccessLogEntryPointer &)
{
std::cerr << HERE << "\n";
STUB
#define STUB_API "errorpage.cc"
#include "tests/STUB.h"
-err_type errorReservePageId(const char *page_name) STUB_RETVAL(err_type())
+err_type errorReservePageId(const char *, const SBuf &) STUB_RETVAL(err_type(0))
void errorAppendEntry(StoreEntry * entry, ErrorState * err) STUB
bool strHdrAcptLangGetItem(const String &hdr, char *lang, int langLen, size_t &pos) STUB_RETVAL(false)
-bool TemplateFile::loadDefault() STUB_RETVAL(false)
+void TemplateFile::loadDefault() STUB
TemplateFile::TemplateFile(char const*, err_type) STUB
bool TemplateFile::loadFor(const HttpRequest *) STUB_RETVAL(false)
// if we have no reply suitable to relay, use 502 Bad Gateway
if (!sz || sz > static_cast<size_t>(connectRespBuf->contentSize()))
- return sendError(new ErrorState(ERR_CONNECT_FAIL, Http::scBadGateway, request.getRaw()),
+ return sendError(new ErrorState(ERR_CONNECT_FAIL, Http::scBadGateway, request.getRaw(), al),
"peer error without reply");
// if we need to send back the server response. write its headers to the client
TunnelStateData *tunnelState = (TunnelStateData *)data;
if (status != Comm::OK) {
- ErrorState *err = new ErrorState(ERR_CONNECT_FAIL, Http::scServiceUnavailable, tunnelState->request.getRaw());
+ const auto err = new ErrorState(ERR_CONNECT_FAIL, Http::scServiceUnavailable, tunnelState->request.getRaw(), tunnelState->al);
err->xerrno = xerrno;
// on timeout is this still: err->xerrno = ETIMEDOUT;
err->port = conn->remote.port();
ch.syncAle(request, http->log_uri);
if (ch.fastCheck().denied()) {
debugs(26, 4, HERE << "MISS access forbidden.");
- err = new ErrorState(ERR_FORWARDING_DENIED, Http::scForbidden, request);
+ err = new ErrorState(ERR_FORWARDING_DENIED, Http::scForbidden, request, http->al);
http->al->http.code = Http::scForbidden;
errorSend(http->getConn()->clientConnection, err);
return;
if (savedError)
return sendError(savedError, "all found paths have failed");
- return sendError(new ErrorState(ERR_CANNOT_FORWARD, Http::scServiceUnavailable, request.getRaw()),
+ return sendError(new ErrorState(ERR_CANNOT_FORWARD, Http::scServiceUnavailable, request.getRaw(), al),
"path selection found no paths");
}
// else continue to use one of the previously noted destinations;
debugs(26,7, "pinned peer connection: " << serverConn);
if (!Comm::IsConnOpen(serverConn)) {
// a PINNED path failure is fatal; do not wait for more paths
- sendError(new ErrorState(ERR_CANNOT_FORWARD, Http::scServiceUnavailable, request.getRaw()),
+ sendError(new ErrorState(ERR_CANNOT_FORWARD, Http::scServiceUnavailable, request.getRaw(), al),
"pinned path failure");
return;
}
StoreEntry *urlres_e = nullptr;
HttpRequest::Pointer request;
HttpRequest::Pointer urlres_r;
+ AccessLogEntry::Pointer ale; ///< details of the requesting transaction
struct {
bool force_menu = false;
virtual void fillChecklist(ACLFilledChecklist &) const;
char *urlres = nullptr;
- AccessLogEntry::Pointer ale; ///< master transaction summary
};
typedef struct {
if (!urlres_r) {
debugs(52, 3, "Bad uri-res URL " << local_urlres);
- ErrorState *err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, r);
+ const auto err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, r, ale);
err->url = xstrdup(local_urlres);
errorAppendEntry(entry, err);
return;
url_entry *urls;
url_entry *u;
url_entry *min_u;
- MemBuf *mb = NULL;
ErrorState *err;
int i;
int urlcnt = 0;
if (rep->sline.status() != Http::scOkay) {
debugs(52, 3, "urnHandleReply: failed.");
- err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, urnState->request.getRaw());
+ err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, urnState->request.getRaw(), urnState->ale);
err->url = xstrdup(e->url());
errorAppendEntry(e, err);
delete rep;
if (!urls) { /* unknown URN error */
debugs(52, 3, "urnTranslateDone: unknown URN " << e->url());
- err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, urnState->request.getRaw());
+ err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, urnState->request.getRaw(), urnState->ale);
err->url = xstrdup(e->url());
errorAppendEntry(e, err);
urnHandleReplyError(urnState, urlres_e);
min_u = urnFindMinRtt(urls, urnState->request->method, NULL);
qsort(urls, urlcnt, sizeof(*urls), url_entry_sort);
e->buffer();
- mb = new MemBuf;
- mb->init();
+ SBuf body;
+ SBuf *mb = &body; // diff reduction hack; TODO: Remove
mb->appendf( "<TITLE>Select URL for %s</TITLE>\n"
"<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n"
"<H2>Select URL for %s</H2>\n"
"</ADDRESS>\n",
APP_FULLNAME, getMyHostname());
rep = new HttpReply;
- rep->setHeaders(Http::scFound, NULL, "text/html", mb->contentSize(), 0, squid_curtime);
+ rep->setHeaders(Http::scFound, NULL, "text/html", mb->length(), 0, squid_curtime);
if (urnState->flags.force_menu) {
debugs(51, 3, "urnHandleReply: forcing menu");
rep->header.putStr(Http::HdrType::LOCATION, min_u->url);
}
- rep->body.setMb(mb);
- /* don't clean or delete mb; rep->body owns it now */
+ rep->body.set(body);
e->replaceHttpReply(rep);
e->complete();
}
safe_free(urls);
- /* mb was absorbed in httpBodySet call, so we must not clean it */
storeUnregister(urnState->sc, urlres_e, urnState);
urnHandleReplyError(urnState, urlres_e);
#ifndef SQUID_URN_H_
#define SQUID_URN_H_
-class AccessLogEntry;
+#include "log/forward.h"
+
class HttpRequest;
class StoreEntry;
-template <class C> class RefCount;
-typedef RefCount<AccessLogEntry> AccessLogEntryPointer;
-
void urnStart(HttpRequest *, StoreEntry *, const AccessLogEntryPointer &ale);
#endif /* SQUID_URN_H_ */
CommIoCbPtrFun(whoisReadReply, this));
comm_read(conn, aBuffer, BUFSIZ, call);
} else {
- ErrorState *err = new ErrorState(ERR_READ_ERROR, Http::scInternalServerError, fwd->request);
+ const auto err = new ErrorState(ERR_READ_ERROR, Http::scInternalServerError, fwd->request, fwd->al);
err->xerrno = xerrno;
fwd->fail(err);
conn->close();