]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MEDIUM] errorfile: use a local file to feed error messages
authorWilly Tarreau <w@1wt.eu>
Sun, 10 Jun 2007 22:29:26 +0000 (00:29 +0200)
committerWilly Tarreau <w@1wt.eu>
Sun, 10 Jun 2007 22:29:26 +0000 (00:29 +0200)
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.

12 files changed:
doc/haproxy-en.txt
doc/haproxy-fr.txt
examples/errorfiles/400.http [new file with mode: 0644]
examples/errorfiles/403.http [new file with mode: 0644]
examples/errorfiles/408.http [new file with mode: 0644]
examples/errorfiles/500.http [new file with mode: 0644]
examples/errorfiles/502.http [new file with mode: 0644]
examples/errorfiles/503.http [new file with mode: 0644]
examples/errorfiles/504.http [new file with mode: 0644]
examples/errorfiles/README [new file with mode: 0644]
examples/haproxy.cfg
src/cfgparse.c

index 2a278b0a1dd009a8ff7636bb6af48750d173cb1d..50a480285f4b5b1f5a3e32a903eaccafc0946172 100644 (file)
@@ -2311,8 +2311,13 @@ Some situations can make haproxy return an HTTP error code to the client :
 
 A succint error message taken from the RFC accompanies these return codes.
 But depending on the clients knowledge, it may be better to return custom, user
-friendly, error pages. This is made possible through the use of the 'errorloc'
-command :
+friendly, error pages. This is made possible in two ways, one involving a
+redirection to a known server, and another one consisting in returning a local
+file.
+
+4.6.1) Relocation
+-----------------
+An error relocation is achieved using the 'errorloc' command :
 
     errorloc <HTTP_code> <location>
 
@@ -2344,6 +2349,33 @@ bring two new keywords to replace 'errorloc' : 'errorloc302' and 'errorloc303'.
 They are preffered over errorloc (which still does 302). Consider using
 errorloc303 everytime you know that your clients support HTTP 303 responses..
 
+4.6.2) Local files
+------------------
+Sometimes, it is desirable to change the returned error without resorting to
+redirections. The second method consists in loading local files during startup
+and send them as pure HTTP content upon error. This is what the 'errorfile'
+keyword does.
+
+Warning, there are traps to consider :
+ - The files are loaded while parsing configuration, before doing a chroot().
+   Thus, they are relative to the real filesystem. For this reason, it is
+   recommended to pass an absolute path to those files.
+
+ - The contents of those files is not HTML, but real HTTP protocol with
+   possible HTML body. So the first line and headers are mandatory. Ideally,
+   every line in the HTTP part should end with CR-LF for maximum compatibility.
+
+ - The response is limited to the buffer size (BUSIZE), generally 8 or 16 kB.
+
+ - The response should not include references to the local server, in order to
+   avoid infinite loops on the browser in case of local failure.
+
+Example :
+---------
+        errorfile 400 /etc/haproxy/errorfiles/400badreq.http
+        errorfile 403 /etc/haproxy/errorfiles/403forbid.http
+        errorfile 503 /etc/haproxy/errorfiles/503sorry.http
+
 
 4.7) Modifying default values
 -----------------------------
index 7d6ebf143a8a091be4975ee908b08bb51f8efefd..9845cff8094578a08c8a0745953e172a2ecbee03 100644 (file)
@@ -2395,7 +2395,13 @@ Certaines situations conduisent 
 
 Un message d'erreur succint tiré de la RFC accompagne ces codes de retour.
 Cependant, en fonction du type de clientèle, on peut préférer retourner des
-pages personnalisées. Ceci est possible par le biais de la commande "errorloc":
+pages personnalisées. Ceci est possible de deux manières, l'une reposant sur
+une redirection vers un serveur connu, et l'autre consistant à retourner un
+fichier local.
+
+4.6.1) Redirection
+------------------
+Une redirection d'erreur est assurée par le biais de la commande "errorloc" :
 
     errorloc <code_HTTP> <location>
 
@@ -2431,6 +2437,38 @@ Leur usage non ambig
 utilise toujours 302). Dans le doute, préférez l'utilisation de 'errorloc303'
 dès que vous savez que vos clients supportent le code de retour HTTP 303.
 
+4.6.2) Fichiers locaux
+----------------------
+Parfois il est souhaitable de changer l'erreur retournée sans recourir à des
+redirections. La seconde méthode consiste à charger des fichiers locaux lors
+du démarrage et à les envoyer en guise de pur contenu HTTP en cas d'erreur.
+C'est ce que fait le mot clé 'errorfile'.
+
+Attention, il y a des pièges à prendre en compte :
+ - les fichiers sont chargés durant l'analyse de la configuration, avant de
+   faire le chroot(). Donc ils sont relatifs au système de fichiers réel. Pour
+   cette raison, il est recommandé de toujours passer un chemin absolu vers ces
+   fichiers.
+
+ - le contenu de ces fichiers n'est pas du HTML mais vraiment du protocole HTTP
+   avec potentiellement un corps HTML. Donc la première ligne et les en-têtes
+   sont obligatoires. Idéalement, chaque ligne dans la partie HTTP devrait se
+   terminer par un CR-LF pour un maximum de compatibilité.
+
+ - les réponses sont limitées à une taille de buffer (BUFSIZE), généralement 8
+   ou 16 ko.
+
+ - les réponses ne devraient pas inclure de références aux serveurs locaux,
+   afin de ne pas risquer de créer des boucles infinies sur le navigateur dans
+   le cas d'une panne locale.
+
+Exemple :
+---------
+        errorfile 400 /etc/haproxy/errorfiles/400badreq.http
+        errorfile 403 /etc/haproxy/errorfiles/403forbid.http
+        errorfile 503 /etc/haproxy/errorfiles/503sorry.http
+
+
 4.7) Changement des valeurs par défaut
 --------------------------------------
 Dans la version 1.1.22 est apparue la notion de valeurs par défaut, ce qui
