/*
- * $Id: errorpage.cc,v 1.74 1997/10/16 23:59:54 wessels Exp $
+ * $Id: errorpage.cc,v 1.164 2001/04/14 00:03:22 hno Exp $
*
* DEBUG: section 4 Error Generation
* AUTHOR: Duane Wessels
*
- * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
- * --------------------------------------------------------
+ * SQUID Web Proxy Cache http://www.squid-cache.org/
+ * ----------------------------------------------------------
*
- * Squid is the result of efforts by numerous individuals from the
- * Internet community. Development is led by Duane Wessels of the
- * National Laboratory for Applied Network Research and funded by
- * the National Science Foundation.
+ * Squid is the result of efforts by numerous individuals from
+ * the Internet community; see the CONTRIBUTORS file for full
+ * details. Many organizations have provided support for Squid's
+ * development; see the SPONSORS file for full details. Squid is
+ * Copyrighted (C) 2001 by the Regents of the University of
+ * California; see the COPYRIGHT file for full details. Squid
+ * incorporates software developed and/or copyrighted by other
+ * sources; see the CREDITS file for full details.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+/*
+ * Abstract: These routines are used to generate error messages to be
+ * sent to clients. The error type is used to select between
+ * the various message formats. (formats are stored in the
+ * Config.errorDirectory)
*/
#include "squid.h"
-const char *err_string[] =
-{
- "ERR_NONE",
- "ERR_NO_CLIENTS",
- "ERR_READ_TIMEOUT",
- "ERR_LIFETIME_EXP",
- "ERR_READ_ERROR",
- "ERR_WRITE_ERROR",
- "ERR_CLIENT_ABORT",
- "ERR_CONNECT_FAIL",
- "ERR_INVALID_REQ",
- "ERR_UNSUP_REQ",
- "ERR_INVALID_URL",
- "ERR_SOCKET_FAILURE",
- "ERR_DNS_FAIL",
- "ERR_CANNOT_FORWARD",
- "ERR_NO_RELAY",
- "ERR_ZERO_SIZE_OBJECT",
- "ERR_FTP_DISABLED",
- "ERR_ACCESS_DENIED",
- "ERR_MAX"
+
+/* local types */
+
+typedef struct {
+ int id;
+ char *page_name;
+} ErrorDynamicPageInfo;
+
+/* local constant and vars */
+
+/*
+ * note: hard coded error messages are not appended with %S automagically
+ * to give you more control on the format
+ */
+static const struct {
+ int type; /* and page_id */
+ const char *text;
+} error_hard_text[] = {
+
+ {
+ ERR_SQUID_SIGNATURE,
+ "\n<br clear=\"all\">\n"
+ "<hr noshade size=1>\n"
+ "Generated %T by %h (%s)\n"
+ "</BODY></HTML>\n"
+ }
};
-static char *error_text[ERR_MAX];
+static Stack ErrorDynamicPages;
+
+/* local prototypes */
-static void errorStateFree _PARAMS((ErrorState * err));
-static char *errorConvert _PARAMS((char token, ErrorState * err));
-static char *errorBuildBuf _PARAMS((ErrorState * err, int *len));
+static const int error_hard_text_count = sizeof(error_hard_text) / sizeof(*error_hard_text);
+static char **error_text = NULL;
+static int error_page_count = 0;
+
+static char *errorTryLoadText(const char *page_name, const char *dir);
+static char *errorLoadText(const char *page_name);
+static const char *errorFindHardText(err_type type);
+static ErrorDynamicPageInfo *errorDynamicPageInfoCreate(int id, const char *page_name);
+static void errorDynamicPageInfoDestroy(ErrorDynamicPageInfo * info);
+static MemBuf errorBuildContent(ErrorState * err);
+static const char *errorConvert(char token, ErrorState * err);
static CWCB errorSendComplete;
+/*
+ * Function: errorInitialize
+ *
+ * Abstract: This function finds the error messages formats, and stores
+ * them in error_text[];
+ *
+ * Global effects:
+ * error_text[] - is modified
+ */
void
errorInitialize(void)
{
err_type i;
+ const char *text;
+ error_page_count = ERR_MAX + ErrorDynamicPages.count;
+ error_text = xcalloc(error_page_count, sizeof(char *));
+ for (i = ERR_NONE, i++; i < error_page_count; i++) {
+ safe_free(error_text[i]);
+ /* hard-coded ? */
+ if ((text = errorFindHardText(i)))
+ error_text[i] = xstrdup(text);
+ else if (i < ERR_MAX) {
+ /* precompiled ? */
+ error_text[i] = errorLoadText(err_type_str[i]);
+ } else {
+ /* dynamic */
+ ErrorDynamicPageInfo *info = ErrorDynamicPages.items[i - ERR_MAX];
+ assert(info && info->id == i && info->page_name);
+ error_text[i] = errorLoadText(info->page_name);
+ }
+ assert(error_text[i]);
+ }
+}
+
+void
+errorClean(void)
+{
+ if (error_text) {
+ int i;
+ for (i = ERR_NONE + 1; i < error_page_count; i++)
+ safe_free(error_text[i]);
+ safe_free(error_text);
+ }
+ while (ErrorDynamicPages.count)
+ errorDynamicPageInfoDestroy(stackPop(&ErrorDynamicPages));
+ error_page_count = 0;
+}
+
+static const char *
+errorFindHardText(err_type type)
+{
+ int i;
+ for (i = 0; i < error_hard_text_count; i++)
+ if (error_hard_text[i].type == type)
+ return error_hard_text[i].text;
+ return NULL;
+}
+
+
+static char *
+errorLoadText(const char *page_name)
+{
+ /* test configured location */
+ char *text = errorTryLoadText(page_name, Config.errorDirectory);
+ /* test default location if failed */
+ if (!text && strcmp(Config.errorDirectory, DEFAULT_SQUID_ERROR_DIR))
+ text = errorTryLoadText(page_name, DEFAULT_SQUID_ERROR_DIR);
+ /* giving up if failed */
+ if (!text)
+ fatal("failed to find or read error text file.");
+ return text;
+}
+
+static char *
+errorTryLoadText(const char *page_name, const char *dir)
+{
int fd;
char path[MAXPATHLEN];
struct stat sb;
- assert(sizeof(err_string) == (ERR_MAX + 1) * 4);
- for (i = ERR_NONE + 1; i < ERR_MAX; i++) {
- snprintf(path, MAXPATHLEN, "%s/%s",
- Config.errorDirectory, err_string[i]);
- fd = file_open(path, O_RDONLY, NULL, NULL);
- if (fd < 0) {
- debug(4, 0) ("errorInitialize: %s: %s\n", path, xstrerror());
- fatal("Failed to open error text file");
- }
- if (fstat(fd, &sb) < 0)
- fatal_dump("stat() failed on error text file");
- safe_free(error_text[i]);
- error_text[i] = xcalloc(sb.st_size, 1);
- if (read(fd, error_text[i], sb.st_size) != sb.st_size)
- fatal_dump("failed to fully read error text file");
- file_close(fd);
+ char *text;
+
+ snprintf(path, sizeof(path), "%s/%s", dir, page_name);
+ fd = file_open(path, O_RDONLY | O_TEXT);
+ if (fd < 0 || fstat(fd, &sb) < 0) {
+ debug(4, 0) ("errorTryLoadText: '%s': %s\n", path, xstrerror());
+ if (fd >= 0)
+ file_close(fd);
+ return NULL;
+ }
+ text = xcalloc(sb.st_size + 2 + 1, 1); /* 2 == space for %S */
+ if (FD_READ_METHOD(fd, text, sb.st_size) != sb.st_size) {
+ debug(4, 0) ("errorTryLoadText: failed to fully read: '%s': %s\n",
+ path, xstrerror());
+ xfree(text);
+ text = NULL;
}
+ file_close(fd);
+ if (strstr(text, "%s") == NULL)
+ strcat(text, "%S"); /* add signature */
+ return text;
+}
+
+static ErrorDynamicPageInfo *
+errorDynamicPageInfoCreate(int id, const char *page_name)
+{
+ ErrorDynamicPageInfo *info = xcalloc(1, sizeof(ErrorDynamicPageInfo));
+ info->id = id;
+ info->page_name = xstrdup(page_name);
+ return info;
}
static void
+errorDynamicPageInfoDestroy(ErrorDynamicPageInfo * info)
+{
+ assert(info);
+ xfree(info->page_name);
+ xfree(info);
+}
+
+int
+errorReservePageId(const char *page_name)
+{
+ ErrorDynamicPageInfo *info =
+ errorDynamicPageInfoCreate(ERR_MAX + ErrorDynamicPages.count, page_name);
+ stackPush(&ErrorDynamicPages, info);
+ return info->id;
+}
+
+static const char *
+errorPageName(int pageId)
+{
+ if (pageId >= ERR_NONE && pageId < ERR_MAX) /* common case */
+ return err_type_str[pageId];
+ if (pageId >= ERR_MAX && pageId - ERR_MAX < ErrorDynamicPages.count)
+ return ((ErrorDynamicPageInfo *) ErrorDynamicPages.
+ items[pageId - ERR_MAX])->page_name;
+ return "ERR_UNKNOWN"; /* should not happen */
+}
+
+/*
+ * Function: errorCon
+ *
+ * Abstract: This function creates a ErrorState object.
+ */
+ErrorState *
+errorCon(err_type type, http_status status)
+{
+ ErrorState *err;
+ err = cbdataAlloc(ErrorState);
+ err->page_id = type; /* has to be reset manually if needed */
+ err->type = type;
+ err->http_status = status;
+ return err;
+}
+
+/*
+ * Function: errorAppendEntry
+ *
+ * Arguments: err - This object is destroyed after use in this function.
+ *
+ * Abstract: This function generates a error page from the info contained
+ * by 'err' and then stores the text in the specified store
+ * entry. This function should only be called by ``server
+ * side routines'' which need to communicate errors to the
+ * client side. It should also be called from client_side.c
+ * because we now support persistent connections, and
+ * cannot assume that we can immediately write to the socket
+ * for an error.
+ */
+void
+errorAppendEntry(StoreEntry * entry, ErrorState * err)
+{
+ HttpReply *rep;
+ MemObject *mem = entry->mem_obj;
+ assert(mem != NULL);
+ assert(mem->inmem_hi == 0);
+ if (entry->store_status != STORE_PENDING) {
+ /*
+ * If the entry is not STORE_PENDING, then no clients
+ * care about it, and we don't need to generate an
+ * error message
+ */
+ assert(EBIT_TEST(entry->flags, ENTRY_ABORTED));
+ assert(mem->nclients == 0);
+ errorStateFree(err);
+ return;
+ }
+ storeLockObject(entry);
+ storeBuffer(entry);
+ rep = errorBuildReply(err);
+ /* Add authentication header */
+ /* TODO: alter errorstate to be accel on|off aware. The 0 on the next line
+ * depends on authenticate behaviour: all schemes to date send no extra data
+ * on 407/401 responses, and do not check the accel state on 401/407 responses
+ */
+ authenticateFixHeader(rep, err->auth_user_request, err->request, 0);
+ httpReplySwapOut(rep, entry);
+ httpReplyAbsorb(mem->reply, rep);
+ EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
+ storeBufferFlush(entry);
+ storeComplete(entry);
+ storeNegativeCache(entry);
+ storeReleaseRequest(entry);
+ storeUnlockObject(entry);
+ errorStateFree(err);
+}
+
+/*
+ * Function: errorSend
+ *
+ * Arguments: err - This object is destroyed after use in this function.
+ *
+ * Abstract: This function generates a error page from the info contained
+ * by 'err' and then sends it to the client.
+ * The callback function errorSendComplete() is called after
+ * the page has been written to the client socket (fd).
+ * errorSendComplete() deallocates 'err'. We need to add
+ * 'err' to the cbdata because comm_write() requires it
+ * for all callback data pointers.
+ *
+ * Note, normally errorSend() should only be called from
+ * routines in ssl.c and pass.c, where we don't have any
+ * StoreEntry's. In client_side.c we must allocate a StoreEntry
+ * for errors and use errorAppendEntry() to account for
+ * persistent/pipeline connections.
+ */
+void
+errorSend(int fd, ErrorState * err)
+{
+ HttpReply *rep;
+ debug(4, 3) ("errorSend: FD %d, err=%p\n", fd, err);
+ assert(fd >= 0);
+ /*
+ * ugh, this is how we make sure error codes get back to
+ * the client side for logging and error tracking.
+ */
+ if (err->request)
+ err->request->err_type = err->type;
+ /* moved in front of errorBuildBuf @?@ */
+ err->flags.flag_cbdata = 1;
+ rep = errorBuildReply(err);
+ comm_write_mbuf(fd, httpReplyPack(rep), errorSendComplete, err);
+ httpReplyDestroy(rep);
+}
+
+/*
+ * Function: errorSendComplete
+ *
+ * Abstract: Called by commHandleWrite() after data has been written
+ * to the client socket.
+ *
+ * Note: If there is a callback, the callback is responsible for
+ * closeing the FD, otherwise we do it ourseves.
+ */
+static void
+errorSendComplete(int fd, char *bufnotused, size_t size, int errflag, void *data)
+{
+ ErrorState *err = data;
+ debug(4, 3) ("errorSendComplete: FD %d, size=%d\n", fd, size);
+ if (errflag != COMM_ERR_CLOSING) {
+ if (err->callback) {
+ debug(4, 3) ("errorSendComplete: callback\n");
+ err->callback(fd, err->callback_data, size);
+ } else {
+ comm_close(fd);
+ debug(4, 3) ("errorSendComplete: comm_close\n");
+ }
+ }
+ errorStateFree(err);
+}
+
+void
errorStateFree(ErrorState * err)
{
requestUnlink(err->request);
safe_free(err->redirect_url);
safe_free(err->url);
+ safe_free(err->host);
+ safe_free(err->dnsserver_msg);
+ safe_free(err->request_hdrs);
+ wordlistDestroy(&err->ftp.server_msg);
+ safe_free(err->ftp.request);
+ safe_free(err->ftp.reply);
+ if (err->auth_user_request)
+ authenticateAuthUserRequestUnlock(err->auth_user_request);
cbdataFree(err);
}
#define CVT_BUF_SZ 512
-static char *
+
+/*
+ * B - URL with FTP %2f hack x
+ * c - Squid error code x
+ * d - seconds elapsed since request received x
+ * e - errno x
+ * E - strerror() x
+ * f - FTP request line x
+ * F - FTP reply line x
+ * g - FTP server message x
+ * h - cache hostname x
+ * H - server host name x
+ * i - client IP address x
+ * I - server IP address x
+ * L - HREF link for more info/contact x
+ * M - Request Method x
+ * m - Error message returned by external Auth. x
+ * p - URL port # x
+ * P - Protocol x
+ * R - Full HTTP Request x
+ * S - squid signature from ERR_SIGNATURE x
+ * s - caching proxy software with version x
+ * t - local time x
+ * T - UTC x
+ * U - URL without password x
+ * u - URL without password, %2f added to path x
+ * w - cachemgr email address x
+ * z - dns server error message x
+ */
+
+static const char *
errorConvert(char token, ErrorState * err)
{
- char *p = NULL;
request_t *r = err->request;
- static char buf[CVT_BUF_SZ];
+ static MemBuf mb = MemBufNULL;
+ const char *p = NULL; /* takes priority over mb if set */
+ int do_quote = 1;
+
+ memBufReset(&mb);
switch (token) {
- case 'U':
- p = r ? urlCanonicalClean(r) : err->url;
+ case 'B':
+ p = r ? ftpUrlWith2f(r) : "[no URL]";
+ break;
+ case 'c':
+ assert(err->type >= ERR_NONE);
+ assert(err->type < ERR_MAX);
+ p = err_type_str[err->type];
+ break;
+ case 'e':
+ memBufPrintf(&mb, "%d", err->xerrno);
+ break;
+ case 'E':
+ if (err->xerrno)
+ memBufPrintf(&mb, "(%d) %s", err->xerrno, strerror(err->xerrno));
+ else
+ memBufPrintf(&mb, "[No Error]");
+ break;
+ case 'f':
+ /* FTP REQUEST LINE */
+ if (err->ftp.request)
+ p = err->ftp.request;
+ else
+ p = "nothing";
+ break;
+ case 'F':
+ /* FTP REPLY LINE */
+ if (err->ftp.request)
+ p = err->ftp.reply;
+ else
+ p = "nothing";
+ break;
+ case 'g':
+ /* FTP SERVER MESSAGE */
+ wordlistCat(err->ftp.server_msg, &mb);
+ break;
+ case 'h':
+ memBufPrintf(&mb, "%s", getMyHostname());
break;
case 'H':
p = r ? r->host : "[unknown host]";
break;
+ case 'i':
+ memBufPrintf(&mb, "%s", inet_ntoa(err->src_addr));
+ break;
+ case 'I':
+ if (err->host) {
+ memBufPrintf(&mb, "%s", err->host);
+ } else
+ p = "[unknown]";
+ break;
+ case 'L':
+ if (Config.errHtmlText) {
+ memBufPrintf(&mb, "%s", Config.errHtmlText);
+ } else
+ p = "[not available]";
+ break;
+ case 'm':
+ p = authenticateAuthUserRequestMessage(err->auth_user_request) ? authenticateAuthUserRequestMessage(err->auth_user_request) : "[not available]";
+ break;
+ case 'M':
+ p = r ? RequestMethodStr[r->method] : "[unkown method]";
+ break;
case 'p':
if (r) {
- snprintf(buf, CVT_BUF_SZ, "%d", (int) r->port);
- p = buf;
+ memBufPrintf(&mb, "%d", (int) r->port);
} else {
p = "[unknown port]";
}
break;
case 'P':
- p = r ? (char *) ProtocolStr[r->protocol] : "[unkown protocol]";
+ p = r ? ProtocolStr[r->protocol] : "[unkown protocol]";
break;
- case 'M':
- p = r ? (char *) RequestMethodStr[r->method] : "[unkown method]";
+ case 'R':
+ if (NULL != r) {
+ Packer p;
+ memBufPrintf(&mb, "%s %s HTTP/%d.%d\n",
+ RequestMethodStr[r->method],
+ strLen(r->urlpath) ? strBuf(r->urlpath) : "/",
+ r->http_ver.major, r->http_ver.minor);
+ packerToMemInit(&p, &mb);
+ httpHeaderPackInto(&r->header, &p);
+ packerClean(&p);
+ } else if (err->request_hdrs) {
+ p = err->request_hdrs;
+ } else {
+ p = "[no request]";
+ }
break;
- case 'z':
- p = err->dnsserver_msg;
+ case 's':
+ p = full_appname_string;
break;
- case 'e':
- snprintf(buf, CVT_BUF_SZ, "%d", err->errno);
- p=buf;
+ case 'S':
+ /* signature may contain %-escapes, recursion */
+ if (err->page_id != ERR_SQUID_SIGNATURE) {
+ const int saved_id = err->page_id;
+ MemBuf sign_mb;
+ err->page_id = ERR_SQUID_SIGNATURE;
+ sign_mb = errorBuildContent(err);
+ memBufPrintf(&mb, "%s", sign_mb.buf);
+ memBufClean(&sign_mb);
+ err->page_id = saved_id;
+ do_quote = 0;
+ } else {
+ /* wow, somebody put %S into ERR_SIGNATURE, stop recursion */
+ p = "[%S]";
+ }
break;
- case 'E':
- snprintf(buf, CVT_BUF_SZ, "(%d) %s", err->errno, strerror(err->errno));
+ case 't':
+ memBufPrintf(&mb, "%s", mkhttpdlogtime(&squid_curtime));
+ break;
+ case 'T':
+ memBufPrintf(&mb, "%s", mkrfc1123(squid_curtime));
+ break;
+ case 'U':
+ p = r ? urlCanonicalClean(r) : err->url ? err->url : "[no URL]";
break;
case 'w':
- snprintf(buf, CVT_BUF_SZ, "%s",Config.adminEmail);
+ if (Config.adminEmail)
+ memBufPrintf(&mb, "%s", Config.adminEmail);
+ else
+ p = "[unknown]";
break;
- case 'h':
- snprintf(buf, CVT_BUF_SZ, "%s", getMyHostname());
+ case 'z':
+ if (err->dnsserver_msg)
+ p = err->dnsserver_msg;
+ else
+ p = "[unknown]";
+ break;
+ case '%':
+ p = "%";
break;
-/*
- * e - errno x
- * E - strerror() x
- * t - local time
- * T - UTC
- * c - Squid error code
- * I - server IP address
- * i - client IP address
- * L - HREF link for more info/contact
- * w - cachemgr email address x
- * h - cache hostname x
- * d - seconds elapsed since request received
- * p - URL port # x
- */
default:
- p = "%UNKNOWN%";
+ memBufPrintf(&mb, "%%%c", token);
break;
}
- if (p == NULL)
- p = "<NULL>";
+ if (!p)
+ p = mb.buf; /* do not use mb after this assignment! */
+ assert(p);
debug(4, 3) ("errorConvert: %%%c --> '%s'\n", token, p);
+ if (do_quote)
+ p = html_quote(p);
return p;
}
-static char *
-errorBuildBuf(ErrorState * err, int *len)
-{
- LOCAL_ARRAY(char, buf, ERROR_BUF_SZ);
- LOCAL_ARRAY(char, content, ERROR_BUF_SZ);
- char *hdr;
- int clen;
- int tlen;
- char *m;
- char *mx;
- char *p;
- char *t;
- assert(err != NULL);
- assert(err->type > ERR_NONE && err->type < ERR_MAX);
- mx = m = xstrdup(error_text[err->type]);
- clen = 0;
- while ((p = strchr(m, '%'))) {
- *p = '\0'; /* terminate */
- xstrncpy(content + clen, m, ERROR_BUF_SZ - clen); /* copy */
- clen += (p - m); /* advance */
- if (clen >= ERROR_BUF_SZ)
- break;
- p++;
- m = p + 1;
- t = errorConvert(*p, err); /* convert */
- xstrncpy(content + clen, t, ERROR_BUF_SZ - clen); /* copy */
- clen += strlen(t); /* advance */
- if (clen >= ERROR_BUF_SZ)
- break;
- }
- if (clen < ERROR_BUF_SZ && m != NULL) {
- xstrncpy(content + clen, m, ERROR_BUF_SZ - clen);
- clen += strlen(m);
- }
- if (clen >= ERROR_BUF_SZ) {
- clen = ERROR_BUF_SZ - 1;
- *(content + clen) = '\0';
- }
- assert(clen == strlen(content));
- hdr = httpReplyHeader((double) 1.0,
- err->http_status,
- "text/html",
- clen,
- 0, /* no LMT for error pages */
- squid_curtime);
- tlen = snprintf(buf, ERROR_BUF_SZ, "%s\r\n%s", hdr, content);
- if (len)
- *len = tlen;
- xfree(mx);
- return buf;
-}
-
-void
-errorSend(int fd, ErrorState * err)
-{
- char *buf;
- int len;
- assert(fd >= 0);
- buf = errorBuildBuf(err, &len);
- cbdataAdd(err);
- cbdataLock(err);
- comm_write(fd, xstrdup(buf), len, errorSendComplete, err, xfree);
-}
-
-void
-errorAppendEntry(StoreEntry * entry, ErrorState * err)
+/* allocates and initializes an error response */
+HttpReply *
+errorBuildReply(ErrorState * err)
{
- char *buf;
- MemObject *mem = entry->mem_obj;
- int len;
- assert(entry->store_status == STORE_PENDING);
- buf = errorBuildBuf(err, &len);
- storeAppend(entry, buf, len);
- if (mem)
- mem->reply->code = err->http_status;
+ HttpReply *rep = httpReplyCreate();
+ MemBuf content = errorBuildContent(err);
+ http_version_t version;
+ /* no LMT for error pages; error pages expire immediately */
+ httpBuildVersion(&version, 1, 0);
+ httpReplySetHeaders(rep, version, err->http_status, NULL, "text/html", content.size, 0, squid_curtime);
+ /*
+ * include some information for downstream caches. Implicit
+ * replaceable content. This isn't quite sufficient. xerrno is not
+ * necessarily meaningful to another system, so we really should
+ * expand it. Additionally, we should identify ourselves. Someone
+ * might want to know. Someone _will_ want to know OTOH, the first
+ * X-CACHE-MISS entry should tell us who.
+ */
+ httpHeaderPutStrf(&rep->header, HDR_X_SQUID_ERROR, "%s %d",
+ errorPageName(err->page_id), err->xerrno);
+ httpBodySet(&rep->body, &content);
+ /* do not memBufClean() the content, it was absorbed by httpBody */
+ return rep;
}
-static void
-errorSendComplete(int fd, char *buf, int size, int errflag, void *data)
+static MemBuf
+errorBuildContent(ErrorState * err)
{
- ErrorState *err = data;
- if (err->callback)
- err->callback(fd, err->callback_data, size);
- cbdataUnlock(err);
- errorStateFree(err);
+ MemBuf content;
+ const char *m;
+ const char *p;
+ const char *t;
+ assert(err != NULL);
+ assert(err->page_id > ERR_NONE && err->page_id < error_page_count);
+ memBufDefInit(&content);
+ m = error_text[err->page_id];
+ assert(m);
+ while ((p = strchr(m, '%'))) {
+ memBufAppend(&content, m, p - m); /* copy */
+ t = errorConvert(*++p, err); /* convert */
+ memBufPrintf(&content, "%s", t); /* copy */
+ m = p + 1; /* advance */
+ }
+ if (*m)
+ memBufPrintf(&content, "%s", m); /* copy tail */
+ assert(content.size == strlen(content.buf));
+ return content;
}