[MINOR] store HTTP error messages into a chunk array

HTTP error messages were all specific cases handled by an IF.
Now they are all in an array so that it will be easier to add
new ones. Also, the return functions now use chunks as inputs
so that it should be easier to provide alternative return
messages if needed.
diff --git a/include/proto/buffers.h b/include/proto/buffers.h
index a5ad208..ca88b16 100644
--- a/include/proto/buffers.h
+++ b/include/proto/buffers.h
@@ -22,6 +22,8 @@
 #ifndef _PROTO_BUFFERS_H
 #define _PROTO_BUFFERS_H
 
+#include <stdlib.h>
+
 #include <common/config.h>
 #include <types/buffers.h>
 
@@ -84,6 +86,21 @@
 int buffer_replace(struct buffer *b, char *pos, char *end, char *str);
 int buffer_replace2(struct buffer *b, char *pos, char *end, char *str, int len);
 
+/*
+ * frees the destination chunk if already allocated, allocates a new string,
+ * and copies the source into it. The pointer to the destination string is
+ * returned, or NULL if the allocation fails or if any pointer is NULL..
+ */
+static inline char *chunk_dup(struct chunk *dst, const struct chunk *src) {
+	if (!dst || !src || !src->str)
+		return NULL;
+	if (dst->str)
+		free(dst->str);
+	dst->len = src->len;
+	dst->str = (char *)malloc(dst->len);
+	memcpy(dst->str, src->str, dst->len);
+	return dst->str;
+}
 
 #endif /* _PROTO_BUFFERS_H */
 
diff --git a/include/proto/httperr.h b/include/proto/httperr.h
new file mode 100644
index 0000000..24a0cd1
--- /dev/null
+++ b/include/proto/httperr.h
@@ -0,0 +1,41 @@
+/*
+  include/proto/httperr.h
+  This file contains declarations for HTTP responses and errors.
+
+  Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
+  
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation, version 2.1
+  exclusively.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#ifndef _PROTO_HTTPERR_H
+#define _PROTO_HTTPERR_H
+
+#include <types/httperr.h>
+
+extern const int http_err_codes[HTTP_ERR_SIZE];
+extern const char *http_err_msgs[HTTP_ERR_SIZE];
+extern const char *HTTP_200;
+extern const char *HTTP_302;
+extern const char *HTTP_303;
+extern const char *HTTP_401_fmt;
+
+#endif /* _PROTO_HTTPERR_H */
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ */
diff --git a/include/proto/proto_http.h b/include/proto/proto_http.h
index 61d58c2..5ee3a53 100644
--- a/include/proto/proto_http.h
+++ b/include/proto/proto_http.h
@@ -40,10 +40,10 @@
 int process_cli(struct session *t);
 int process_srv(struct session *t);
 
-void client_retnclose(struct session *s, int len, const char *msg);
-void client_return(struct session *s, int len, const char *msg);
+void client_retnclose(struct session *s, const struct chunk *msg);
+void client_return(struct session *s, const struct chunk *msg);
 void srv_close_with_err(struct session *t, int err, int finst,
-			int status, int msglen, const char *msg);
+			int status, const struct chunk *msg);
 
 int produce_content(struct session *s);
 void debug_hdr(const char *dir, struct session *t, const char *start, const char *end);
diff --git a/include/types/httperr.h b/include/types/httperr.h
index c594048..c466328 100644
--- a/include/types/httperr.h
+++ b/include/types/httperr.h
@@ -32,6 +32,19 @@
 #define DATA_ST_INIT	0
 #define DATA_ST_DATA	1
 
+/*
+ * All implemented return codes
+ */
+enum {
+	HTTP_ERR_400 = 0,
+	HTTP_ERR_403,
+	HTTP_ERR_408,
+	HTTP_ERR_500,
+	HTTP_ERR_502,
+	HTTP_ERR_503,
+	HTTP_ERR_504,
+	HTTP_ERR_SIZE
+};
 
 
 #endif /* _TYPES_HTTPERR_H */
diff --git a/include/types/proxy.h b/include/types/proxy.h
index a82f195..8480143 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -34,6 +34,7 @@
 #include <common/regex.h>
 
 #include <types/buffers.h>
+#include <types/httperr.h>
 #include <types/session.h>
 #include <types/server.h>
 
@@ -125,22 +126,7 @@
 	int grace;				/* grace time after stop request */
 	char *check_req;			/* HTTP or SSL request to use for PR_O_HTTP_CHK|PR_O_SSL3_CHK */
 	int check_len;				/* Length of the HTTP or SSL3 request */
-	struct {
-		char *msg400;			/* message for error 400 */
-		int len400;			/* message length for error 400 */
-		char *msg403;			/* message for error 403 */
-		int len403;			/* message length for error 403 */
-		char *msg408;			/* message for error 408 */
-		int len408;			/* message length for error 408 */
-		char *msg500;			/* message for error 500 */
-		int len500;			/* message length for error 500 */
-		char *msg502;			/* message for error 502 */
-		int len502;			/* message length for error 502 */
-		char *msg503;			/* message for error 503 */
-		int len503;			/* message length for error 503 */
-		char *msg504;			/* message for error 504 */
-		int len504;			/* message length for error 504 */
-	} errmsg;
+	struct chunk errmsg[HTTP_ERR_SIZE];	/* default or customized error messages for known errors */
 };
 
 extern struct proxy *proxy;
diff --git a/src/backend.c b/src/backend.c
index 15d85da..f794ea5 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -545,7 +545,7 @@
 		/* if not retryable anymore, let's abort */
 		tv_eternity(&t->req->cex);
 		srv_close_with_err(t, conn_err, SN_FINST_C,
-				   503, t->fe->errmsg.len503, t->fe->errmsg.msg503);
+				   503, &t->fe->errmsg[HTTP_ERR_503]);
 		if (t->srv)
 			t->srv->failed_conns++;
 		t->be->beprm->failed_conns++;
