On 7/04/2013 5:48 p.m., Amos Jeffries wrote:
What this does is convert ErrorState object to using the generic
libformat.la parser and macro expansions instead of its own rather
limited custom ones for error pages and deny_info URL creation.
Alex: If possible could you guys throw this through WebPolygraph to see
if the different in parsing shows up at all?
I'm checking the performance variance for error-only workload now.
Change Summary:
1) register the 1-byte error page macros as libformat codes
2) convert the ErrorState::Convert macro expander to use those codes
instead of char literals
3) extend the libformat to handle several process-wide informational
macros and a human-readabhe RFC1123 time format
4) extend the Format::assemble method to call the adapted
ErrorState::Convert function when it encounters byte codes requiring
ErrorState special handling
5) convert the ErrorState::ConvertText and
ErrorState::DenyInfoLocation methods to use the libformat API to parse
and assemble the output page
NOTES on problems remaining:
*a) Several of the error page macros are better served by other
logformat macro expansions. However on some the interaction between
deny_info and ERR_* template usages has required that the
ErrorState::Convert logics be used for now.
*b) Doing a whole parse cycle for every error response is very costly.
We are already loading the text page files on every error response.
This adds the un-optimized libformat parser lag on top of that. To
avoid this we really need to add a cache for error page templates and
strings, such that each one is only loaded and parsed once. From that
we can more speedily assemble the expansions final output.
*c) For now AccessLogEntry required by the libformat API is generated
by ErrorState just before use. Ideally the code creating the
ErrorState should pass it an already filled out ALE object for the
transaction. This way we will get a lot more of the libformat macros
filled out. That is done in a few places where easily possible, but
many places still cannot do it. This restricts the format codes which
will work on any given error.
Amos
=== modified file 'src/AccessLogEntry.h'
--- src/AccessLogEntry.h 2013-05-23 08:18:09 +0000
+++ src/AccessLogEntry.h 2013-06-01 12:59:51 +0000
@@ -48,10 +48,10 @@
#include "ssl/gadgets.h"
#endif
-/* forward decls */
+class ErrorState;
+class CustomLog;
class HttpReply;
class HttpRequest;
-class CustomLog;
class AccessLogEntry: public RefCountable
{
@@ -60,7 +60,7 @@
typedef RefCount<AccessLogEntry> Pointer;
AccessLogEntry() : url(NULL), tcpClient(), reply(NULL), request(NULL),
- adapted_request(NULL) {}
+ adapted_request(NULL), errorPage(NULL) {}
~AccessLogEntry();
/// Fetch the client IP log string into the given buffer.
@@ -277,6 +277,10 @@
}
icap;
#endif
+
+ /// details of the error page presented to the client (if any)
+ // NP: circular reference, so only to be set by ErrorPageState::ConvertText
+ ErrorState *errorPage;
};
class ACLChecklist;
=== modified file 'src/client_side.cc'
--- src/client_side.cc 2013-05-26 01:57:47 +0000
+++ src/client_side.cc 2013-06-02 00:50:34 +0000
@@ -2539,7 +2539,7 @@
assert (repContext);
repContext->setReplyToError(ERR_TOO_BIG,
Http::scBadRequest, Http::METHOD_NONE, NULL,
- clientConnection->remote, NULL, NULL, NULL);
+ clientConnection, NULL, NULL, NULL);
context->registerWithConn();
context->pullData();
}
@@ -2634,7 +2634,7 @@
// Create an error object and fill it
ErrorState *err = new ErrorState(ERR_SECURE_CONNECT_FAIL,
Http::scServiceUnavailable, request);
- err->src_addr = clientConnection->remote;
+ err->tcpClient = clientConnection;
Ssl::ErrorDetail *errDetail = new Ssl::ErrorDetail(
SQUID_X509_V_ERR_DOMAIN_MISMATCH,
sslServerBump->serverCert.get(), NULL);
@@ -2681,15 +2681,15 @@
assert (repContext);
switch (hp->request_parse_status) {
case Http::scHeaderTooLarge:
- repContext->setReplyToError(ERR_TOO_BIG, Http::scBadRequest,
method, http->uri, conn->clientConnection->remote, NULL, conn->in.buf, NULL);
+ repContext->setReplyToError(ERR_TOO_BIG, Http::scBadRequest,
method, http->uri, conn->clientConnection, NULL, conn->in.buf, NULL);
break;
case Http::scMethodNotAllowed:
repContext->setReplyToError(ERR_UNSUP_REQ,
Http::scMethodNotAllowed, method, http->uri,
- conn->clientConnection->remote, NULL,
conn->in.buf, NULL);
+ conn->clientConnection, NULL,
conn->in.buf, NULL);
break;
default:
repContext->setReplyToError(ERR_INVALID_REQ,
hp->request_parse_status, method, http->uri,
- conn->clientConnection->remote, NULL,
conn->in.buf, NULL);
+ conn->clientConnection, NULL,
conn->in.buf, NULL);
}
assert(context->http->out.offset == 0);
context->pullData();
@@ -2704,7 +2704,7 @@
setLogUri(http, http->uri, true);
clientReplyContext *repContext = dynamic_cast<clientReplyContext
*>(node->data.getRaw());
assert (repContext);
- repContext->setReplyToError(ERR_INVALID_URL, Http::scBadRequest,
method, http->uri, conn->clientConnection->remote, NULL, NULL, NULL);
+ repContext->setReplyToError(ERR_INVALID_URL, Http::scBadRequest,
method, http->uri, conn->clientConnection, NULL, NULL, NULL);
assert(context->http->out.offset == 0);
context->pullData();
goto finish;
@@ -2724,7 +2724,7 @@
clientReplyContext *repContext = dynamic_cast<clientReplyContext
*>(node->data.getRaw());
assert (repContext);
repContext->setReplyToError(ERR_UNSUP_HTTPVERSION,
Http::scHttpVersionNotSupported, method, http->uri,
- conn->clientConnection->remote, NULL,
HttpParserHdrBuf(hp), NULL);
+ conn->clientConnection, NULL,
HttpParserHdrBuf(hp), NULL);
assert(context->http->out.offset == 0);
context->pullData();
goto finish;
@@ -2741,7 +2741,7 @@
setLogUri(http, http->uri, true);
clientReplyContext *repContext = dynamic_cast<clientReplyContext
*>(node->data.getRaw());
assert (repContext);
- repContext->setReplyToError(ERR_INVALID_REQ, Http::scBadRequest,
method, http->uri, conn->clientConnection->remote, NULL, NULL, NULL);
+ repContext->setReplyToError(ERR_INVALID_REQ, Http::scBadRequest,
method, http->uri, conn->clientConnection, NULL, NULL, NULL);
assert(context->http->out.offset == 0);
context->pullData();
goto finish;
@@ -2828,7 +2828,7 @@
clientReplyContext *repContext = dynamic_cast<clientReplyContext
*>(node->data.getRaw());
assert (repContext);
repContext->setReplyToError(ERR_UNSUP_REQ, Http::scNotImplemented,
request->method, NULL,
- conn->clientConnection->remote,
request.getRaw(), NULL, NULL);
+ conn->clientConnection, request.getRaw(),
NULL, NULL);
assert(context->http->out.offset == 0);
context->pullData();
goto finish;
@@ -2841,7 +2841,7 @@
conn->quitAfterError(request.getRaw());
repContext->setReplyToError(ERR_INVALID_REQ,
Http::scLengthRequired, request->method,
NULL,
- conn->clientConnection->remote,
request.getRaw(), NULL, NULL);
+ conn->clientConnection, request.getRaw(),
NULL, NULL);
assert(context->http->out.offset == 0);
context->pullData();
goto finish;
@@ -2856,7 +2856,7 @@
assert (repContext);
conn->quitAfterError(request.getRaw());
repContext->setReplyToError(ERR_INVALID_REQ,
Http::scExpectationFailed, request->method, http->uri,
- conn->clientConnection->remote,
request.getRaw(), NULL, NULL);
+ conn->clientConnection,
request.getRaw(), NULL, NULL);
assert(context->http->out.offset == 0);
context->pullData();
goto finish;
@@ -2897,7 +2897,7 @@
conn->quitAfterError(request.getRaw());
repContext->setReplyToError(ERR_TOO_BIG,
Http::scRequestEntityTooLarge,
Http::METHOD_NONE, NULL,
- conn->clientConnection->remote,
http->request, NULL, NULL);
+ conn->clientConnection, http->request,
NULL, NULL);
assert(context->http->out.offset == 0);
context->pullData();
goto finish;
@@ -4646,3 +4646,16 @@
/* NOTE: pinning.pinned should be kept. This combined with fd == -1 at the
end of a request indicates that the host
* connection has gone away */
}
+
+ErrorState *
+ClientBuildError(err_type page_id, Http::StatusCode status, char const *url,
+ const Comm::ConnectionPointer &client, HttpRequest * request)
+{
+ ErrorState *err = new ErrorState(page_id, status, request);
+ err->tcpClient = client;
+
+ if (url)
+ err->url = xstrdup(url);
+
+ return err;
+}
=== modified file 'src/client_side.h'
--- src/client_side.h 2013-05-30 14:58:49 +0000
+++ src/client_side.h 2013-06-02 00:39:07 +0000
@@ -47,6 +47,7 @@
class ClientHttpRequest;
class clientStreamNode;
class ChunkedCodingParser;
+class ErrorState;
class HelperReply;
namespace AnyP
{
@@ -430,4 +431,6 @@
void clientHttpConnectionsClose(void);
void httpRequestFree(void *);
+ErrorState *ClientBuildError(err_type, Http::StatusCode, char const *url,
const Comm::ConnectionPointer &c, HttpRequest *r);
+
#endif /* SQUID_CLIENTSIDE_H */
=== modified file 'src/client_side_reply.cc'
--- src/client_side_reply.cc 2013-05-30 14:58:49 +0000
+++ src/client_side_reply.cc 2013-06-02 00:32:46 +0000
@@ -72,7 +72,6 @@
/* Local functions */
extern "C" CSS clientReplyStatus;
-ErrorState *clientBuildError(err_type, Http::StatusCode, char const *,
Ip::Address &, HttpRequest *);
/* privates */
@@ -102,7 +101,7 @@
void
clientReplyContext::setReplyToError(
err_type err, Http::StatusCode status, const HttpRequestMethod& method,
char const *uri,
- Ip::Address &addr, HttpRequest * failedrequest, const char
*unparsedrequest,
+ const Comm::ConnectionPointer &client, HttpRequest * failedrequest, const
char *unparsedrequest,
#if USE_AUTH
Auth::UserRequest::Pointer auth_user_request
#else
@@ -110,7 +109,7 @@
#endif
)
{
- ErrorState *errstate = clientBuildError(err, status, uri, addr,
failedrequest);
+ ErrorState *errstate = ClientBuildError(err, status, uri, client,
failedrequest);
if (unparsedrequest)
errstate->request_hdrs = xstrdup(unparsedrequest);
@@ -654,7 +653,7 @@
/// Deny loops
if (r->flags.loopDetected) {
http->al->http.code = Http::scForbidden;
- err = clientBuildError(ERR_ACCESS_DENIED, Http::scForbidden, NULL,
http->getConn()->clientConnection->remote, http->request);
+ err = ClientBuildError(ERR_ACCESS_DENIED, Http::scForbidden, NULL,
http->getConn()->clientConnection, http->request);
createStoreEntry(r->method, RequestFlags());
errorAppendEntry(http->storeEntry(), err);
triggerInitialStoreRead();
@@ -698,8 +697,8 @@
debugs(88, 4, "clientProcessOnlyIfCachedMiss: '" <<
RequestMethodStr(http->request->method) << " " << http->uri << "'");
http->al->http.code = Http::scGateway_Timeout;
- ErrorState *err = clientBuildError(ERR_ONLY_IF_CACHED_MISS,
Http::scGateway_Timeout, NULL,
-
http->getConn()->clientConnection->remote, http->request);
+ ErrorState *err = ClientBuildError(ERR_ONLY_IF_CACHED_MISS,
Http::scGateway_Timeout, NULL,
+ http->getConn()->clientConnection,
http->request);
removeClientStoreReference(&sc, http);
startError(err);
}
@@ -865,8 +864,8 @@
if (EBIT_TEST(entry->flags, ENTRY_SPECIAL)) {
http->logType = LOG_TCP_DENIED;
- ErrorState *err = clientBuildError(ERR_ACCESS_DENIED,
Http::scForbidden, NULL,
-
http->getConn()->clientConnection->remote, http->request);
+ ErrorState *err = ClientBuildError(ERR_ACCESS_DENIED,
Http::scForbidden, NULL,
+ http->getConn()->clientConnection,
http->request);
startError(err);
return;
}
@@ -904,7 +903,7 @@
if (!Config2.onoff.enable_purge) {
http->logType = LOG_TCP_DENIED;
- ErrorState *err = clientBuildError(ERR_ACCESS_DENIED,
Http::scForbidden, NULL, http->getConn()->clientConnection->remote,
http->request);
+ ErrorState *err = ClientBuildError(ERR_ACCESS_DENIED,
Http::scForbidden, NULL, http->getConn()->clientConnection, http->request);
startError(err);
return;
}
@@ -1852,12 +1851,9 @@
void
clientReplyContext::sendBodyTooLargeError()
{
- Ip::Address tmp_noaddr;
- tmp_noaddr.SetNoAddr(); // TODO: make a global const
+ Comm::ConnectionPointer client = http->getConn() != NULL ?
http->getConn()->clientConnection : NULL;
http->logType = LOG_TCP_DENIED_REPLY;
- ErrorState *err = clientBuildError(ERR_TOO_BIG, Http::scForbidden, NULL,
- http->getConn() != NULL ?
http->getConn()->clientConnection->remote : tmp_noaddr,
- http->request);
+ ErrorState *err = ClientBuildError(ERR_TOO_BIG, Http::scForbidden, NULL,
client, http->request);
removeClientStoreReference(&(sc), http);
HTTPMSGUNLOCK(reply);
startError(err);
@@ -1869,9 +1865,8 @@
clientReplyContext::sendPreconditionFailedError()
{
http->logType = LOG_TCP_HIT;
- ErrorState *const err =
- clientBuildError(ERR_PRECONDITION_FAILED, Http::scPreconditionFailed,
- NULL, http->getConn()->clientConnection->remote,
http->request);
+ ErrorState *const err = ClientBuildError(ERR_PRECONDITION_FAILED,
Http::scPreconditionFailed,
+ NULL, http->getConn()->clientConnection,
http->request);
removeClientStoreReference(&sc, http);
HTTPMSGUNLOCK(reply);
startError(err);
@@ -1975,11 +1970,8 @@
if (page_id == ERR_NONE)
page_id = ERR_ACCESS_DENIED;
- Ip::Address tmp_noaddr;
- tmp_noaddr.SetNoAddr();
- err = clientBuildError(page_id, Http::scForbidden, NULL,
- http->getConn() != NULL ?
http->getConn()->clientConnection->remote : tmp_noaddr,
- http->request);
+ Comm::ConnectionPointer client = http->getConn() != NULL ?
http->getConn()->clientConnection : NULL;
+ err = ClientBuildError(page_id, Http::scForbidden, NULL, client,
http->request);
removeClientStoreReference(&sc, http);
@@ -2199,16 +2191,3 @@
*/
http->storeEntry(e);
}
-
-ErrorState *
-clientBuildError(err_type page_id, Http::StatusCode status, char const *url,
- Ip::Address &src_addr, HttpRequest * request)
-{
- ErrorState *err = new ErrorState(page_id, status, request);
- err->src_addr = src_addr;
-
- if (url)
- err->url = xstrdup(url);
-
- return err;
-}
=== modified file 'src/client_side_reply.h'
--- src/client_side_reply.h 2013-05-30 14:58:49 +0000
+++ src/client_side_reply.h 2013-06-01 12:59:51 +0000
@@ -72,7 +72,7 @@
/// replaces current response store entry with the given one
void setReplyToStoreEntry(StoreEntry *e);
/// builds error using clientBuildError() and calls setReplyToError() below
- void setReplyToError(err_type, Http::StatusCode, const HttpRequestMethod&,
char const *, Ip::Address &, HttpRequest *, const char *,
+ void setReplyToError(err_type, Http::StatusCode, const HttpRequestMethod&,
char const *, const Comm::ConnectionPointer &, HttpRequest *, const char *,
#if USE_AUTH
Auth::UserRequest::Pointer);
#else
=== modified file 'src/client_side_request.cc'
--- src/client_side_request.cc 2013-05-26 01:08:42 +0000
+++ src/client_side_request.cc 2013-06-02 00:35:01 +0000
@@ -103,8 +103,6 @@
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 *);
-
CBDATA_CLASS_INIT(ClientRequestContext);
void *
@@ -612,7 +610,7 @@
assert (repContext);
repContext->setReplyToError(ERR_CONFLICT_HOST, Http::scConflict,
http->request->method, NULL,
- http->getConn()->clientConnection->remote,
+ http->getConn()->clientConnection,
http->request,
NULL,
#if USE_AUTH
@@ -838,13 +836,8 @@
page_id = ERR_ACCESS_DENIED;
}
- Ip::Address tmpnoaddr;
- tmpnoaddr.SetNoAddr();
- error = clientBuildError(page_id, status,
- NULL,
- http->getConn() != NULL ?
http->getConn()->clientConnection->remote : tmpnoaddr,
- http->request
- );
+ Comm::ConnectionPointer client = (http->getConn() != NULL ?
http->getConn()->clientConnection : NULL);
+ error = ClientBuildError(page_id, status, NULL, client, http->request);
#if USE_AUTH
error->auth_user_request =
@@ -2078,13 +2071,10 @@
// setReplyToError, but it seems unlikely that the errno reflects the
// true cause of the error at this point, so I did not pass it.
if (calloutContext) {
- Ip::Address noAddr;
- noAddr.SetNoAddr();
ConnStateData * c = getConn();
- calloutContext->error = clientBuildError(ERR_ICAP_FAILURE,
Http::scInternalServerError,
- NULL,
- c != NULL ? c->clientConnection->remote :
noAddr,
- request
+ Comm::ConnectionPointer client = (c ? c->clientConnection : NULL);
+ calloutContext->error = ClientBuildError(ERR_ICAP_FAILURE,
Http::scInternalServerError,
+ NULL, client, request
);
#if USE_AUTH
calloutContext->error->auth_user_request =
=== modified file 'src/errorpage.cc'
--- src/errorpage.cc 2013-03-17 12:19:16 +0000
+++ src/errorpage.cc 2013-06-01 13:51:38 +0000
@@ -31,11 +31,13 @@
*/
#include "squid.h"
#include "cache_cf.h"
+#include "client_side_request.h"
#include "comm/Connection.h"
#include "comm/Write.h"
#include "disk.h"
#include "err_detail_type.h"
#include "errorpage.h"
+#include "format/Format.h"
#include "ftp.h"
#include "Store.h"
#include "html_quote.h"
@@ -568,7 +570,7 @@
return "ERR_UNKNOWN"; /* should not happen */
}
-ErrorState::ErrorState(err_type t, Http::StatusCode status, HttpRequest * req)
:
+ErrorState::ErrorState(err_type t, Http::StatusCode status, HttpRequest * req,
AccessLogEntry *ale) :
type(t),
page_id(t),
err_language(NULL),
@@ -582,7 +584,7 @@
port(0),
dnsError(),
ttl(0),
- src_addr(),
+ tcpClient(),
redirect_url(NULL),
callback(NULL),
callback_data(NULL),
@@ -591,7 +593,10 @@
#if USE_SSL
detail(NULL),
#endif
- detailCode(ERR_DETAIL_NONE)
+ detailCode(ERR_DETAIL_NONE),
+ building_deny_info_url(false),
+ allowRecursion(true),
+ ale_(ale)
{
memset(&ftp, 0, sizeof(ftp));
@@ -601,7 +606,6 @@
if (req != NULL) {
request = req;
HTTPMSGLOCK(request);
- src_addr = req->client_addr;
}
}
@@ -694,6 +698,9 @@
ErrorState::~ErrorState()
{
+ // ensure that the AccessLogEntry object has no reference to us
+ assert(!ale_ || ale_->errorPage == NULL);
+
HTTPMSGUNLOCK(request);
safe_free(redirect_url);
safe_free(url);
@@ -718,7 +725,6 @@
ErrorState::Dump(MemBuf * mb)
{
MemBuf str;
- char ntoabuf[MAX_IPSTRLEN];
str.reset();
/* email subject line */
@@ -746,7 +752,8 @@
str.Printf("TimeStamp: %s\r\n\r\n", mkrfc1123(squid_curtime));
/* - IP stuff */
- str.Printf("ClientIP: %s\r\n", src_addr.NtoA(ntoabuf,MAX_IPSTRLEN));
+// char ntoabuf[MAX_IPSTRLEN];
+//XXX str.Printf("ClientIP: %s\r\n", src_addr.NtoA(ntoabuf,MAX_IPSTRLEN));
if (request && request->hier.host[0] != '\0') {
str.Printf("ServerIP: %s\r\n", request->hier.host);
@@ -796,42 +803,18 @@
#define CVT_BUF_SZ 512
const char *
-ErrorState::Convert(char token, bool building_deny_info_url, bool
allowRecursion)
+ErrorState::Convert(Format::ByteCode_t token)
{
static MemBuf mb;
const char *p = NULL; /* 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];
mb.reset();
switch (token) {
- case 'a':
-#if USE_AUTH
- if (request && request->auth_user_request != NULL)
- p = request->auth_user_request->username();
- if (!p)
-#endif
- p = "-";
- break;
-
- case 'b':
- mb.Printf("%d", getMyPort());
- break;
-
- case 'B':
- if (building_deny_info_url) break;
- p = request ? ftpUrlWith2f(request) : "[no URL]";
- break;
-
- case 'c':
- if (building_deny_info_url) break;
- p = errorPageName(type);
- break;
-
- case 'D':
+ case Format::LFT_ERR_PAGE_UD: // '%D'
if (!allowRecursion)
p = "%D"; // if recursion is not allowed, do not convert
#if USE_SSL
@@ -840,9 +823,11 @@
detail->useRequest(request);
const String &errDetail = detail->toString();
if (errDetail.defined()) {
- MemBuf *detail_mb = ConvertText(errDetail.termedBuf(), false);
- mb.append(detail_mb->content(), detail_mb->contentSize());
- delete detail_mb;
+ allowRecursion = false;
+ MemBuf detail_mb;
+ ConvertText(errDetail.termedBuf(), detail_mb);
+ allowRecursion = true;
+ mb.append(detail_mb.content(), detail_mb.contentSize());
do_quote = 0;
}
}
@@ -851,18 +836,18 @@
mb.Printf("[No Error Detail]");
break;
- case 'e':
+ case Format::LFT_ERR_PAGE_LE: // '%e'
mb.Printf("%d", xerrno);
break;
- case 'E':
+ case Format::LFT_ERR_PAGE_UE: // '%E'
if (xerrno)
mb.Printf("(%d) %s", xerrno, strerror(xerrno));
else
mb.Printf("[No Error]");
break;
- case 'f':
+ case Format::LFT_ERR_PAGE_LF: // '%f'
if (building_deny_info_url) break;
/* FTP REQUEST LINE */
if (ftp.request)
@@ -871,7 +856,7 @@
p = "nothing";
break;
- case 'F':
+ case Format::LFT_ERR_PAGE_UF: // '%F'
if (building_deny_info_url) break;
/* FTP REPLY LINE */
if (ftp.reply)
@@ -880,7 +865,7 @@
p = "nothing";
break;
- case 'g':
+ case Format::LFT_ERR_PAGE_LG: // '%g'
if (building_deny_info_url) break;
/* FTP SERVER RESPONSE */
if (ftp.listing) {
@@ -891,38 +876,13 @@
}
break;
- case 'h':
- mb.Printf("%s", getMyHostname());
- break;
-
- case 'H':
- if (request) {
- if (request->hier.host[0] != '\0') // if non-empty string.
- p = request->hier.host;
- else
- p = request->GetHost();
- } else if (!building_deny_info_url)
- p = "[unknown host]";
- break;
-
- case 'i':
- mb.Printf("%s", src_addr.NtoA(ntoabuf,MAX_IPSTRLEN));
- break;
-
- case 'I':
- if (request && request->hier.tcpServer != NULL)
- p = request->hier.tcpServer->remote.NtoA(ntoabuf,MAX_IPSTRLEN);
- else if (!building_deny_info_url)
- p = "[unknown]";
- break;
-
- case 'l':
+ case Format::LFT_ERR_PAGE_LL: // '%l'
if (building_deny_info_url) break;
mb.append(error_stylesheet.content(), error_stylesheet.contentSize());
do_quote = 0;
break;
- case 'L':
+ case Format::LFT_ERR_PAGE_UL: // '%L'
if (building_deny_info_url) break;
if (Config.errHtmlText) {
mb.Printf("%s", Config.errHtmlText);
@@ -931,7 +891,7 @@
p = "[not available]";
break;
- case 'm':
+ case Format::LFT_ERR_PAGE_LM: // '%m'
if (building_deny_info_url) break;
#if USE_AUTH
p = auth_user_request->denyMessage("[not available]");
@@ -940,28 +900,13 @@
#endif
break;
- case 'M':
- if (request)
- p = RequestMethodStr(request->method);
- else if (!building_deny_info_url)
- p= "[unknown method]";
- break;
-
- case 'o':
+ case Format::LFT_ERR_PAGE_LO: // '%o'
p = request ? request->extacl_message.termedBuf() :
external_acl_message;
if (!p && !building_deny_info_url)
p = "[not available]";
break;
- case 'p':
- if (request) {
- mb.Printf("%d", (int) request->port);
- } else if (!building_deny_info_url) {
- p = "[unknown port]";
- }
- break;
-
- case 'P':
+ case Format::LFT_ERR_PAGE_UP: // '%P'
if (request) {
p = AnyP::ProtocolType_str[request->protocol];
} else if (!building_deny_info_url) {
@@ -969,7 +914,7 @@
}
break;
- case 'R':
+ case Format::LFT_ERR_PAGE_UR: // '%R'
if (building_deny_info_url) {
p = (request->urlpath.size() != 0 ? request->urlpath.termedBuf() :
"/");
no_urlescape = 1;
@@ -999,7 +944,7 @@
}
break;
- case 's':
+ case Format::LFT_ERR_PAGE_LS: // '%s'
/* for backward compat we make %s show the full URL. Drop this in some
future release. */
if (building_deny_info_url) {
p = request ? urlCanonical(request) : url;
@@ -1008,7 +953,7 @@
p = visible_appname_string;
break;
- case 'S':
+ case Format::LFT_ERR_PAGE_US: // '%S'
if (building_deny_info_url) {
p = visible_appname_string;
break;
@@ -1029,49 +974,14 @@
}
break;
- case 't':
- mb.Printf("%s", Time::FormatHttpd(squid_curtime));
- break;
-
- case 'T':
- mb.Printf("%s", mkrfc1123(squid_curtime));
- break;
-
- case 'U':
- /* Using the fake-https version of canonical so error pages see
https:// */
- /* even when the url-path cannot be shown as more than '*' */
- if (request)
- p = urlCanonicalFakeHttps(request);
- else if (url)
- p = url;
- else if (!building_deny_info_url)
- p = "[no URL]";
- break;
-
- case 'u':
- if (request)
- p = urlCanonical(request);
- else if (url)
- p = url;
- else if (!building_deny_info_url)
- p = "[no URL]";
- break;
-
- case 'w':
- if (Config.adminEmail)
- mb.Printf("%s", Config.adminEmail);
- else if (!building_deny_info_url)
- p = "[unknown]";
- break;
-
- case 'W':
+ case Format::LFT_ERR_PAGE_UW: // '%W'
if (building_deny_info_url) break;
if (Config.adminEmail && Config.onoff.emailErrData)
Dump(&mb);
no_urlescape = 1;
break;
- case 'x':
+ case Format::LFT_ERR_PAGE_LX: // '%x'
#if USE_SSL
if (detail)
mb.Printf("%s", detail->errorName());
@@ -1081,7 +991,7 @@
p = "[Unknown Error Code]";
break;
- case 'z':
+ case Format::LFT_ERR_PAGE_LZ: // '%z'
if (building_deny_info_url) break;
if (dnsError.size() > 0)
p = dnsError.termedBuf();
@@ -1091,7 +1001,7 @@
p = "[unknown]";
break;
- case 'Z':
+ case Format::LFT_ERR_PAGE_UZ: // '%Z'
if (building_deny_info_url) break;
if (err_msg)
p = err_msg;
@@ -1099,13 +1009,7 @@
p = "[unknown]";
break;
- case '%':
- p = "%";
- break;
-
default:
- mb.Printf("%%%c", token);
- do_quote = 0;
break;
}
@@ -1128,24 +1032,17 @@
void
ErrorState::DenyInfoLocation(const char *name, HttpRequest *aRequest, MemBuf
&result)
{
+ // XXX: make this process much faster by pre-parsing the deny_info pattern
+ // and just assembling the Location: output here.
+
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.Printf("%s", t); /* copy */
- m = p + 1; /* advance */
- }
-
- if (*m)
- result.Printf("%s", m); /* copy tail */
-
- assert((size_t)result.contentSize() == strlen(result.content()));
+ building_deny_info_url = true;
+ ConvertText(m, result);
+ building_deny_info_url = false;
}
HttpReply *
@@ -1278,7 +1175,8 @@
debugs(4, 2, HERE << "No existing error page language negotiated for "
<< errorPageName(page_id) << ". Using default error file.");
}
- MemBuf *result = ConvertText(m, true);
+ MemBuf *result = new MemBuf;
+ ConvertText(m, *result);
#if USE_ERR_LOCALES
if (localeTmpl)
delete localeTmpl;
@@ -1286,25 +1184,53 @@
return result;
}
-MemBuf *ErrorState::ConvertText(const char *text, bool allowRecursion)
+void
+ErrorState::ConvertText(const char *text, MemBuf &buf)
{
- 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->Printf("%s", t); /* copy */
- m = p + 1; /* advance */
- }
-
- if (*m)
- content->Printf("%s", m); /* copy tail */
-
- assert((size_t)content->contentSize() == strlen(content->content()));
-
- return content;
+ // if request exists, we might have access to the transaction ALE object
+ if (ale_ == NULL && request != NULL &&
request->clientConnectionManager.valid() &&
+ request->clientConnectionManager->currentobject != NULL) {
+ ClientSocketContext::Pointer c =
request->clientConnectionManager->currentobject;
+ ale_ = c->http->al;
+ }
+
+ // if we are in a recursive pattern (%D or %S) ALE may already be setup
+ // or if the creator of this ErrorState was kind enough to supply the real
transaction ALE object
+ if (ale_ == NULL) {
+ ale_ = new AccessLogEntry();
+
+ // fill ale_ with all the details Format::assemble() will need to
process error page codes.
+
+ if (request) {
+ // XXX: we only know about one request here.
+ ale_->request = request;
+ HTTPMSGLOCK(request);
+ ale_->adapted_request = request;
+ HTTPMSGLOCK(request);
+ ale_->_private.method_str = RequestMethodStr(request->method);
+ const char *hierHost = (request->hier.host[0] != '\0' ?
request->hier.host : request->GetHost());
+ ale_->hier.note(request->hier.tcpServer, hierHost);
+
+ if (request->clientConnectionManager.valid())
+ ale_->tcpClient =
request->clientConnectionManager->clientConnection;
+ }
+
+ // if there is no request information (error before parsing it)
+ if (ale_->tcpClient == NULL)
+ ale_->tcpClient = tcpClient;
+
+ ale_->url = (request ? urlCanonicalFakeHttps(request) : url);
+ }
+
+ buf.init();
+
+ // NP: let fmt.assemble() have temporary access to 'this' state info
+ ale_->errorPage = cbdataReference(this);
+
+ // parse the text using Format:: parser
+ Format::Format fmt("errorPageTemporary");
+ fmt.parse(text);
+ fmt.assemble(buf, ale_, 0);
+
+ cbdataReferenceDone(ale_->errorPage);
}
=== modified file 'src/errorpage.h'
--- src/errorpage.h 2013-03-16 04:57:43 +0000
+++ src/errorpage.h 2013-04-19 03:41:32 +0000
@@ -34,10 +34,12 @@
#ifndef SQUID_ERRORPAGE_H
#define SQUID_ERRORPAGE_H
+#include "AccessLogEntry.h"
#include "cbdata.h"
#include "comm/forward.h"
#include "err_detail_type.h"
#include "err_type.h"
+#include "format/ByteCode.h"
#include "http/StatusCode.h"
#include "ip/Address.h"
#include "SquidString.h"
@@ -97,7 +99,7 @@
class ErrorState
{
public:
- ErrorState(err_type type, Http::StatusCode, HttpRequest * request);
+ ErrorState(err_type type, Http::StatusCode, HttpRequest * request,
AccessLogEntry *ale = NULL);
ErrorState(); // not implemented.
~ErrorState();
@@ -109,6 +111,17 @@
/// set error type-specific detail code
void detailError(int dCode) {detailCode = dCode;}
+ /** \deprecated
+ * Map some Error page and deny_info template % codes into textual output.
+ * Only the LFT_ERR_PAGE_* ones are mapped, others are ignored.
+ *
+ * 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
+ */
+ const char *Convert(Format::ByteCode_t token);
+
private:
/**
* Locates error page template to be used for this error
@@ -119,10 +132,10 @@
/**
* 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
+ * \param text The string to be converted
+ * \param buf The buffer to be filled. Will be init()'d before use.
*/
- MemBuf *ConvertText(const char *text, bool allowRecursion);
+ void ConvertText(const char *text, MemBuf &buf);
/**
* Generates the Location: header value for a deny_info error page
@@ -131,18 +144,6 @@
void DenyInfoLocation(const char *name, HttpRequest *request, MemBuf
&result);
/**
- * 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);
-
- /**
* CacheManager / Debug dump of the ErrorState object.
* Writes output into the given MemBuf.
\retval 0 successful completion.
@@ -164,7 +165,9 @@
String dnsError; ///< DNS lookup error message
time_t ttl;
- Ip::Address src_addr;
+ /// client TCP connection details, for use when request does not exist.
+ Comm::ConnectionPointer tcpClient;
+
char *redirect_url;
ERCB *callback;
void *callback_data;
@@ -186,7 +189,17 @@
/// type-specific detail about the transaction error;
/// overwrites xerrno; overwritten by detail, if any.
int detailCode;
+
private:
+ /// Perform special deny_info actions, such as URL-encoding and token
skipping.
+ bool building_deny_info_url;
+
+ /// Whether to convert codes which output may contain codes
+ bool allowRecursion;
+
+ /// an AccessLogEntry we are using to generate output
+ AccessLogEntry::Pointer ale_;
+
CBDATA_CLASS2(ErrorState);
};
=== modified file 'src/esi/Esi.cc'
--- src/esi/Esi.cc 2013-03-18 04:55:51 +0000
+++ src/esi/Esi.cc 2013-06-02 00:41:38 +0000
@@ -1451,8 +1451,6 @@
/* don't touch incoming, it's a pointer into buffered anyway */
}
-ErrorState *clientBuildError (err_type, Http::StatusCode, char const *,
Ip::Address &, HttpRequest *);
-
/* This can ONLY be used before we have sent *any* data to the client */
void
ESIContext::fail ()
@@ -1469,7 +1467,7 @@
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);
+ ErrorState * err = ClientBuildError(errorpage, errorstatus, NULL,
http->getConn()->clientConnection, http->request);
err->err_msg = errormessage;
errormessage = NULL;
rep = err->BuildHttpReply();
=== modified file 'src/format/ByteCode.h'
--- src/format/ByteCode.h 2013-02-11 23:11:12 +0000
+++ src/format/ByteCode.h 2013-04-19 00:56:57 +0000
@@ -136,6 +136,7 @@
LFT_TIME_SUBSECOND,
LFT_TIME_LOCALTIME,
LFT_TIME_GMT,
+ LFT_TIME_GMT_RFC1123,
/* processing time details */
LFT_TIME_TO_HANDLE_REQUEST,
@@ -143,6 +144,12 @@
LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME,
LFT_DNS_WAIT_TIME,
+ /* Squid application details */
+ LFT_SQUID_KID_HOSTNAME,
+ LFT_SQUID_KID_PORT,
+ LFT_SQUID_KID_APPSTRING,
+ LFT_SQUID_KID_ADMINEMAIL,
+
/* Squid internal processing details */
LFT_SQUID_STATUS,
LFT_SQUID_ERROR,
@@ -196,6 +203,26 @@
LFT_SSL_USER_CERT_ISSUER,
#endif
+ /* Squid Error Page special items U=Upper, L=lower cased alphabet codes */
+ LFT_ERR_PAGE_UD,
+ LFT_ERR_PAGE_LE,
+ LFT_ERR_PAGE_UE,
+ LFT_ERR_PAGE_LF,
+ LFT_ERR_PAGE_UF,
+ LFT_ERR_PAGE_LG,
+ LFT_ERR_PAGE_LL,
+ LFT_ERR_PAGE_UL,
+ LFT_ERR_PAGE_LM,
+ LFT_ERR_PAGE_LO,
+ LFT_ERR_PAGE_UP,
+ LFT_ERR_PAGE_UR,
+ LFT_ERR_PAGE_LS,
+ LFT_ERR_PAGE_US,
+ LFT_ERR_PAGE_UW,
+ LFT_ERR_PAGE_LX,
+ LFT_ERR_PAGE_LZ,
+ LFT_ERR_PAGE_UZ,
+
LFT_NOTE,
LFT_PERCENT /* special string cases for escaped
chars */
} ByteCode_t;
=== modified file 'src/format/Format.cc'
--- src/format/Format.cc 2013-05-24 23:40:50 +0000
+++ src/format/Format.cc 2013-06-01 12:59:51 +0000
@@ -9,11 +9,14 @@
#include "format/Quoting.h"
#include "format/Token.h"
#include "fqdncache.h"
+#include "globals.h"
#include "HttpRequest.h"
#include "MemBuf.h"
#include "rfc1738.h"
+#include "SquidConfig.h"
#include "SquidTime.h"
#include "Store.h"
+#include "tools.h"
#include "URL.h"
#if USE_SSL
#include "ssl/ErrorDetail.h"
@@ -442,7 +445,7 @@
break;
case LFT_TIME_LOCALTIME:
-
+ case LFT_TIME_GMT_RFC1123:
case LFT_TIME_GMT: {
const char *spec;
@@ -453,6 +456,12 @@
if (!spec)
spec = "%d/%b/%Y:%H:%M:%S %z";
t = localtime(&squid_curtime);
+
+ } else if (fmt->type == LFT_TIME_GMT_RFC1123) {
+ if (!spec)
+ spec = "%a, %d %b %Y %H:%M:%S GMT";
+ t = gmtime(&squid_curtime);
+
} else {
if (!spec)
spec = "%d/%b/%Y:%H:%M:%S";
@@ -825,6 +834,23 @@
// or internal error messages).
break;
+ case LFT_SQUID_KID_HOSTNAME:
+ out = getMyHostname();
+ break;
+
+ case LFT_SQUID_KID_PORT:
+ outint = getMyPort();
+ doint = 1;
+ break;
+
+ case LFT_SQUID_KID_APPSTRING:
+ out = visible_appname_string;
+ break;
+
+ case LFT_SQUID_KID_ADMINEMAIL:
+ out = Config.adminEmail;
+ break;
+
case LFT_SQUID_STATUS:
if (al->http.timedout || al->http.aborted) {
snprintf(tmp, sizeof(tmp), "%s%s", LogTags_str[al->cache.code],
@@ -866,6 +892,31 @@
}
break;
+ case LFT_ERR_PAGE_UD:
+ case LFT_ERR_PAGE_LE:
+ case LFT_ERR_PAGE_UE:
+ case LFT_ERR_PAGE_LF:
+ case LFT_ERR_PAGE_UF:
+ case LFT_ERR_PAGE_LG:
+ case LFT_ERR_PAGE_LL:
+ case LFT_ERR_PAGE_UL:
+ case LFT_ERR_PAGE_LM:
+ case LFT_ERR_PAGE_LO:
+ case LFT_ERR_PAGE_UP:
+ case LFT_ERR_PAGE_UR:
+ case LFT_ERR_PAGE_LS:
+ case LFT_ERR_PAGE_US:
+ case LFT_ERR_PAGE_UW:
+ case LFT_ERR_PAGE_LX:
+ case LFT_ERR_PAGE_LZ:
+ case LFT_ERR_PAGE_UZ:
+ // NP: these code types are used only by the ErrorState page
generator
+ // for backward compatibility use the old code to generate the
display output
+ if (al->errorPage) {
+ out = al->errorPage->Convert(fmt->type);
+ }
+ break;
+
case LFT_SQUID_HIERARCHY:
if (al->hier.ping.timedout)
mb.append("TIMEOUT_", 8);
=== modified file 'src/format/Token.cc'
--- src/format/Token.cc 2013-05-22 01:04:34 +0000
+++ src/format/Token.cc 2013-06-01 12:59:51 +0000
@@ -1,4 +1,5 @@
#include "squid.h"
+#include "base/StringArea.h"
#include "format/Config.h"
#include "format/Token.h"
#include "format/TokenTableEntry.h"
@@ -31,6 +32,46 @@
{"%", LFT_PERCENT},
+ // 1-byte Tokens used by the Error page templates
+
+ // these ones map easily ..
+ {"a", LFT_USER_LOGIN},
+ {"b", LFT_SQUID_KID_PORT},
+ {"B", LFT_SERVER_REQ_URI},
+ {"c", LFT_SQUID_ERROR},
+ /*{"d", LFT_TIME_TO_HANDLE_REQUEST},*/
+ {"h", LFT_SQUID_KID_HOSTNAME},
+ {"H", LFT_SERVER_FQDN_OR_PEER_NAME},
+ {"i", LFT_CLIENT_IP_ADDRESS},
+ {"I", LFT_SERVER_IP_ADDRESS},
+ {"M", LFT_REQUEST_METHOD},
+ {"p", LFT_SERVER_PORT},
+ {"t", LFT_TIME_LOCALTIME},
+ {"T", LFT_TIME_GMT_RFC1123},
+ {"u", LFT_CLIENT_REQ_URI},
+ {"U", LFT_REQUEST_URI},
+ {"w", LFT_SQUID_KID_ADMINEMAIL},
+
+ // NP: this bunch are specific to the ErrorState object generator
+ {"D", LFT_ERR_PAGE_UD},
+ {"e", LFT_ERR_PAGE_LE},
+ {"E", LFT_ERR_PAGE_UE},
+ {"f", LFT_ERR_PAGE_LF},
+ {"F", LFT_ERR_PAGE_UF},
+ {"g", LFT_ERR_PAGE_LG},
+ {"l", LFT_ERR_PAGE_LL},
+ {"L", LFT_ERR_PAGE_UL},
+ {"m", LFT_ERR_PAGE_LM},
+ {"o", LFT_ERR_PAGE_LO},
+ {"P", LFT_ERR_PAGE_UP},
+ {"R", LFT_ERR_PAGE_UR},
+ {"s", LFT_ERR_PAGE_LS},
+ {"S", LFT_ERR_PAGE_US},
+ {"W", LFT_ERR_PAGE_UW},
+ {"x", LFT_ERR_PAGE_LX},
+ {"z", LFT_ERR_PAGE_LZ},
+ {"Z", LFT_ERR_PAGE_UZ},
+
{NULL, LFT_NONE} /* this must be last */
};
@@ -373,7 +414,12 @@
}
if (type == LFT_NONE) {
- fatalf("Can't parse configuration token: '%s'\n", def);
+ debugs(46, 3, "Can't parse configuration token: '" <<
StringArea(def,min(strlen(def), static_cast<size_t>(10))) << "'");
+ // NP: unknown token found. Assume it should have been %% instead of %.
+ if (def[0] == '%') {
+ type = LFT_PERCENT;
+ ++cur;
+ }
}
if (*cur == ' ') {
=== modified file 'src/redirect.cc'
--- src/redirect.cc 2013-04-04 06:15:00 +0000
+++ src/redirect.cc 2013-04-19 13:10:43 +0000
@@ -292,13 +292,9 @@
clientStreamNode *node = (clientStreamNode
*)http->client_stream.tail->prev->data;
clientReplyContext *repContext = dynamic_cast<clientReplyContext
*>(node->data.getRaw());
assert (repContext);
- Ip::Address tmpnoaddr;
- tmpnoaddr.SetNoAddr();
+ Comm::ConnectionPointer client = http->getConn() != NULL ?
http->getConn()->clientConnection : NULL;
repContext->setReplyToError(ERR_GATEWAY_FAILURE, status,
- http->request->method, NULL,
- http->getConn() != NULL &&
http->getConn()->clientConnection != NULL ?
- http->getConn()->clientConnection->remote
: tmpnoaddr,
- http->request,
+ http->request->method, NULL, client,
http->request,
NULL,
#if USE_AUTH
http->getConn() != NULL &&
http->getConn()->getAuth() != NULL ?