diff --git a/examples/errorfiles/400.http b/examples/errorfiles/400.http
new file mode 100644 (file)
index 0000000..e223e38
--- /dev/null
@@ -0,0 +1,9 @@
+HTTP/1.0 400 Bad request\r
+Cache-Control: no-cache\r
+Connection: close\r
+Content-Type: text/html\r
+\r
+<html><body><h1>400 Bad request</h1>
+Your browser sent an invalid request.
+</body></html>
+
diff --git a/examples/errorfiles/403.http b/examples/errorfiles/403.http
new file mode 100644 (file)
index 0000000..a67e807
--- /dev/null
@@ -0,0 +1,9 @@
+HTTP/1.0 403 Forbidden\r
+Cache-Control: no-cache\r
+Connection: close\r
+Content-Type: text/html\r
+\r
+<html><body><h1>403 Forbidden</h1>
+Request forbidden by administrative rules.
+</body></html>
+
diff --git a/examples/errorfiles/408.http b/examples/errorfiles/408.http
new file mode 100644 (file)
index 0000000..aafb130
--- /dev/null
@@ -0,0 +1,9 @@
+HTTP/1.0 408 Request Time-out\r
+Cache-Control: no-cache\r
+Connection: close\r
+Content-Type: text/html\r
+\r
+<html><body><h1>408 Request Time-out</h1>
+Your browser didn't send a complete request in time.
+</body></html>
+
diff --git a/examples/errorfiles/500.http b/examples/errorfiles/500.http
new file mode 100644 (file)
index 0000000..bb121e8
--- /dev/null
@@ -0,0 +1,9 @@
+HTTP/1.0 500 Server Error\r
+Cache-Control: no-cache\r
+Connection: close\r
+Content-Type: text/html\r
+\r
+<html><body><h1>500 Server Error</h1>
+An internal server error occured.
+</body></html>
+
diff --git a/examples/errorfiles/502.http b/examples/errorfiles/502.http
new file mode 100644 (file)
index 0000000..94b35d4
--- /dev/null
@@ -0,0 +1,9 @@
+HTTP/1.0 502 Bad Gateway\r
+Cache-Control: no-cache\r
+Connection: close\r
+Content-Type: text/html\r
+\r
+<html><body><h1>502 Bad Gateway</h1>
+The server returned an invalid or incomplete response.
+</body></html>
+
diff --git a/examples/errorfiles/503.http b/examples/errorfiles/503.http
new file mode 100644 (file)
index 0000000..48fde58
--- /dev/null
@@ -0,0 +1,9 @@
+HTTP/1.0 503 Service Unavailable\r
+Cache-Control: no-cache\r
+Connection: close\r
+Content-Type: text/html\r
+\r
+<html><body><h1>503 Service Unavailable</h1>
+No server is available to handle this request.
+</body></html>
+
diff --git a/examples/errorfiles/504.http b/examples/errorfiles/504.http
new file mode 100644 (file)
index 0000000..f925184
--- /dev/null
@@ -0,0 +1,9 @@
+HTTP/1.0 504 Gateway Time-out\r
+Cache-Control: no-cache\r
+Connection: close\r
+Content-Type: text/html\r
+\r
+<html><body><h1>504 Gateway Time-out</h1>
+The server didn't respond in time.
+</body></html>
+
diff --git a/examples/errorfiles/README b/examples/errorfiles/README
new file mode 100644 (file)
index 0000000..a882632
--- /dev/null
@@ -0,0 +1,9 @@
+These files are default error files that can be customized
+if necessary. They are complete HTTP responses, so that
+everything is possible, including using redirects or setting
+special headers.
+
+They can be used with the 'errorfile' keyword like this :
+
+    errorfile 503 /etc/haproxy/errors/503.http
+
index 1add7ff3409675e09b50544e4ef96a6853ee4369..1c71d617716e5826de72734f914d1a38d1b1c2c5 100644 (file)
@@ -76,4 +76,5 @@ listen        appli5-backup 0.0.0.0:10005
        rspidel ^Set-cookie:\ IP=       # do not let this cookie tell our internal IP address
        
        errorloc        502     http://192.168.114.58/error502.html
+       errorfile       503     /etc/haproxy/errors/503.http
 
index 69f11e0c53b8e4d6bf33453c97c99332e25c5c24..77bd8277e904fe8c75967a2c33cf11fa02c33481 100644 (file)
 #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 cfg_parse_listen(const char *file, int linenum, char **args)
                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 @@ int cfg_parse_listen(const char *file, int linenum, char **args)
                        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;