@@ -587,7 +587,7 @@
 		case SN_ERR_INTERNAL:
 			tv_eternity(&t->req->cex);
 			srv_close_with_err(t, SN_ERR_INTERNAL, SN_FINST_C,
-					   500, t->fe->errmsg.len500, t->fe->errmsg.msg500);
+					   500, &t->fe->errmsg[HTTP_ERR_500]);
 			if (t->srv)
 				t->srv->failed_conns++;
 			t->be->beprm->failed_conns++;
@@ -647,7 +647,7 @@
 		/* note: it is guaranteed that t->srv == NULL here */
 		tv_eternity(&t->req->cex);
 		srv_close_with_err(t, SN_ERR_SRVTO, SN_FINST_C,
-				   503, t->fe->errmsg.len503, t->fe->errmsg.msg503);
+				   503, &t->fe->errmsg[HTTP_ERR_503]);
 		if (t->srv)
 			t->srv->failed_conns++;
 		t->be->beprm->failed_conns++;
@@ -669,7 +669,7 @@
 	default:
 		tv_eternity(&t->req->cex);
 		srv_close_with_err(t, SN_ERR_INTERNAL, SN_FINST_C,
-				   500, t->fe->errmsg.len500, t->fe->errmsg.msg500);
+				   500, &t->fe->errmsg[HTTP_ERR_500]);
 		if (t->srv)
 			t->srv->failed_conns++;
 		t->be->beprm->failed_conns++;
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 0034bda..f3659f5 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -25,86 +25,20 @@
 
 #include <types/capture.h>
 #include <types/global.h>
+#include <types/httperr.h>
 #include <types/polling.h>
 #include <types/proxy.h>
 #include <types/queue.h>
 
 #include <proto/backend.h>
+#include <proto/buffers.h>
 #include <proto/checks.h>
+#include <proto/httperr.h>
 #include <proto/log.h>
 #include <proto/server.h>
 #include <proto/task.h>
 
 
-const char *HTTP_302 =
-	"HTTP/1.0 302 Found\r\n"
-	"Cache-Control: no-cache\r\n"
-	"Connection: close\r\n"
-	"Location: "; /* not terminated since it will be concatenated with the URL */
-
-/* same as 302 except that the browser MUST retry with the GET method */
-const char *HTTP_303 =
-	"HTTP/1.0 303 See Other\r\n"
-	"Cache-Control: no-cache\r\n"
-	"Connection: close\r\n"
-	"Location: "; /* not terminated since it will be concatenated with the URL */
-
-const char *HTTP_400 =
-	"HTTP/1.0 400 Bad request\r\n"
-	"Cache-Control: no-cache\r\n"
-	"Connection: close\r\n"
-	"Content-Type: text/html\r\n"
-	"\r\n"
-	"<html><body><h1>400 Bad request</h1>\nYour browser sent an invalid request.\n</body></html>\n";
-
-const char *HTTP_403 =
-	"HTTP/1.0 403 Forbidden\r\n"
-	"Cache-Control: no-cache\r\n"
-	"Connection: close\r\n"
-	"Content-Type: text/html\r\n"
-	"\r\n"
-	"<html><body><h1>403 Forbidden</h1>\nRequest forbidden by administrative rules.\n</body></html>\n";
-
-const char *HTTP_408 =
-	"HTTP/1.0 408 Request Time-out\r\n"
-	"Cache-Control: no-cache\r\n"
-	"Connection: close\r\n"
-	"Content-Type: text/html\r\n"
-	"\r\n"
-	"<html><body><h1>408 Request Time-out</h1>\nYour browser didn't send a complete request in time.\n</body></html>\n";
-
-const char *HTTP_500 =
-	"HTTP/1.0 500 Server Error\r\n"
-	"Cache-Control: no-cache\r\n"
-	"Connection: close\r\n"
-	"Content-Type: text/html\r\n"
-	"\r\n"
-	"<html><body><h1>500 Server Error</h1>\nAn internal server error occured.\n</body></html>\n";
-
-const char *HTTP_502 =
-	"HTTP/1.0 502 Bad Gateway\r\n"
-	"Cache-Control: no-cache\r\n"
-	"Connection: close\r\n"
-	"Content-Type: text/html\r\n"
-	"\r\n"
-	"<html><body><h1>502 Bad Gateway</h1>\nThe server returned an invalid or incomplete response.\n</body></html>\n";
-
-const char *HTTP_503 =
-	"HTTP/1.0 503 Service Unavailable\r\n"
-	"Cache-Control: no-cache\r\n"
-	"Connection: close\r\n"
-	"Content-Type: text/html\r\n"
-	"\r\n"
-	"<html><body><h1>503 Service Unavailable</h1>\nNo server is available to handle this request.\n</body></html>\n";
-
-const char *HTTP_504 =
-	"HTTP/1.0 504 Gateway Time-out\r\n"
-	"Cache-Control: no-cache\r\n"
-	"Connection: close\r\n"
-	"Content-Type: text/html\r\n"
-	"\r\n"
-	"<html><body><h1>504 Gateway Time-out</h1>\nThe server didn't respond in time.\n</body></html>\n";
-
 /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
  * ssl-hello-chk option to ensure that the remote server speaks SSL.
  *
@@ -477,33 +411,11 @@
 		curproxy->capture_namelen = defproxy.capture_namelen;
 		curproxy->capture_len = defproxy.capture_len;
 
-		if (defproxy.errmsg.msg400)
-			curproxy->errmsg.msg400 = strdup(defproxy.errmsg.msg400);
-		curproxy->errmsg.len400 = defproxy.errmsg.len400;
 
