/*
- * Copyright (C) 1996-2019 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
#include "clients/forward.h"
#include "comm/Connection.h"
#include "comm/Write.h"
-#include "err_detail_type.h"
+#include "error/Detail.h"
+#include "error/SysErrorDetail.h"
#include "errorpage.h"
#include "fde.h"
#include "format/Format.h"
#include "rfc1738.h"
#include "sbuf/Stream.h"
#include "SquidConfig.h"
-#include "SquidTime.h"
#include "Store.h"
#include "tools.h"
#include "wordlist.h"
#include "ssl/ErrorDetailManager.h"
#endif
+#include <array>
+
/**
\defgroup ErrorPageInternal Error Page Internals
\ingroup ErrorPageAPI
/* local constant and vars */
-/**
- \ingroup ErrorPageInternal
- *
- \note hard coded error messages are not appended with %S
- * automagically to give you more control on the format
- */
-static const struct {
- int type; /* and page_id */
- const char *text;
-}
-
-error_hard_text[] = {
+/// an error page (or a part of an error page) with hard-coded template text
+class HardCodedError {
+public:
+ err_type type; ///< identifies the error (or a special error template part)
+ const char *text; ///< a string literal containing the error template
+};
+/// error messages that cannot be configured/customized externally
+static const std::array<HardCodedError, 7> HardCodedErrors = {
{
- ERR_SQUID_SIGNATURE,
- "\n<br>\n"
- "<hr>\n"
- "<div id=\"footer\">\n"
- "Generated %T by %h (%s)\n"
- "</div>\n"
- "</body></html>\n"
- },
- {
- TCP_RESET,
- "reset"
- },
- {
- ERR_SECURE_ACCEPT_FAIL,
- "secure accept fail"
- },
- {
- ERR_REQUEST_START_TIMEOUT,
- "request start timedout"
- },
- {
- MGR_INDEX,
- "mgr_index"
+ {
+ ERR_SQUID_SIGNATURE,
+ "\n<br>\n"
+ "<hr>\n"
+ "<div id=\"footer\">\n"
+ "Generated %T by %h (%s)\n"
+ "</div>\n"
+ "</body></html>\n"
+ },
+ {
+ TCP_RESET,
+ "reset"
+ },
+ {
+ ERR_CLIENT_GONE,
+ "unexpected client disconnect"
+ },
+ {
+ ERR_SECURE_ACCEPT_FAIL,
+ "secure accept fail"
+ },
+ {
+ ERR_REQUEST_START_TIMEOUT,
+ "request start timedout"
+ },
+ {
+ ERR_REQUEST_PARSE_TIMEOUT,
+ "request parse timedout"
+ },
+ {
+ ERR_RELAY_REMOTE,
+ "relay server response"
+ }
}
};
/* local prototypes */
/// \ingroup ErrorPageInternal
-static const int error_hard_text_count = sizeof(error_hard_text) / sizeof(*error_hard_text);
-
-/// \ingroup ErrorPageInternal
-static char **error_text = NULL;
+static char **error_text = nullptr;
/// \ingroup ErrorPageInternal
static int error_page_count = 0;
const char *text() { return template_.c_str(); }
protected:
- virtual void setDefault() override {
+ void setDefault() override {
template_ = "Internal Error: Missing Template ";
template_.append(templateName.termedBuf());
}
};
/// \ingroup ErrorPageInternal
-err_type &operator++ (err_type &anErr)
+static err_type &
+operator++ (err_type &anErr)
{
int tmp = (int)anErr;
anErr = (err_type)(++tmp);
}
/// \ingroup ErrorPageInternal
-int operator - (err_type const &anErr, err_type const &anErr2)
+static int
+operator -(err_type const &anErr, err_type const &anErr2)
{
return (int)anErr - (int)anErr2;
}
static const char *
errorFindHardText(err_type type)
{
- int i;
-
- for (i = 0; i < error_hard_text_count; ++i)
- if (error_hard_text[i].type == type)
- return error_hard_text[i].text;
-
- return NULL;
+ for (const auto &m: HardCodedErrors) {
+ if (m.type == type)
+ return m.text;
+ }
+ return nullptr;
}
TemplateFile::TemplateFile(const char *name, const err_type code): silent(false), wasLoaded(false), templateName(name), templateCode(code)
/** test error_default_language location */
if (!loaded() && Config.errorDefaultLanguage) {
if (!tryLoadTemplate(Config.errorDefaultLanguage)) {
- debugs(1, (templateCode < TCP_RESET ? DBG_CRITICAL : 3), "Unable to load default error language files. Reset to backups.");
+ debugs(1, (templateCode < TCP_RESET ? DBG_CRITICAL : 3), "ERROR: Unable to load default error language files. Reset to backups.");
}
}
#endif
if ( strlen(lang) == 2) {
/* TODO glob the error directory for sub-dirs matching: <tag> '-*' */
/* use first result. */
- debugs(4,2, HERE << "wildcard fallback errors not coded yet.");
+ debugs(4,2, "wildcard fallback errors not coded yet.");
}
#endif
char lang[256];
size_t pos = 0; // current parsing position in header string
- debugs(4, 6, HERE << "Testing Header: '" << hdr << "'");
+ debugs(4, 6, "Testing Header: '" << hdr << "'");
while ( strHdrAcptLangGetItem(hdr, lang, 256, pos) ) {
/* wildcard uses the configured default language */
if (lang[0] == '*' && lang[1] == '\0') {
- debugs(4, 6, HERE << "Found language '" << lang << "'. Using configured default.");
+ debugs(4, 6, "Found language '" << lang << "'. Using configured default.");
return false;
}
- debugs(4, 6, HERE << "Found language '" << lang << "', testing for available template");
+ debugs(4, 6, "Found language '" << lang << "', testing for available template");
if (tryLoadTemplate(lang)) {
/* store the language we found for the Content-Language reply header */
debugs(4, DBG_IMPORTANT, "WARNING: Error Pages Missing Language: " << lang);
}
}
+#else
+ (void)request;
#endif
return loaded();
void
errorAppendEntry(StoreEntry * entry, ErrorState * err)
{
- assert(entry->mem_obj != NULL);
+ assert(entry->mem_obj != nullptr);
assert (entry->isEmpty());
- debugs(4, 4, "Creating an error page for entry " << entry <<
- " with errorstate " << err <<
- " page id " << err->page_id);
+ debugs(4, 4, "storing " << err << " in " << *entry);
if (entry->store_status != STORE_PENDING) {
debugs(4, 2, "Skipping error page due to store_status: " << entry->store_status);
errorSendComplete(const Comm::ConnectionPointer &conn, char *, size_t size, Comm::Flag errflag, int, void *data)
{
ErrorState *err = static_cast<ErrorState *>(data);
- debugs(4, 3, HERE << conn << ", size=" << size);
+ debugs(4, 3, conn << ", size=" << size);
if (errflag != Comm::ERR_CLOSING) {
if (err->callback) {
if (err_language != Config.errorDefaultLanguage)
#endif
safe_free(err_language);
-#if USE_OPENSSL
- delete detail;
-#endif
}
int
str.appendf("DNS ErrMsg: %s\r\n", dnsError.termedBuf());
/* - TimeStamp */
- str.appendf("TimeStamp: %s\r\n\r\n", mkrfc1123(squid_curtime));
+ str.appendf("TimeStamp: %s\r\n\r\n", Time::FormatRfc1123(squid_curtime));
/* - IP stuff */
str.appendf("ClientIP: %s\r\n", src_addr.toStr(ntoabuf,MAX_IPSTRLEN));
ErrorState::compileLegacyCode(Build &build)
{
static MemBuf mb;
- const char *p = NULL; /* takes priority over mb if set */
+ const char *p = nullptr; /* takes priority over mb if set */
int do_quote = 1;
int no_urlescape = 0; /* if true then item is NOT to be further URL-encoded */
char ntoabuf[MAX_IPSTRLEN];
case 'D':
if (!build.allowRecursion)
p = "%D"; // if recursion is not allowed, do not convert
-#if USE_OPENSSL
- // currently only SSL error details implemented
else if (detail) {
- detail->useRequest(request.getRaw());
- const String &errDetail = detail->toString();
- if (errDetail.size() > 0) {
- const auto compiledDetail = compileBody(errDetail.termedBuf(), false);
- mb.append(compiledDetail.rawContent(), compiledDetail.length());
- do_quote = 0;
- }
+ auto rawDetail = detail->verbose(request);
+ // XXX: Performance regression. c_str() reallocates
+ const auto compiledDetail = compileBody(rawDetail.c_str(), false);
+ mb.append(compiledDetail.rawContent(), compiledDetail.length());
+ do_quote = 0;
}
-#endif
if (!mb.contentSize())
mb.append("[No Error Detail]", 17);
break;
case 'O':
if (!building_deny_info_url)
do_quote = 0;
+ [[fallthrough]];
case 'o':
p = request ? request->extacl_message.termedBuf() : external_acl_message;
if (!p && !building_deny_info_url)
case 'R':
if (building_deny_info_url) {
- if (request != NULL) {
+ if (request != nullptr) {
const SBuf &tmp = request->url.path();
mb.append(tmp.rawContent(), tmp.length());
no_urlescape = 1;
break;
case 'T':
- mb.appendf("%s", mkrfc1123(squid_curtime));
+ mb.appendf("%s", Time::FormatRfc1123(squid_curtime));
break;
case 'U':
break;
case 'x':
-#if USE_OPENSSL
- if (detail)
- mb.appendf("%s", detail->errorName());
- else
-#endif
- if (!building_deny_info_url)
- p = "[Unknown Error Code]";
+ if (detail) {
+ const auto brief = detail->brief();
+ mb.append(brief.rawContent(), brief.length());
+ } else if (!building_deny_info_url) {
+ p = "[Unknown Error Code]";
+ }
break;
case 'z':
status = Http::scTemporaryRedirect;
}
- rep->setHeaders(status, NULL, "text/html;charset=utf-8", 0, 0, -1);
+ rep->setHeaders(status, nullptr, "text/html;charset=utf-8", 0, 0, -1);
if (request) {
auto location = compile(urlTemplate, true, true);
httpHeaderPutStrf(&rep->header, Http::HdrType::X_SQUID_ERROR, "%d %s", httpStatus, "Access Denied");
} else {
const auto body = buildBody();
- rep->setHeaders(httpStatus, NULL, "text/html;charset=utf-8", body.length(), 0, -1);
+ rep->setHeaders(httpStatus, nullptr, "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
*/
if (!Config.errorDirectory) {
/* We 'negotiated' this ONLY from the Accept-Language. */
- rep->header.delById(Http::HdrType::VARY);
- rep->header.putStr(Http::HdrType::VARY, "Accept-Language");
+ static const SBuf acceptLanguage("Accept-Language");
+ rep->header.updateOrAddStr(Http::HdrType::VARY, acceptLanguage);
}
/* add the Content-Language header according to RFC section 14.12 */
// Make sure error codes get back to the client side for logging and
// error tracking.
if (request) {
- int edc = ERR_DETAIL_NONE; // error detail code
-#if USE_OPENSSL
if (detail)
- edc = detail->errorNo();
+ request->detailError(type, detail);
else
-#endif
- if (detailCode)
- edc = detailCode;
- else
- edc = xerrno;
- request->detailError(type, edc);
+ request->detailError(type, SysErrorDetail::NewIfAny(xerrno));
}
return rep;
if (!Config.errorDirectory)
err_language = Config.errorDefaultLanguage;
#endif
- debugs(4, 2, "No existing error page language negotiated for " << errorPageName(page_id) << ". Using default error file.");
+ debugs(4, 2, "No existing error page language negotiated for " << this << ". Using default error file.");
return compileBody(error_text[page_id], true);
}
anErr.validate();
}
+std::ostream &
+operator <<(std::ostream &os, const ErrorState *err)
+{
+ if (err)
+ os << errorPageName(err->page_id);
+ else
+ os << "[none]";
+ return os;
+}
+