=== 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 ?