-		if (defproxy.errmsg.msg403)
-			curproxy->errmsg.msg403 = strdup(defproxy.errmsg.msg403);
-		curproxy->errmsg.len403 = defproxy.errmsg.len403;
-
-		if (defproxy.errmsg.msg408)
-			curproxy->errmsg.msg408 = strdup(defproxy.errmsg.msg408);
-		curproxy->errmsg.len408 = defproxy.errmsg.len408;
-
-		if (defproxy.errmsg.msg500)
-			curproxy->errmsg.msg500 = strdup(defproxy.errmsg.msg500);
-		curproxy->errmsg.len500 = defproxy.errmsg.len500;
-
-		if (defproxy.errmsg.msg502)
-			curproxy->errmsg.msg502 = strdup(defproxy.errmsg.msg502);
-		curproxy->errmsg.len502 = defproxy.errmsg.len502;
-
-		if (defproxy.errmsg.msg503)
-			curproxy->errmsg.msg503 = strdup(defproxy.errmsg.msg503);
-		curproxy->errmsg.len503 = defproxy.errmsg.len503;
-
-		if (defproxy.errmsg.msg504)
-			curproxy->errmsg.msg504 = strdup(defproxy.errmsg.msg504);
-		curproxy->errmsg.len504 = defproxy.errmsg.len504;
+		for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
+			if (defproxy.errmsg[rc].str)
+				chunk_dup(&curproxy->errmsg[rc], &defproxy.errmsg[rc]);
+		}
 
 		curproxy->clitimeout = defproxy.clitimeout;
 		curproxy->contimeout = defproxy.contimeout;
@@ -533,14 +445,13 @@
 		if (defproxy.check_req)     free(defproxy.check_req);
 		if (defproxy.cookie_name)   free(defproxy.cookie_name);
 		if (defproxy.capture_name)  free(defproxy.capture_name);
-		if (defproxy.errmsg.msg400) free(defproxy.errmsg.msg400);
-		if (defproxy.errmsg.msg403) free(defproxy.errmsg.msg403);
-		if (defproxy.errmsg.msg408) free(defproxy.errmsg.msg408);
-		if (defproxy.errmsg.msg500) free(defproxy.errmsg.msg500);
-		if (defproxy.errmsg.msg502) free(defproxy.errmsg.msg502);
-		if (defproxy.errmsg.msg503) free(defproxy.errmsg.msg503);
-		if (defproxy.errmsg.msg504) free(defproxy.errmsg.msg504);
 		if (defproxy.monitor_uri)   free(defproxy.monitor_uri);
+
+		for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
+			if (defproxy.errmsg[rc].len)
+				free(defproxy.errmsg[rc].str);
+		}
+
 		/* we cannot free uri_auth because it might already be used */
 		init_default_instance();
 		curproxy = &defproxy;
@@ -1778,7 +1689,7 @@
 		// }
 
 		if (*(args[2]) == 0) {
-			Alert("parsing [%s:%d] : <errorloc> expects <error> and <url> as arguments.\n", file, linenum);
+			Alert("parsing [%s:%d] : <%s> expects <status_code> and <url> as arguments.\n", file, linenum);
 			return -1;
 		}
 
@@ -1791,64 +1702,19 @@
 			errlen = sprintf(err, "%s%s\r\n\r\n", HTTP_302, args[2]);
 		}
 
