diff --git a/src/client_side_reply.cc b/src/client_side_reply.cc index 3fb52ac9ce3..df1db731340 100644 --- a/src/client_side_reply.cc +++ b/src/client_side_reply.cc @@ -23,6 +23,7 @@ #include "globals.h" #include "HeaderMangling.h" #include "http/Stream.h" +#include "HttpHdrCc.h" #include "HttpHeaderTools.h" #include "HttpReply.h" #include "HttpRequest.h" @@ -1002,12 +1003,18 @@ clientReplyContext::traceReply() triggerInitialStoreRead(); http->storeEntry()->releaseRequest(); http->storeEntry()->buffer(); - MemBuf content; - content.init(); - http->request->pack(&content, true /* hide authorization data */); - const HttpReplyPointer rep(new HttpReply); - rep->setHeaders(Http::scOkay, nullptr, "message/http", content.contentSize(), 0, squid_curtime); - rep->body.set(SBuf(content.buf, content.size)); + + ErrorState err(HTTP_TRACE_REPLY, Http::scOkay, http->request, http->al); + auto rep = err.BuildHttpReply(); + // RFC 9110 specifies that TRACE response is not cacheable + // Reinforce that with explicit caching controls + HttpHdrCc cc; + cc.noStore(true); + cc.mustRevalidate(true); + cc.maxAge(0); + rep->putCc(cc); + // Reinforce that with explicit Expires for old HTTP/1.0 agents + rep->header.putTime(Http::HdrType::EXPIRES, squid_curtime); http->storeEntry()->replaceHttpReply(rep); http->storeEntry()->completeSuccessfully("traceReply() stored the entire response"); } diff --git a/src/error/forward.h b/src/error/forward.h index d341fbc33c2..4eaf96e1393 100644 --- a/src/error/forward.h +++ b/src/error/forward.h @@ -82,6 +82,9 @@ typedef enum { ERR_REQUEST_PARSE_TIMEOUT, // Aborts the connection instead of error page ERR_RELAY_REMOTE, // Sends server reply instead of error page + /* TRACE Response is optional, but SHOULD be equivalent to '%R\n' if sent */ + HTTP_TRACE_REPLY, + /* Cache Manager GUI can install a manager index/home page */ MGR_INDEX, diff --git a/src/errorpage.cc b/src/errorpage.cc index 87698fc52a2..91b1b4bbc65 100644 --- a/src/errorpage.cc +++ b/src/errorpage.cc @@ -381,12 +381,17 @@ TemplateFile::loadDefault() } #endif - /* test default location if failed (templates == English translation base templates) */ + /** test default templates if failed (templates == English translation base templates) */ if (!loaded()) { tryLoadTemplate("templates"); } - /* giving up if failed */ + /** test for any 'soft-coded' template available as default-if-none. */ + if (!loaded()) { + tryInternalDefault(); + } + + /** giving up if failed. Will result in "Internal Error" production. */ if (!loaded()) { debugs(1, (templateCode < TCP_RESET ? DBG_CRITICAL : 3), "WARNING: failed to find or read error text file " << templateName); template_.clear(); @@ -420,6 +425,26 @@ TemplateFile::tryLoadTemplate(const char *lang) return false; } +void +TemplateFile::tryInternalDefault() +{ + if (loaded()) + return; // admin has provided a template. + + // Default templates if no file is found. + static const std::list> SoftCodedErrors = { + { HTTP_TRACE_REPLY, SBuf("%R\n") } + }; + + for (const auto &m: SoftCodedErrors) { + if (m.first == templateCode) { + template_ = m.second; + wasLoaded = parse(); + return; + } + } +} + bool TemplateFile::loadFromFile(const char *path) { diff --git a/src/errorpage.h b/src/errorpage.h index 297b306978d..8131cfa3e2b 100644 --- a/src/errorpage.h +++ b/src/errorpage.h @@ -329,6 +329,9 @@ class TemplateFile */ bool tryLoadTemplate(const char *lang); + /// Try to load a built-in template, if none is configured. + void tryInternalDefault(); + 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.