diff --git a/doc/haproxy-en.txt b/doc/haproxy-en.txt index 2a278b0a1..50a480285 100644 --- a/doc/haproxy-en.txt +++ b/doc/haproxy-en.txt @@ -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 @@ -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 ----------------------------- diff --git a/doc/haproxy-fr.txt b/doc/haproxy-fr.txt index 7d6ebf143..9845cff80 100644 --- a/doc/haproxy-fr.txt +++ b/doc/haproxy-fr.txt @@ -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 @@ -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 index 000000000..e223e38d9 --- /dev/null +++ b/examples/errorfiles/400.http @@ -0,0 +1,9 @@ +HTTP/1.0 400 Bad request +Cache-Control: no-cache +Connection: close +Content-Type: text/html + +

400 Bad request

+Your browser sent an invalid request. + + diff --git a/examples/errorfiles/403.http b/examples/errorfiles/403.http new file mode 100644 index 000000000..a67e80755 --- /dev/null +++ b/examples/errorfiles/403.http @@ -0,0 +1,9 @@ +HTTP/1.0 403 Forbidden +Cache-Control: no-cache +Connection: close +Content-Type: text/html + +

403 Forbidden

+Request forbidden by administrative rules. + + diff --git a/examples/errorfiles/408.http b/examples/errorfiles/408.http new file mode 100644 index 000000000..aafb1300f --- /dev/null +++ b/examples/errorfiles/408.http @@ -0,0 +1,9 @@ +HTTP/1.0 408 Request Time-out +Cache-Control: no-cache +Connection: close +Content-Type: text/html + +

408 Request Time-out

+Your browser didn't send a complete request in time. + + diff --git a/examples/errorfiles/500.http b/examples/errorfiles/500.http new file mode 100644 index 000000000..bb121e82a --- /dev/null +++ b/examples/errorfiles/500.http @@ -0,0 +1,9 @@ +HTTP/1.0 500 Server Error +Cache-Control: no-cache +Connection: close +Content-Type: text/html + +

500 Server Error

+An internal server error occured. + + diff --git a/examples/errorfiles/502.http b/examples/errorfiles/502.http new file mode 100644 index 000000000..94b35d492 --- /dev/null +++ b/examples/errorfiles/502.http @@ -0,0 +1,9 @@ +HTTP/1.0 502 Bad Gateway +Cache-Control: no-cache +Connection: close +Content-Type: text/html + +

502 Bad Gateway

+The server returned an invalid or incomplete response. + + diff --git a/examples/errorfiles/503.http b/examples/errorfiles/503.http new file mode 100644 index 000000000..48fde5881 --- /dev/null +++ b/examples/errorfiles/503.http @@ -0,0 +1,9 @@ +HTTP/1.0 503 Service Unavailable +Cache-Control: no-cache +Connection: close +Content-Type: text/html + +

503 Service Unavailable

+No server is available to handle this request. + + diff --git a/examples/errorfiles/504.http b/examples/errorfiles/504.http new file mode 100644 index 000000000..f92518414 --- /dev/null +++ b/examples/errorfiles/504.http @@ -0,0 +1,9 @@ +HTTP/1.0 504 Gateway Time-out +Cache-Control: no-cache +Connection: close +Content-Type: text/html + +

504 Gateway Time-out

+The server didn't respond in time. + + diff --git a/examples/errorfiles/README b/examples/errorfiles/README new file mode 100644 index 000000000..a882632f8 --- /dev/null +++ b/examples/errorfiles/README @@ -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 + diff --git a/examples/haproxy.cfg b/examples/haproxy.cfg index 1add7ff34..1c71d6177 100644 --- a/examples/haproxy.cfg +++ b/examples/haproxy.cfg @@ -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 diff --git a/src/cfgparse.c b/src/cfgparse.c index 69f11e0c5..77bd8277e 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -18,6 +18,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -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 and 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;