-		if (errnum == 400) {
-			if (curproxy->errmsg.msg400) {
-				//Warning("parsing [%s:%d] : error %d already defined.\n", file, linenum, errnum);
-				free(curproxy->errmsg.msg400);
-			}
-			curproxy->errmsg.msg400 = err;
-			curproxy->errmsg.len400 = errlen;
-		}
-		else if (errnum == 403) {
-			if (curproxy->errmsg.msg403) {
-				//Warning("parsing [%s:%d] : error %d already defined.\n", file, linenum, errnum);
-				free(curproxy->errmsg.msg403);
-			}
-			curproxy->errmsg.msg403 = err;
-			curproxy->errmsg.len403 = errlen;
-		}
-		else if (errnum == 408) {
-			if (curproxy->errmsg.msg408) {
-				//Warning("parsing [%s:%d] : error %d already defined.\n", file, linenum, errnum);
-				free(curproxy->errmsg.msg408);
-			}
-			curproxy->errmsg.msg408 = err;
-			curproxy->errmsg.len408 = errlen;
-		}
-		else if (errnum == 500) {
-			if (curproxy->errmsg.msg500) {
-				//Warning("parsing [%s:%d] : error %d already defined.\n", file, linenum, errnum);
-				free(curproxy->errmsg.msg500);
-			}
-			curproxy->errmsg.msg500 = err;
-			curproxy->errmsg.len500 = errlen;
-		}
-		else if (errnum == 502) {
-			if (curproxy->errmsg.msg502) {
-				//Warning("parsing [%s:%d] : error %d already defined.\n", file, linenum, errnum);
-				free(curproxy->errmsg.msg502);
-			}
-			curproxy->errmsg.msg502 = err;
-			curproxy->errmsg.len502 = errlen;
-		}
-		else if (errnum == 503) {
-			if (curproxy->errmsg.msg503) {
-				//Warning("parsing [%s:%d] : error %d already defined.\n", file, linenum, errnum);
-				free(curproxy->errmsg.msg503);
-			}
-			curproxy->errmsg.msg503 = err;
-			curproxy->errmsg.len503 = errlen;
-		}
-		else if (errnum == 504) {
-			if (curproxy->errmsg.msg504) {
-				//Warning("parsing [%s:%d] : error %d already defined.\n", file, linenum, errnum);
-				free(curproxy->errmsg.msg504);
+		for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
+			if (http_err_codes[rc] == errnum) {
+				if (curproxy->errmsg[rc].str)
+					free(curproxy->errmsg[rc].str);
+				curproxy->errmsg[rc].str = err;
+				curproxy->errmsg[rc].len = errlen;
+				break;
 			}
-			curproxy->errmsg.msg504 = err;
-			curproxy->errmsg.len504 = errlen;
 		}
-		else {
-			Warning("parsing [%s:%d] : error %d relocation will be ignored.\n", file, linenum, errnum);
+
+		if (rc >= HTTP_ERR_SIZE) {
+			Warning("parsing [%s:%d] : status code %d not handled, error relocation will be ignored.\n",
+				file, linenum, errnum);
 			free(err);
 		}
 	}
@@ -1874,7 +1740,7 @@
 	char *args[MAX_LINE_ARGS];
 	int arg;
 	int cfgerr = 0;
-	int nbchk, mininter;
+	int nbchk, mininter, rc;
 	int confsect = CFG_NONE;
 
 	struct proxy *curproxy = NULL;
@@ -2163,33 +2029,15 @@
 		if (curproxy->options & PR_O_LOGASAP)
 			curproxy->to_log &= ~LW_BYTES;
 
-		if (curproxy->errmsg.msg400 == NULL) {
-			curproxy->errmsg.msg400 = (char *)HTTP_400;
-			curproxy->errmsg.len400 = strlen(HTTP_400);
-		}
-		if (curproxy->errmsg.msg403 == NULL) {
-			curproxy->errmsg.msg403 = (char *)HTTP_403;
-			curproxy->errmsg.len403 = strlen(HTTP_403);
-		}
-		if (curproxy->errmsg.msg408 == NULL) {
-			curproxy->errmsg.msg408 = (char *)HTTP_408;
-			curproxy->errmsg.len408 = strlen(HTTP_408);
-		}
-		if (curproxy->errmsg.msg500 == NULL) {
-			curproxy->errmsg.msg500 = (char *)HTTP_500;
-			curproxy->errmsg.len500 = strlen(HTTP_500);
-		}
-		if (curproxy->errmsg.msg502 == NULL) {
-			curproxy->errmsg.msg502 = (char *)HTTP_502;
-			curproxy->errmsg.len502 = strlen(HTTP_502);
-		}
-		if (curproxy->errmsg.msg503 == NULL) {
-			curproxy->errmsg.msg503 = (char *)HTTP_503;
-			curproxy->errmsg.len503 = strlen(HTTP_503);
-		}
-		if (curproxy->errmsg.msg504 == NULL) {
-			curproxy->errmsg.msg504 = (char *)HTTP_504;
-			curproxy->errmsg.len504 = strlen(HTTP_504);
+		for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
+			if (!http_err_msgs[rc]) {
+				Alert("Internal error: no message defined for HTTP return code %d. Aborting.\n");
+				abort();
+			}
+			if (!curproxy->errmsg[rc].str) {
+				curproxy->errmsg[rc].str = strdup(http_err_msgs[rc]);
+				curproxy->errmsg[rc].len = strlen(http_err_msgs[rc]);
+			}
 		}
 
 		/*
diff --git a/src/client.c b/src/client.c
index 8386d78..8e26a95 100644
--- a/src/client.c
+++ b/src/client.c
@@ -378,14 +378,17 @@
 		fdtab[cfd].cb[DIR_WR].b = s->rep;
 
 		if ((p->mode == PR_MODE_HTTP && (s->flags & SN_MONITOR)) ||
-		    (p->mode == PR_MODE_HEALTH && (p->options & PR_O_HTTP_CHK)))
+		    (p->mode == PR_MODE_HEALTH && (p->options & PR_O_HTTP_CHK))) {
 			/* Either we got a request from a monitoring system on an HTTP instance,
 			 * or we're in health check mode with the 'httpchk' option enabled. In
 			 * both cases, we return a fake "HTTP/1.0 200 OK" response and we exit.
 			 */
-			client_retnclose(s, 19, "HTTP/1.0 200 OK\r\n\r\n"); /* forge a 200 response */
+			struct chunk msg = { .str = "HTTP/1.0 200 OK\r\n\r\n", .len = 19 };
+			client_retnclose(s, &msg); /* forge a 200 response */
+		}
 		else if (p->mode == PR_MODE_HEALTH) {  /* health check mode, no client reading */
-			client_retnclose(s, 3, "OK\n"); /* forge an "OK" response */
+			struct chunk msg = { .str = "OK\n", .len = 3 };
+			client_retnclose(s, &msg); /* forge an "OK" response */
 		}
 		else {
 			MY_FD_SET(cfd, StaticReadEvent);
diff --git a/src/proto_http.c b/src/proto_http.c
index 9cce59b..89b5b60 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -67,7 +67,7 @@
 #endif
 
 /* This is used by remote monitoring */
-const char *HTTP_200 =
+const char HTTP_200[] =
 	"HTTP/1.0 200 OK\r\n"
 	"Cache-Control: no-cache\r\n"
 	"Connection: close\r\n"
@@ -75,6 +75,24 @@
 	"\r\n"
 	"<html><body><h1>200 OK</h1>\nHAProxy: service ready.\n</body></html>\n";
 
+const struct chunk http_200_chunk = {
+	.str = (char *)&HTTP_200,
+	.len = sizeof(HTTP_200)-1
+};
+
+const char *HTTP_302 =
+	"HTTP/1.0 302 Found\r\n"
+	"Cache-Control: no-cache\r\n"
+	"Connection: close\r\n"
+	"Location: "; /* not terminated since it will be concatenated with the URL */
+
+/* same as 302 except that the browser MUST retry with the GET method */
+const char *HTTP_303 =
+	"HTTP/1.0 303 See Other\r\n"
+	"Cache-Control: no-cache\r\n"
+	"Connection: close\r\n"
+	"Location: "; /* not terminated since it will be concatenated with the URL */
+
 /* Warning: this one is an sprintf() fmt string, with <realm> as its only argument */
 const char *HTTP_401_fmt =
 	"HTTP/1.0 401 Unauthorized\r\n"
@@ -85,6 +103,76 @@
 	"\r\n"
 	"<html><body><h1>401 Unauthorized</h1>\nYou need a valid user and password to access this content.\n</body></html>\n";
 
+
+const int http_err_codes[HTTP_ERR_SIZE] = {
+	[HTTP_ERR_400] = 400,
+	[HTTP_ERR_403] = 403,
+	[HTTP_ERR_408] = 408,
+	[HTTP_ERR_500] = 500,
+	[HTTP_ERR_502] = 502,
+	[HTTP_ERR_503] = 503,
+	[HTTP_ERR_504] = 504,
+};
+
+const char *http_err_msgs[HTTP_ERR_SIZE] = {
+	[HTTP_ERR_400] =
+ 	"HTTP/1.0 400 Bad request\r\n"
+	"Cache-Control: no-cache\r\n"
+	"Connection: close\r\n"
+	"Content-Type: text/html\r\n"
+	"\r\n"
+	"<html><body><h1>400 Bad request</h1>\nYour browser sent an invalid request.\n</body></html>\n",
+
+	[HTTP_ERR_403] =
+	"HTTP/1.0 403 Forbidden\r\n"
+	"Cache-Control: no-cache\r\n"
+	"Connection: close\r\n"
+	"Content-Type: text/html\r\n"
+	"\r\n"
+	"<html><body><h1>403 Forbidden</h1>\nRequest forbidden by administrative rules.\n</body></html>\n",
+
+	[HTTP_ERR_408] =
+	"HTTP/1.0 408 Request Time-out\r\n"
+	"Cache-Control: no-cache\r\n"
+	"Connection: close\r\n"
+	"Content-Type: text/html\r\n"
+	"\r\n"
+	"<html><body><h1>408 Request Time-out</h1>\nYour browser didn't send a complete request in time.\n</body></html>\n",
+
+	[HTTP_ERR_500] =
+	"HTTP/1.0 500 Server Error\r\n"
+	"Cache-Control: no-cache\r\n"
+	"Connection: close\r\n"
+	"Content-Type: text/html\r\n"
+	"\r\n"
+	"<html><body><h1>500 Server Error</h1>\nAn internal server error occured.\n</body></html>\n",
+
+	[HTTP_ERR_502] =
+	"HTTP/1.0 502 Bad Gateway\r\n"
+	"Cache-Control: no-cache\r\n"
+	"Connection: close\r\n"
+	"Content-Type: text/html\r\n"
+	"\r\n"
+	"<html><body><h1>502 Bad Gateway</h1>\nThe server returned an invalid or incomplete response.\n</body></html>\n",
+
+	[HTTP_ERR_503] =
+	"HTTP/1.0 503 Service Unavailable\r\n"
+	"Cache-Control: no-cache\r\n"
+	"Connection: close\r\n"
+	"Content-Type: text/html\r\n"
+	"\r\n"
+	"<html><body><h1>503 Service Unavailable</h1>\nNo server is available to handle this request.\n</body></html>\n",
+
+	[HTTP_ERR_504] =
+	"HTTP/1.0 504 Gateway Time-out\r\n"
+	"Cache-Control: no-cache\r\n"
+	"Connection: close\r\n"
+	"Content-Type: text/html\r\n"
+	"\r\n"
+	"<html><body><h1>504 Gateway Time-out</h1>\nThe server didn't respond in time.\n</body></html>\n",
+
+};
+
 
 /*
  * We have 26 list of methods (1 per first letter), each of which can have
@@ -131,10 +219,11 @@
  * returns a message to the client ; the connection is shut down for read,
  * and the request is cleared so that no server connection can be initiated.
  * The client must be in a valid state for this (HEADER, DATA ...).
- * Nothing is performed on the server side.
+ * Nothing is performed on the server side. The message is contained in a
+ * "chunk". If it is null, then an empty message is used.
  * The reply buffer doesn't need to be empty before this.
  */
-void client_retnclose(struct session *s, int len, const char *msg)
+void client_retnclose(struct session *s, const struct chunk *msg)
 {
 	MY_FD_CLR(s->cli_fd, StaticReadEvent);
 	MY_FD_SET(s->cli_fd, StaticWriteEvent);
@@ -146,35 +235,39 @@
 	shutdown(s->cli_fd, SHUT_RD);
 	s->cli_state = CL_STSHUTR;
 	buffer_flush(s->rep);
-	buffer_write(s->rep, msg, len);
+	if (msg->len)
+		buffer_write(s->rep, msg->str, msg->len);
 	s->req->l = 0;
 }
 
 
 /*
  * returns a message into the rep buffer, and flushes the req buffer.
- * The reply buffer doesn't need to be empty before this.
+ * The reply buffer doesn't need to be empty before this. The message
+ * is contained in a "chunk". If it is null, then an empty message is
+ * used.
  */
-void client_return(struct session *s, int len, const char *msg)
+void client_return(struct session *s, const struct chunk *msg)
 {
 	buffer_flush(s->rep);
-	buffer_write(s->rep, msg, len);
+	if (msg->len)
+		buffer_write(s->rep, msg->str, msg->len);
 	s->req->l = 0;
 }
 
 
 /* This function turns the server state into the SV_STCLOSE, and sets
- * indicators accordingly. Note that if <status> is 0, no message is
- * returned.
+ * indicators accordingly. Note that if <status> is 0, or if the message
+ * pointer is NULL, then no message is returned.
  */
 void srv_close_with_err(struct session *t, int err, int finst,
-			int status, int msglen, const char *msg)
+			int status, const struct chunk *msg)
 {
 	t->srv_state = SV_STCLOSE;
-	if (status > 0) {
+	if (status > 0 && msg) {
 		t->logs.status = status;
 		if (t->fe->mode == PR_MODE_HTTP)
-			client_return(t, msglen, msg);
+			client_return(t, msg);
 	}
 	if (!(t->flags & SN_ERR_MASK))
 		t->flags |= err;
@@ -814,7 +907,7 @@
 			else if (tv_cmp2_ms(&req->rex, &now) <= 0) {
 				/* read timeout : give up with an error message. */
 				t->logs.status = 408;
-				client_retnclose(t, t->fe->errmsg.len408, t->fe->errmsg.msg408);
+				client_retnclose(t, &t->fe->errmsg[HTTP_ERR_408]);
 				if (!(t->flags & SN_ERR_MASK))
 					t->flags |= SN_ERR_CLITO;
 				if (!(t->flags & SN_FINST_MASK))
@@ -876,7 +969,7 @@
 				 */
 				t->flags |= SN_MONITOR;
 				t->logs.status = 200;
-				client_retnclose(t, strlen(HTTP_200), HTTP_200);
+				client_retnclose(t, &http_200_chunk);
 				goto return_prx_cond;
 			}
 		}
@@ -937,7 +1030,7 @@
 				t->logs.status = 403;
 				/* let's log the request time */
 				t->logs.t_request = tv_diff(&t->logs.tv_accept, &now);
-				client_retnclose(t, t->fe->errmsg.len403, t->fe->errmsg.msg403);
+				client_retnclose(t, &t->fe->errmsg[HTTP_ERR_403]);
 				goto return_prx_cond;
 			}
 
@@ -1140,7 +1233,7 @@
 	return_bad_req: /* let's centralize all bad requests */
 		t->hreq.hdr_state = HTTP_PA_ERROR;
 		t->logs.status = 400;
-		client_retnclose(t, t->fe->errmsg.len400, t->fe->errmsg.msg400);
+		client_retnclose(t, &t->fe->errmsg[HTTP_ERR_400]);
 	return_prx_cond:
 		if (!(t->flags & SN_ERR_MASK))
 			t->flags |= SN_ERR_PRXCOND;
@@ -1469,9 +1562,9 @@
 			 * overwrite the client_retnclose() output.
 			 */
 			if (t->flags & SN_CLTARPIT)
-				srv_close_with_err(t, SN_ERR_CLICL, SN_FINST_T, 0, 0, NULL);
+				srv_close_with_err(t, SN_ERR_CLICL, SN_FINST_T, 0, NULL);
 			else
-				srv_close_with_err(t, SN_ERR_CLICL, t->pend_pos ? SN_FINST_Q : SN_FINST_C, 0, 0, NULL);
+				srv_close_with_err(t, SN_ERR_CLICL, t->pend_pos ? SN_FINST_Q : SN_FINST_C, 0, NULL);
 
 			return 1;
 		}
@@ -1493,7 +1586,7 @@
 				tv_eternity(&req->cex);
 				t->logs.t_queue = tv_diff(&t->logs.tv_accept, &now);
 				srv_close_with_err(t, SN_ERR_PRXCOND, SN_FINST_T,
-						   500, t->fe->errmsg.len500, t->fe->errmsg.msg500);
+						   500, &t->fe->errmsg[HTTP_ERR_500]);
 				return 1;
 			}
 
