ErrorDetailManager.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2023 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
20{
22}
23
25{
27}
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(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 char *lang = errorDetails->errLanguage.termedBuf();
114 assert(lang);
115 if (cache.find(lang) == cache.end())
116 cache[lang] = errorDetails;
117}
118
121{
122#if USE_ERR_LOCALES
123 String hdr;
124 if (request != nullptr && request->header.getList(Http::HdrType::ACCEPT_LANGUAGE, &hdr)) {
125 ErrorDetailsList::Pointer errDetails = nullptr;
126 //Try to retrieve from cache
127 size_t pos = 0;
128 char lang[256];
129 // Get the first ellement of the Accept-Language header
130 strHdrAcptLangGetItem(hdr, lang, 256, pos);
131 errDetails = getCachedDetails(lang); // search in cache
132
133 if (!errDetails) { // Else try to load from disk
134 debugs(83, 8, "Creating new ErrDetailList to read from disk");
135 errDetails = new ErrorDetailsList();
136 ErrorDetailFile detailTmpl(errDetails);
137 if (detailTmpl.loadFor(request.getRaw())) {
138 if (detailTmpl.language()) {
139 debugs(83, 8, "Found details on disk for language " << detailTmpl.language());
140 errDetails->errLanguage = detailTmpl.language();
141 cacheDetails(errDetails);
142 }
143 }
144 }
145
146 assert(errDetails);
147 if (const auto entry = errDetails->findRecord(value))
148 return entry;
149 }
150#else
151 (void)request;
152#endif
153
154 return findDefaultDetail(value);
155}
156
159{
160 return theDefaultErrorDetails->findRecord(value);
161}
162
163// Use HttpHeaders parser to parse error-details.txt files
165{
166public:
168};
169
170//The end of an error detrail entry is a double "\n". The headersEnd
171// functions can detect it
172inline size_t detailEntryEnd(const char *s, size_t len) {return headersEnd(s, len);}
173
174bool
176{
177 if (!theDetails)
178 return false;
179
180 auto buf = template_;
181 buf.append("\n\n"); // ensure detailEntryEnd() finds the last entry
182
183 while (const auto size = detailEntryEnd(buf.rawContent(), buf.length())) {
184 auto *s = buf.c_str();
185 const auto e = s + size;
186
187 //ignore spaces, new lines and comment lines (starting with #) at the beginning
188 for (; (*s == '\n' || *s == ' ' || *s == '\t' || *s == '#') && s < e; ++s) {
189 if (*s == '#')
190 while (s<e && *s != '\n')
191 ++s; // skip until the end of line
192 }
193
194 if ( s != e) {
195 DetailEntryParser parser;
197 // no applyStatusCodeRules() -- error templates lack HTTP status code
198 if (!parser.parse(s, e - s, interpreter)) {
199 debugs(83, DBG_IMPORTANT, "WARNING: parse error on:" << s);
200 return false;
201 }
202
203 const String errorName = parser.getByName("name");
204 if (!errorName.size()) {
205 debugs(83, DBG_IMPORTANT, "WARNING: invalid or no error detail name on:" << s);
206 return false;
207 }
208
209 Security::ErrorCode ssl_error = Ssl::GetErrorCode(errorName.termedBuf());
210 if (ssl_error != SSL_ERROR_NONE) {
211
212 if (theDetails->findRecord(ssl_error)) {
213 debugs(83, DBG_IMPORTANT, "WARNING: duplicate entry: " << errorName);
214 return false;
215 }
216
217 try {
218 theDetails->theList.try_emplace(ssl_error, StringToSBuf(errorName), parser);
219 }
220 catch (...) {
221 // TODO: Reject the whole file on this and surrounding problems instead of
222 // keeping/using just the previously parsed entries while telling the admin
223 // that we "failed to find or read error text file error-details.txt".
224 debugs(83, DBG_IMPORTANT, "ERROR: Ignoring bad " << errorName << " detail entry: " << CurrentException);
225 return false;
226 }
227
228 } else if (!Ssl::ErrorIsOptional(errorName.termedBuf())) {
229 debugs(83, DBG_IMPORTANT, "WARNING: invalid error detail name: " << errorName);
230 return false;
231 }
232
233 }// else {only spaces and black lines; just ignore}
234
235 buf.consume(size);
236 }
237 debugs(83, 9, Raw("unparsed data", buf.rawContent(), buf.length()));
238 return true;
239}
240
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:75
SBuf StringToSBuf(const String &s)
create a new SBuf from a String by copying contents
Definition: StringConvert.h:17
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)
Definition: HttpHeader.cc:350
String getList(Http::HdrType id) const
Definition: HttpHeader.cc:789
String getByName(const SBuf &name) const
Definition: HttpHeader.cc:849
bool hasNamed(const SBuf &s, String *value=nullptr) const
Definition: HttpHeader.cc:866
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
String 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:92
size_type size() const
Definition: SquidString.h:73
void loadDefault()
Definition: errorpage.cc:356
const char * language()
The language used for the template.
Definition: errorpage.h:313
bool loadFor(const HttpRequest *request)
Definition: errorpage.cc:519
an std::runtime_error with thrower location info
Definition: TextException.h:21
#define DBG_IMPORTANT
Definition: Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:194
@ ERR_NONE
Definition: forward.h:15
bool strHdrAcptLangGetItem(const String &hdr, char *lang, int langLen, size_t &pos)
Definition: errorpage.cc:465
size_t headersEnd(const char *mime, size_t l, bool &containsObsFold)
Definition: mime_header.cc:17
SBuf SlowlyParseQuotedString(const char *description, const char *start, size_t length)
@ ACCEPT_LANGUAGE
int ErrorCode
Squid-defined error code (<0), an error code returned by X.509 API, or zero.
Definition: forward.h:132
Definition: Xaction.cc:40
void errorDetailClean()
bool ErrorIsOptional(const char *name)
Definition: ErrorDetail.cc:149
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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors