[MEDIUM] errorfile: use a local file to feed error messages
It is now possible to read error messages from local files,
using the 'errorfile' keyword. Those files are read during
parsing, so there's no I/O involved. They make it possible
to return custom error messages with custom status and headers.
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 69f11e0..77bd827 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -18,6 +18,10 @@
#include <pwd.h>
#include <grp.h>
#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
#include <common/cfgparse.h>
#include <common/config.h>
@@ -2093,11 +2097,6 @@
int errnum, errlen;
char *err;
- // if (curproxy == &defproxy) {
- // Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
- // return -1;
- // }
-
if (warnifnotcap(curproxy, PR_CAP_FE | PR_CAP_BE, file, linenum, args[0], NULL))
return 0;
@@ -2131,6 +2130,64 @@
free(err);
}
}
+ else if (!strcmp(args[0], "errorfile")) { /* error message from a file */
+ int errnum, errlen, fd;
+ char *err;
+ struct stat stat;
+
+ if (warnifnotcap(curproxy, PR_CAP_FE | PR_CAP_BE, file, linenum, args[0], NULL))
+ return 0;
+
+ if (*(args[2]) == 0) {
+ Alert("parsing [%s:%d] : <%s> expects <status_code> and <file> as arguments.\n", file, linenum);
+ return -1;
+ }
+
+ fd = open(args[2], O_RDONLY);
+ if ((fd < 0) || (fstat(fd, &stat) < 0)) {
+ Alert("parsing [%s:%d] : error opening file <%s> for custom error message <%s>.\n",
+ file, linenum, args[2], args[1]);
+ if (fd >= 0)
+ close(fd);
+ return -1;
+ }
+
+ if (stat.st_size <= BUFSIZE) {
+ errlen = stat.st_size;
+ } else {
+ Warning("parsing [%s:%d] : custom error message file <%s> larger than %d bytes. Truncating.\n",
+ file, linenum, args[2], BUFSIZE);
+ errlen = BUFSIZE;
+ }
+
+ err = malloc(errlen); /* malloc() must succeed during parsing */
+ errnum = read(fd, err, errlen);
+ if (errnum != errlen) {
+ Alert("parsing [%s:%d] : error reading file <%s> for custom error message <%s>.\n",
+ file, linenum, args[2], args[1]);
+ close(fd);
+ free(err);
+ return -1;
+ }
+ close(fd);
+
+ errnum = atol(args[1]);
+ 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;
+ }
+ }
+
+ if (rc >= HTTP_ERR_SIZE) {
+ Warning("parsing [%s:%d] : status code %d not handled, error customization will be ignored.\n",
+ file, linenum, errnum);
+ free(err);
+ }
+ }
else {
Alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section\n", file, linenum, args[0], "listen");
return -1;