Squid Web Cache master
Loading...
Searching...
No Matches
ErrorDetailManager.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9#include "squid.h"
10#include "base/Raw.h"
11#include "ErrorDetail.h"
12#include "ErrorDetailManager.h"
13#include "errorpage.h"
15#include "mime_header.h"
16#include "sbuf/Stream.h"
17#include "sbuf/StringConvert.h"
18
23
28
30static SBuf
31SlowlyParseQuotedField(const char * const description, const HttpHeader &parser, const char * const fieldName)
32{
33 String fieldValue;
34 if (!parser.hasNamed(fieldName, strlen(fieldName), &fieldValue))
35 throw TextException(ToSBuf("Missing ", description), Here());
36 return Http::SlowlyParseQuotedString(description, fieldValue.termedBuf(), fieldValue.size());
37}
38
40 name(aName),
41 detail(SlowlyParseQuotedField("error 'detail' field", fields, "detail")),
42 descr(SlowlyParseQuotedField("error 'descr' field", fields, "descr"))
43{
44 // TODO: Warn about and report extra/unrecognized error detail fields.
45 // TODO: Validate formatting %codes inside parsed quoted field values.
46}
47
48namespace Ssl
49{
50
53{
54public:
55 explicit ErrorDetailFile(ErrorDetailsList::Pointer const details): TemplateFile("error-details.txt", ERR_NONE) {
56 theDetails = details;
57 }
58
59private:
61 bool parse() override;
62};
63}// namespace Ssl
64
65/******************/
68{
69 const ErrorDetails::const_iterator it = theList.find(value);
70 return it != theList.end() ? &it->second : nullptr;
71}
72
74
76{
77 if (!TheDetailsManager)
78 TheDetailsManager = new Ssl::ErrorDetailsManager;
79
80 assert(TheDetailsManager);
81 return *TheDetailsManager;
82}
83
85{
86 delete TheDetailsManager;
87 TheDetailsManager = nullptr;
88}
89
91{
92 theDefaultErrorDetails = new ErrorDetailsList();
93 ErrorDetailFile detailTmpl(theDefaultErrorDetails);
94 detailTmpl.loadDefault();
95}
96
98Ssl::ErrorDetailsManager::getCachedDetails(const char * const lang) const
99{
100 Cache::iterator it;
101 it = cache.find(SBuf(lang));
102 if (it != cache.end()) {
103 debugs(83, 8, "Found template details in cache for language: " << lang);
104 return it->second;
105 }
106
107 return nullptr;
108}
109
110void
112{
113 const auto &lang = errorDetails->errLanguage;
114 if (cache.find(lang) == cache.end())
115 cache[lang] = errorDetails;
116}
117
120{
121#if USE_ERR_LOCALES
122 String hdr;
123 if (request != nullptr && request->header.getList(Http::HdrType::ACCEPT_LANGUAGE, &hdr)) {
124 ErrorDetailsList::Pointer errDetails = nullptr;
125 //Try to retrieve from cache
126 size_t pos = 0;
127 char lang[256];
128 // Get the first element of the Accept-Language header
129 strHdrAcptLangGetItem(hdr, lang, 256, pos);
130 errDetails = getCachedDetails(lang); // search in cache
131
132 if (!errDetails) { // Else try to load from disk
133 debugs(83, 8, "Creating new ErrDetailList to read from disk");
134 errDetails = new ErrorDetailsList();
135 ErrorDetailFile detailTmpl(errDetails);
136 if (detailTmpl.loadFor(request.getRaw())) {
137 if (detailTmpl.language()) {
138 debugs(83, 8, "Found details on disk for language " << detailTmpl.language());
139 errDetails->errLanguage = detailTmpl.language();
140 cacheDetails(errDetails);
141 }
142 }
143 }
144
145 assert(errDetails);
146 if (const auto entry = errDetails->findRecord(value))
147 return entry;
148 }
149#else
150 (void)request;
151#endif
152
153 return findDefaultDetail(value);
154}
155
158{
159 return theDefaultErrorDetails->findRecord(value);
160}
161
162// Use HttpHeaders parser to parse error-details.txt files
168
169//The end of an error detrail entry is a double "\n". The headersEnd
170// functions can detect it
171inline size_t detailEntryEnd(const char *s, size_t len) {return headersEnd(s, len);}
172
173bool
175{
176 if (!theDetails)
177 return false;
178
179 auto buf = template_;
180 buf.append("\n\n"); // ensure detailEntryEnd() finds the last entry
181
182 while (const auto size = detailEntryEnd(buf.rawContent(), buf.length())) {
183 auto *s = buf.c_str();
184 const auto e = s + size;
185
186 //ignore spaces, new lines and comment lines (starting with #) at the beginning
187 for (; (*s == '\n' || *s == ' ' || *s == '\t' || *s == '#') && s < e; ++s) {
188 if (*s == '#')
189 while (s<e && *s != '\n')
190 ++s; // skip until the end of line
191 }
192
193 if ( s != e) {
194 DetailEntryParser parser;
196 // no applyStatusCodeRules() -- error templates lack HTTP status code
197 if (!parser.parse(s, e - s, interpreter)) {
198 debugs(83, DBG_IMPORTANT, "WARNING: parse error on:" << s);
199 return false;
200 }
201
202 const String errorName = parser.getByName("name");
203 if (!errorName.size()) {
204 debugs(83, DBG_IMPORTANT, "WARNING: invalid or no error detail name on:" << s);
205 return false;
206 }
207
208 Security::ErrorCode ssl_error = Ssl::GetErrorCode(errorName.termedBuf());
209 if (ssl_error != SSL_ERROR_NONE) {
210
211 if (theDetails->findRecord(ssl_error)) {
212 debugs(83, DBG_IMPORTANT, "WARNING: duplicate entry: " << errorName);
213 return false;
214 }
215
216 try {
217 theDetails->theList.try_emplace(ssl_error, StringToSBuf(errorName), parser);
218 }
219 catch (...) {
220 // TODO: Reject the whole file on this and surrounding problems instead of
221 // keeping/using just the previously parsed entries while telling the admin
222 // that we "failed to find or read error text file error-details.txt".
223 debugs(83, DBG_IMPORTANT, "ERROR: Ignoring bad " << errorName << " detail entry: " << CurrentException);
224 return false;
225 }
226
227 } else if (!Ssl::ErrorIsOptional(errorName.termedBuf())) {
228 debugs(83, DBG_IMPORTANT, "WARNING: invalid error detail name: " << errorName);
229 return false;
230 }
231
232 }// else {only spaces and black lines; just ignore}
233
234 buf.consume(size);
235 }
236 debugs(83, 9, Raw("unparsed data", buf.rawContent(), buf.length()));
237 return true;
238}
239
static SBuf SlowlyParseQuotedField(const char *const description, const HttpHeader &parser, const char *const fieldName)
ErrorDetailEntry constructor helper that extracts a quoted HTTP field value.
size_t detailEntryEnd(const char *s, size_t len)
#define Here()
source code location of the caller
Definition Here.h:15
@ hoErrorDetail
Definition HttpHeader.h:39
int size
Definition ModDevPoll.cc:70
SBuf StringToSBuf(const String &s)
create a new SBuf from a String by copying contents
std::ostream & CurrentException(std::ostream &os)
prints active (i.e., thrown but not yet handled) exception
#define assert(EX)
Definition assert.h:17
int parse(const char *header_start, size_t len, Http::ContentLengthInterpreter &interpreter)
String getList(Http::HdrType id) const
String getByName(const SBuf &name) const
bool hasNamed(const SBuf &s, String *value=nullptr) const
HttpHeader header
Definition Message.h:74
Definition Raw.h:21
C * getRaw() const
Definition RefCount.h:89
Definition SBuf.h:94
ErrorDetailEntry(const SBuf &aName, const HttpHeader &)
extracts quoted detail and descr fields from the given header
manages error detail templates
ErrorDetailFile(ErrorDetailsList::Pointer const details)
ErrorDetailsList::Pointer theDetails
bool parse() override
post-process the loaded template
SBuf errLanguage
The language of the error-details.txt template, if any.
const ErrorDetailEntry * findRecord(Security::ErrorCode) const
static void Shutdown()
reset the ErrorDetailsManager instance
ErrorDetailsList::Pointer getCachedDetails(const char *lang) const
Return cached error details list for a given language if exist.
const ErrorDetailEntry * findDefaultDetail(Security::ErrorCode) const
static ErrorDetailsManager * TheDetailsManager
An instance of ErrorDetailsManager to be used by squid (ssl/ErrorDetails.*)
static ErrorDetailsManager & GetInstance()
Instance class.
void cacheDetails(const ErrorDetailsList::Pointer &errorDetails) const
cache the given error details list.
const ErrorDetailEntry * findDetail(Security::ErrorCode value, const HttpRequest::Pointer &request) const
char const * termedBuf() const
Definition SquidString.h:93
size_type size() const
Definition SquidString.h:74
void loadDefault()
Definition errorpage.cc:363
const char * language()
The language used for the template.
Definition errorpage.h:312
bool loadFor(const HttpRequest *request)
Definition errorpage.cc:526
an std::runtime_error with thrower location info
#define DBG_IMPORTANT
Definition Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
@ ERR_NONE
Definition forward.h:15
bool strHdrAcptLangGetItem(const String &hdr, char *lang, int langLen, size_t &pos)
Definition errorpage.cc:472
size_t headersEnd(const char *mime, size_t l, bool &containsObsFold)
SBuf SlowlyParseQuotedString(const char *description, const char *start, size_t length)
int ErrorCode
Squid-defined error code (<0), an error code returned by X.509 API, or zero.
Definition forward.h:134
Definition Xaction.cc:40
void errorDetailClean()
bool ErrorIsOptional(const char *name)
Security::ErrorCode GetErrorCode(const char *name)
The Security::ErrorCode code of the error described by "name".
Definition ErrorDetail.h:30
void errorDetailInitialize()
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition Stream.h:63