@@ -1510,7 +1603,7 @@
 					tv_eternity(&req->cex);
 					t->logs.t_queue = tv_diff(&t->logs.tv_accept, &now);
 					srv_close_with_err(t, SN_ERR_SRVTO, SN_FINST_Q,
-							   503, t->fe->errmsg.len503, t->fe->errmsg.msg503);
+							   503, &t->fe->errmsg[HTTP_ERR_503]);
 					if (t->srv)
 						t->srv->failed_conns++;
 					t->fe->failed_conns++;
@@ -1546,7 +1639,7 @@
 			/* note that this must not return any error because it would be able to
 			 * overwrite the client_retnclose() output.
 			 */
-			srv_close_with_err(t, SN_ERR_CLICL, SN_FINST_C, 0, 0, NULL);
+			srv_close_with_err(t, SN_ERR_CLICL, SN_FINST_C, 0, NULL);
 			return 1;
 		}
 		if (!(req->flags & BF_WRITE_STATUS) && tv_cmp2_ms(&req->cex, &now) > 0) {
@@ -1695,7 +1788,7 @@
 						t->be->failed_secu++;
 						t->srv_state = SV_STCLOSE;
 						t->logs.status = 502;
-						client_return(t, t->fe->errmsg.len502, t->fe->errmsg.msg502);
+						client_return(t, &t->fe->errmsg[HTTP_ERR_502]);
 						if (!(t->flags & SN_ERR_MASK))
 							t->flags |= SN_ERR_PRXCOND;
 						if (!(t->flags & SN_FINST_MASK))
@@ -1726,7 +1819,7 @@
 					t->be->failed_secu++;
 					t->srv_state = SV_STCLOSE;
 					t->logs.status = 502;
-					client_return(t, t->fe->errmsg.len502, t->fe->errmsg.msg502);
+					client_return(t, &t->fe->errmsg[HTTP_ERR_502]);
 					if (!(t->flags & SN_ERR_MASK))
 						t->flags |= SN_ERR_PRXCOND;
 					if (!(t->flags & SN_FINST_MASK))
@@ -2166,7 +2259,7 @@
 
 			t->srv_state = SV_STCLOSE;
 			t->logs.status = 502;
-			client_return(t, t->fe->errmsg.len502, t->fe->errmsg.msg502);
+			client_return(t, &t->fe->errmsg[HTTP_ERR_502]);
 			if (!(t->flags & SN_ERR_MASK))
 				t->flags |= SN_ERR_SRVCL;
 			if (!(t->flags & SN_FINST_MASK))
@@ -2204,7 +2297,7 @@
 			t->be->failed_resp++;
 			t->srv_state = SV_STCLOSE;
 			t->logs.status = 504;
-			client_return(t, t->fe->errmsg.len504, t->fe->errmsg.msg504);
+			client_return(t, &t->fe->errmsg[HTTP_ERR_504]);
 			if (!(t->flags & SN_ERR_MASK))
 				t->flags |= SN_ERR_SRVTO;
 			if (!(t->flags & SN_FINST_MASK))
@@ -2596,14 +2689,15 @@
 	struct buffer *rep = s->rep;
 	struct proxy *px;
 	struct server *sv;
-	int msglen;
+	struct chunk msg;
 
 	if (s->data_source == DATA_SRC_NONE) {
 		s->flags &= ~SN_SELF_GEN;
 		return 1;
 	}
 	else if (s->data_source == DATA_SRC_STATS) {
-		msglen = 0;
+		msg.len = 0;
+		msg.str = trash;
 
 		if (s->data_state == DATA_ST_INIT) { /* the function had not been called yet */
 			unsigned int up;
@@ -2611,7 +2705,7 @@
 			s->flags |= SN_SELF_GEN;  // more data will follow
 
 			/* send the start of the HTTP response */
-			msglen += snprintf(trash + msglen, sizeof(trash) - msglen,
+			msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
 					   "HTTP/1.0 200 OK\r\n"
 					   "Cache-Control: no-cache\r\n"
 					   "Connection: close\r\n"
@@ -2619,8 +2713,8 @@
 					   "\r\n\r\n");
 	    
 			s->logs.status = 200;
-			client_retnclose(s, msglen, trash); // send the start of the response.
-			msglen = 0;
+			client_retnclose(s, &msg); // send the start of the response.
+			msg.len = 0;
 
 			if (!(s->flags & SN_ERR_MASK))  // this is not really an error but it is
 				s->flags |= SN_ERR_PRXCOND; // to mark that it comes from the proxy
@@ -2628,7 +2722,7 @@
 				s->flags |= SN_FINST_R;
 
 			/* WARNING! This must fit in the first buffer !!! */	    
-			msglen += snprintf(trash + msglen, sizeof(trash) - msglen,
+			msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
 					   "<html><head><title>Statistics Report for " PRODUCT_NAME "</title>\n"
 					   "<meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n"
 					   "<style type=\"text/css\"><!--\n"
@@ -2682,14 +2776,14 @@
 					   "-->"
 					   "</style></head>");
 
-			if (buffer_write(rep, trash, msglen) != 0)
+			if (buffer_write(rep, trash, msg.len) != 0)
 				return 0;
-			msglen = 0;
+			msg.len = 0;
 
 			up = (now.tv_sec - start_date.tv_sec);
 
 			/* WARNING! this has to fit the first packet too */
-			msglen += snprintf(trash + msglen, sizeof(trash) - msglen,
+			msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
 					   "<body><h1>" PRODUCT_NAME "</h1>\n"
 					   "<h2>Statistics Report for pid %d</h2>\n"
 					   "<hr width=\"100%%\" class=\"hr\">\n"
@@ -2725,9 +2819,9 @@
 					   actconn
 					   );
 	    
-			if (buffer_write(rep, trash, msglen) != 0)
+			if (buffer_write(rep, trash, msg.len) != 0)
 				return 0;
-			msglen = 0;
+			msg.len = 0;
 
 			s->data_state = DATA_ST_DATA;
 			memset(&s->data_ctx, 0, sizeof(s->data_ctx));
@@ -2773,7 +2867,7 @@
 						goto next_proxy;
 				}
 
-				msglen += snprintf(trash + msglen, sizeof(trash) - msglen,
+				msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
 						   "<h3>&gt; Proxy instance %s : "
 						   "%d front conns (max=%d), %d back, "
 						   "%d queued (%d unassigned), %d total front conns, %d back</h3>\n"
@@ -2782,7 +2876,7 @@
 						   px->feconn, px->maxconn, px->beconn,
 						   px->totpend, px->nbpend, px->cum_feconn, px->cum_beconn);
 		
-				msglen += snprintf(trash + msglen, sizeof(trash) - msglen,
+				msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
 						   "<table cols=\"16\" class=\"tbl\">\n"
 						   "<tr align=\"center\" bgcolor=\"#20C0C0\">"
 						   "<th colspan=5>Server</th>"
@@ -2795,9 +2889,9 @@
 						   "<th>Curr.</th><th>Max.</th><th>Limit</th><th>Cumul.</th>"
 						   "<th>Conn.</th><th>Resp.</th><th>Sec.</th><th>Check</th><th>Down</th></tr>\n");
 		
-				if (buffer_write(rep, trash, msglen) != 0)
+				if (buffer_write(rep, trash, msg.len) != 0)
 					return 0;
-				msglen = 0;
+				msg.len = 0;
 
 				s->data_ctx.stats.sv = px->srv;
 				s->data_ctx.stats.px_st = DATA_ST_DATA;
@@ -2829,48 +2923,48 @@
 						sv_state = 0; /* DOWN */
 
 				/* name, weight */
-				msglen += snprintf(trash, sizeof(trash),
+				msg.len += snprintf(trash, sizeof(trash),
 						   "<tr align=center bgcolor=\"%s\"><td>%s</td><td>%d</td><td>",
 						   (sv->state & SRV_BACKUP) ? bck_tab_bg[sv_state] : act_tab_bg[sv_state],
 						   sv->id, sv->uweight+1);
 				/* status */
-				msglen += snprintf(trash + msglen, sizeof(trash) - msglen, srv_hlt_st[sv_state],
+				msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, srv_hlt_st[sv_state],
 						   (sv->state & SRV_RUNNING) ? (sv->health - sv->rise + 1) : (sv->health),
 						   (sv->state & SRV_RUNNING) ? (sv->fall) : (sv->rise));
 
 				/* act, bck */
-				msglen += snprintf(trash + msglen, sizeof(trash) - msglen,
+				msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
 						   "</td><td>%s</td><td>%s</td>",
 						   (sv->state & SRV_BACKUP) ? "-" : "Y",
 						   (sv->state & SRV_BACKUP) ? "Y" : "-");
 
 				/* queue : current, max */
-				msglen += snprintf(trash + msglen, sizeof(trash) - msglen,
+				msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
 						   "<td align=right>%d</td><td align=right>%d</td>",
 						   sv->nbpend, sv->nbpend_max);
 
 				/* sessions : current, max, limit, cumul */
-				msglen += snprintf(trash + msglen, sizeof(trash) - msglen,
+				msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
 						   "<td align=right>%d</td><td align=right>%d</td><td align=right>%s</td><td align=right>%d</td>",
 						   sv->cur_sess, sv->cur_sess_max, sv->maxconn ? ultoa(sv->maxconn) : "-", sv->cum_sess);
 
 				/* errors : connect, response, security */
-				msglen += snprintf(trash + msglen, sizeof(trash) - msglen,
+				msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
 						   "<td align=right>%d</td><td align=right>%d</td><td align=right>%d</td>\n",
 						   sv->failed_conns, sv->failed_resp, sv->failed_secu);
 
 				/* check failures : unique, fatal */
 				if (sv->state & SRV_CHECKED)
-					msglen += snprintf(trash + msglen, sizeof(trash) - msglen,
+					msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
 							   "<td align=right>%d</td><td align=right>%d</td></tr>\n",
 							   sv->failed_checks, sv->down_trans);
 				else
-					msglen += snprintf(trash + msglen, sizeof(trash) - msglen,
+					msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
 							   "<td align=right>-</td><td align=right>-</td></tr>\n");
 
-				if (buffer_write(rep, trash, msglen) != 0)
+				if (buffer_write(rep, trash, msg.len) != 0)
 					return 0;
-				msglen = 0;
+				msg.len = 0;
 
 				s->data_ctx.stats.sv = sv->next;
 			} /* while sv */
@@ -2904,35 +2998,35 @@
 			}
 
 			/* name, weight, status, act, bck */
-			msglen += snprintf(trash + msglen, sizeof(trash),
+			msg.len += snprintf(trash + msg.len, sizeof(trash),
 					   "<tr align=center bgcolor=\"#e8e8d0\">"
 					   "<td>Dispatcher</td><td>-</td>"
 					   "<td>%s</td><td>-</td><td>-</td>",
 					   px->state == PR_STRUN ? "UP" : "DOWN");
 
 			/* queue : current, max */
-			msglen += snprintf(trash + msglen, sizeof(trash) - msglen,
+			msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
 					   "<td align=right>%d</td><td align=right>%d</td>",
 					   px->nbpend, px->nbpend_max);
 
 			/* sessions : current, max, limit, cumul. */
-			msglen += snprintf(trash + msglen, sizeof(trash) - msglen,
+			msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
 					   "<td align=right>%d</td><td align=right>%d</td><td align=right>-</td><td align=right>%d</td>",
 					   dispatch_sess, px->beconn_max, dispatch_cum);
 
 			/* errors : connect, response, security */
-			msglen += snprintf(trash + msglen, sizeof(trash) - msglen,
+			msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
 					   "<td align=right>%d</td><td align=right>%d</td><td align=right>%d</td>\n",
 					   failed_conns, failed_resp, failed_secu);
 
 			/* check failures : unique, fatal */
-			msglen += snprintf(trash + msglen, sizeof(trash) - msglen,
+			msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
 					   "<td align=right>-</td><td align=right>-</td></tr>\n");
 
 
 			/* now the summary for the whole proxy */
 			/* name, weight, status, act, bck */
-			msglen += snprintf(trash + msglen, sizeof(trash),
+			msg.len += snprintf(trash + msg.len, sizeof(trash),
 					   "<tr align=center style=\"color: #ffff80;  background: #20C0C0;\">"
 					   "<td><b>Total</b></td><td>-</td>"
 					   "<td><b>%s</b></td><td><b>%d</b></td><td><b>%d</b></td>",
@@ -2940,30 +3034,30 @@
 					   px->srv_act, px->srv_bck);
 
 			/* queue : current, max */
-			msglen += snprintf(trash + msglen, sizeof(trash) - msglen,
+			msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
 					   "<td align=right><b>%d</b></td><td align=right><b>%d</b></td>",
 					   px->totpend, px->nbpend_max);
 
 			/* sessions : current, max, limit, cumul */
-			msglen += snprintf(trash + msglen, sizeof(trash) - msglen,
+			msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
 					   "<td align=right><b>%d</b></td><td align=right><b>%d</b></td><td align=right><b>-</b></td><td align=right><b>%d</b></td>",
 					   px->beconn, px->beconn_max, px->cum_beconn);
 
 			/* errors : connect, response, security */
-			msglen += snprintf(trash + msglen, sizeof(trash) - msglen,
+			msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
 					   "<td align=right>%d</td><td align=right>%d</td><td align=right>%d</td>\n",
 					   px->failed_conns, px->failed_resp, px->failed_secu);
 
 			/* check failures : unique, fatal */
-			msglen += snprintf(trash + msglen, sizeof(trash) - msglen,
+			msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
 					   "<td align=right>%d</td><td align=right>%d</td></tr>\n",
 					   failed_checks, down_trans);
 
-			msglen += snprintf(trash + msglen, sizeof(trash) - msglen, "</table><p>\n");
+			msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, "</table><p>\n");
 
-			if (buffer_write(rep, trash, msglen) != 0)
+			if (buffer_write(rep, trash, msg.len) != 0)
 				return 0;
-			msglen = 0;
+			msg.len = 0;
 	    
 			s->data_ctx.stats.px_st = DATA_ST_INIT;
 		next_proxy:
@@ -2976,7 +3070,7 @@
 	else {
 		/* unknown data source */
 		s->logs.status = 500;
-		client_retnclose(s, s->fe->errmsg.len500, s->fe->errmsg.msg500);
+		client_retnclose(s, &s->fe->errmsg[HTTP_ERR_500]);
 		if (!(s->flags & SN_ERR_MASK))
 			s->flags |= SN_ERR_PRXCOND;
 		if (!(s->flags & SN_FINST_MASK))
@@ -3631,12 +3725,13 @@
 	}
 
 	if (!authenticated) {
-		int msglen;
+		struct chunk msg;
 
 		/* no need to go further */
-		msglen = sprintf(trash, HTTP_401_fmt, uri_auth->auth_realm);
+		msg.str = trash;
+		msg.len = sprintf(trash, HTTP_401_fmt, uri_auth->auth_realm);
 		t->logs.status = 401;
-		client_retnclose(t, msglen, trash);
+		client_retnclose(t, &msg);
 		if (!(t->flags & SN_ERR_MASK))
 			t->flags |= SN_ERR_PRXCOND;
 		if (!(t->flags & SN_FINST_MASK))