* released 1.1.17

* add the notion of "backup" servers, which are used only when all other
  servers are down.
* make Set-Cookie return "" instead of "(null)" when the server has no
  cookie assigned (useful for backup servers).
* "log" now supports an optionnal level name (info, notice, err ...) above
  which nothing is sent.
* replaced some strncmp() with memcmp() for better efficiency.
* added "capture cookie" option which logs client and/or server cookies
* cleaned up/down messages and dump servers states upon SIGHUP
* added a redirection feature for errors : "errorloc <errnum> <url>"
* now we won't insist on connecting to a dead server, even with a cookie,
  unless option "persist" is specified.
* added HTTP/408 response for client request time-out and HTTP/50[234] for
  server reply time-out or errors.
* updates to the examples files
* added a 'do_status' command to the Formilux init script
This commit is contained in:
willy tarreau 2005-12-17 13:41:01 +01:00
parent bc4e1fb68a
commit 8337c6b7bd
5 changed files with 661 additions and 125 deletions

View File

@ -1,5 +1,20 @@
* ChangeLog : * ChangeLog :
* *
* 2002/10/18 : 1.1.17
* - add the notion of "backup" servers, which are used only when all other
* servers are down.
* - make Set-Cookie return "" instead of "(null)" when the server has no
* cookie assigned (useful for backup servers).
* - "log" now supports an optionnal level name (info, notice, err ...) above
* which nothing is sent.
* - replaced some strncmp() with memcmp() for better efficiency.
* - added "capture cookie" option which logs client and/or server cookies
* - cleaned up/down messages and dump servers states upon SIGHUP
* - added a redirection feature for errors : "errorloc <errnum> <url>"
* - now we won't insist on connecting to a dead server, even with a cookie,
* unless option "persist" is specified.
* - added HTTP/408 response for client request time-out and HTTP/50[234] for
* server reply time-out or errors.
* 2002/09/01 : 1.1.16 * 2002/09/01 : 1.1.16
* - implement HTTP health checks when option "httpchk" is specified. * - implement HTTP health checks when option "httpchk" is specified.
* 2002/08/07 : 1.1.15 * 2002/08/07 : 1.1.15

View File

@ -1,9 +1,9 @@
H A - P r o x y H A - P r o x y
--------------- ---------------
version 1.1.16 version 1.1.17
willy tarreau willy tarreau
2002/09/01 2002/10/25
================ ================
| Introduction | | Introduction |
@ -15,9 +15,11 @@ environnement hautement disponible. En effet, il est capable de :
- effectuer une répartition de charge avec création de cookies pour assurer la - effectuer une répartition de charge avec création de cookies pour assurer la
persistence de session ; persistence de session ;
- fournir une visibilité externe de son état de santé ; - fournir une visibilité externe de son état de santé ;
- s'arrêter en douceur sans perte brutale de service. - s'arrêter en douceur sans perte brutale de service ;
- modifier/ajouter/supprimer des entêtes dans la requête et la réponse. - modifier/ajouter/supprimer des entêtes dans la requête et la réponse ;
- interdire des requêtes qui vérifient certaines conditions. - interdire des requêtes qui vérifient certaines conditions ;
- utiliser des serveurs de secours lorsque les serveurs principaux sont hors
d'usage.
Il requiert peu de ressources, et son architecture événementielle mono-processus Il requiert peu de ressources, et son architecture événementielle mono-processus
lui permet facilement de gérer plusieurs milliers de connexions simultanées sur lui permet facilement de gérer plusieurs milliers de connexions simultanées sur
@ -83,7 +85,7 @@ Il s'agit des param
proxies. Ils sont tous spécifiés dans la section 'global'. Les paramètres proxies. Ils sont tous spécifiés dans la section 'global'. Les paramètres
supportés sont : supportés sont :
- log <adresse> <catégorie> - log <adresse> <catégorie> [niveau_max]
- maxconn <nombre> - maxconn <nombre>
- uid <identifiant> - uid <identifiant>
- gid <identifiant> - gid <identifiant>
@ -99,12 +101,18 @@ La plupart des
apparition de serveurs, connexions, erreurs. Tous les messages sont envoyés en apparition de serveurs, connexions, erreurs. Tous les messages sont envoyés en
syslog vers un ou deux serveurs. La syntaxe est la suivante : syslog vers un ou deux serveurs. La syntaxe est la suivante :
log <adresse_ip> <facility> log <adresse_ip> <catégorie> [niveau_max]
Les connexions sont envoyées en niveau "info". Les démarrages de service et de Les connexions sont envoyées en niveau "info". Les démarrages de service et de
serveurs seront envoyés en "notice", les signaux d'arrêts en "warning" et les serveurs seront envoyés en "notice", les signaux d'arrêts en "warning" et les
arrêts définitifs de services et de serveurs en "alert". Ceci est valable aussi arrêts définitifs de services et de serveurs en "alert". Ceci est valable aussi
bien pour les proxies que pour les serveurs testés par les proxies. bien pour les proxies que pour les serveurs testés par les proxies. Le paramètre
optionnel <niveau_max> définit le niveau maximal de traces émises parmi les 8
valeurs suivantes :
emerg, alert, crit, err, warning, notice, info, debug
Par compatibilité avec les versions 1.1.16 et antérieures, La valeur par défaut
est "debug" si l'option n'est pas précisée.
Les catégories possibles sont : Les catégories possibles sont :
kern, user, mail, daemon, auth, syslog, lpr, news, kern, user, mail, daemon, auth, syslog, lpr, news,
@ -115,7 +123,7 @@ Exemple :
--------- ---------
global global
log 192.168.2.200 local3 log 192.168.2.200 local3
log 192.168.2.201 local4 log 127.0.0.1 local4 notice
1.2) limitation du nombre de connexions 1.2) limitation du nombre de connexions
--------------------------------------- ---------------------------------------
@ -139,7 +147,7 @@ capable de positionner lui-m
------------------------------ ------------------------------
Afin de réduire les risques d'attaques dans le cas où une faille non identifiée Afin de réduire les risques d'attaques dans le cas où une faille non identifiée
serait exploitée, il est possible de diminuer les privilèges du processus, et serait exploitée, il est possible de diminuer les privilèges du processus, et
de le cloisonner. de l'isoler dans un répertoire sans risque.
Dans la section 'global', le paramètre 'uid' permet de spécifier un identifiant Dans la section 'global', le paramètre 'uid' permet de spécifier un identifiant
numérique d'utilisateur. La valeur 0, correspondant normalement au super- numérique d'utilisateur. La valeur 0, correspondant normalement au super-
@ -153,11 +161,14 @@ processus utilisent les m
Le paramètre 'chroot' autorise à changer la racine du processus une fois le Le paramètre 'chroot' autorise à changer la racine du processus une fois le
programme lancé, de sorte que ni le processus, ni l'un de ses descendants ne programme lancé, de sorte que ni le processus, ni l'un de ses descendants ne
puisse remonter de nouveau à la racine. Ce type de cloisonnement (chroot) est puissent remonter de nouveau à la racine. Ce type de cloisonnement (chroot) est
parfois contournable sur certains OS (Linux 2.2, Solaris), mais visiblement parfois contournable sur certains OS (Linux 2.2, Solaris), mais visiblement
fiable sur d'autres (Linux 2.4). Aussi, il est important d'utiliser un fiable sur d'autres (Linux 2.4). Aussi, il est important d'utiliser un
répertoire spécifique au service pour cet usage, et de ne pas mutualiser un même répertoire spécifique au service pour cet usage, et de ne pas mutualiser un même
répertoire pour plusieurs services de nature différente. répertoire pour plusieurs services de nature différente. Pour rendre l'isolement
plus robuste, il est conseillé d'utiliser un répertoire vide, sans aucun droit,
et de changer l'uid du processus de sorte qu'il ne puisse rien faire dans ledit
répertoire.
Remarque: dans le cas où une telle faille serait mise en évidence, il est fort Remarque: dans le cas où une telle faille serait mise en évidence, il est fort
probable que les premières tentatives de son exploitation provoquent un arrêt du probable que les premières tentatives de son exploitation provoquent un arrêt du
@ -473,8 +484,9 @@ lors d'un acc
cookie SERVERID rewrite cookie SERVERID rewrite
Pour créer un cookie comportant la valeur attribuée à un serveur lors d'un accès Pour créer un cookie comportant la valeur attribuée à un serveur lors d'un accès
en répartition de charge interne. Dans ce cas, il est indispensable que tous les en répartition de charge interne. Dans ce cas, il est souhaitable que tous les
serveurs aient un cookie renseigné : serveurs aient un cookie renseigné. Un serveur non assigné d'un cookie
retournera un cookie vide (cookie de suppression) :
cookie SERVERID insert cookie SERVERID insert
@ -510,8 +522,8 @@ Remarques :
2.10) Assignation d'un serveur à une valeur de cookie 2.10) Assignation d'un serveur à une valeur de cookie
---------------------------------------------------- ----------------------------------------------------
En mode HTTP, il est possible d'associer des serveurs à des valeurs de En mode HTTP, il est possible d'associer des valeurs de cookie à des serveurs
cookie par le paramètre 'server'. La syntaxe est : par le paramètre 'server'. La syntaxe est :
server <identifiant> <adresse_ip>:<port> cookie <valeur> server <identifiant> <adresse_ip>:<port> cookie <valeur>
@ -558,7 +570,7 @@ Exemple : m
------------------------------ ------------------------------
Il est possible de tester l'état des serveurs par établissement de connexion TCP Il est possible de tester l'état des serveurs par établissement de connexion TCP
ou par envoi d'une requête HTTP. Un serveur hors d'usage ne sera pas utilisé ou par envoi d'une requête HTTP. Un serveur hors d'usage ne sera pas utilisé
dans leprocessus de répartition de charge interne. Pour activer la surveillance, dans le processus de répartition de charge interne. Pour activer la surveillance,
ajouter le mot clé 'check' à la fin de la déclaration du serveur. Il est ajouter le mot clé 'check' à la fin de la déclaration du serveur. Il est
possible de spécifier l'intervalle (en millisecondes) séparant deux tests du possible de spécifier l'intervalle (en millisecondes) séparant deux tests du
serveur par le paramètre "inter", le nombre d'échecs acceptés par le paramètre serveur par le paramètre "inter", le nombre d'échecs acceptés par le paramètre
@ -578,6 +590,25 @@ consid
Le temps maximal imparti pour une réponse est égal à l'intervalle entre deux Le temps maximal imparti pour une réponse est égal à l'intervalle entre deux
tests (paramètre "inter"). Pour activer ce mode, spécifier l'option "httpchk". tests (paramètre "inter"). Pour activer ce mode, spécifier l'option "httpchk".
Depuis la version 1.1.17, il est possible de définir des serveurs de secours,
utilisés uniquement lorsqu'aucun des autres serveurs ne fonctionne. Pour cela,
ajouter le mot clé "backup" sur la ligne de définition du serveur. Un serveur
de secours n'est appelé que lorsque tous les serveurs normaux, ainsi que tous
les serveurs de secours qui le précèdent sont hors d'usage. Il n'y a donc pas
de répartition de charge entre des serveurs de secours. Ce type de serveurs
peut servir à retourner des pages d'indisponibilité de service. Dans ce cas,
il est préférable de ne pas affecter de cookie, afin que les clients qui le
rencontrent n'y soient pas affectés définitivement. Le fait de ne pas mettre
de cookie envoie un cookie vide, ce qui a pour effet de supprimer un éventuel
cookie affecté précédemment.
Enfin, depuis la version 1.1.17, il est possible de visualiser rapidement l'état
courant de tous les serveurs. Pour cela, il suffit d'envoyer un signal SIGHUP au
processus proxy. L'état de tous les serveurs de tous les proxies est envoyé dans
les logs en niveau "notice", ainsi que sur la sortie d'erreurs si elle est
active. C'est une bonne raison pour avoir au moins un serveur de logs local en
niveau notice.
Exemples : Exemples :
---------- ----------
# même que précédemment avec surveillance TCP # même que précédemment avec surveillance TCP
@ -607,6 +638,16 @@ Exemples :
server web1 192.168.1.1:80 cookie server01 check server web1 192.168.1.1:80 cookie server01 check
server web2 192.168.1.2:80 cookie server02 check server web2 192.168.1.2:80 cookie server02 check
# idem avec serveur applicatif de secours, et serveur de pages d'erreurs
listen web_appl 0.0.0.0:80
mode http
cookie SERVERID insert nocache indirect
balance roundrobin
server web1 192.168.1.1:80 cookie server01 check
server web2 192.168.1.2:80 cookie server02 check
server web-backup 192.168.1.3:80 cookie server03 check backup
server web-excuse 192.168.1.4:80 check backup
3.2) Reconnexion vers un répartiteur en cas d'échec direct 3.2) Reconnexion vers un répartiteur en cas d'échec direct
---------------------------------------------------------- ----------------------------------------------------------
@ -624,6 +665,22 @@ Exemple :
dispatch 192.168.1.100:80 dispatch 192.168.1.100:80
server web1 192.168.1.1:80 cookie server01 server web1 192.168.1.1:80 cookie server01
server web2 192.168.1.2:80 cookie server02 server web2 192.168.1.2:80 cookie server02
redispatch # renvoyer vers dispatch si refus de connexion.
Par défaut (et dans les versions 1.1.16 et antérieures), le paramètre redispatch
ne s'applique qu'aux échecs de connexion au serveur. Depuis la version 1.1.17,
il s'applique aussi aux connexions destinées à des serveurs identifiés comme
hors d'usage par la surveillance. Si l'on souhaite malgré tout qu'un client
disposant d'un cookie correspondant à un serveur défectueux tente de s'y
connecter, il faut préciser l'option "persist" :
listen http_proxy 0.0.0.0:80
mode http
option persist
cookie SERVERID
dispatch 192.168.1.100:80
server web1 192.168.1.1:80 cookie server01
server web2 192.168.1.2:80 cookie server02
redispatch # renvoyer vers dispatch si serveur HS. redispatch # renvoyer vers dispatch si serveur HS.
@ -660,12 +717,14 @@ Exemple :
Les connexions TCP et HTTP peuvent donner lieu à une journalisation sommaire ou Les connexions TCP et HTTP peuvent donner lieu à une journalisation sommaire ou
détaillée indiquant, pour chaque connexion, la date, l'heure, l'adresse IP détaillée indiquant, pour chaque connexion, la date, l'heure, l'adresse IP
source, le serveur destination, la durée de la connexion, les temps de réponse, source, le serveur destination, la durée de la connexion, les temps de réponse,
la requête HTTP, le code de retour, la quantité de données transmise. la requête HTTP, le code de retour, la quantité de données transmises, et même
Tous les messages sont envoyés en syslog vers un ou deux serveurs. La syntaxe dans certains cas, la valeur d'un cookie permettant de suivre les sessions.
Tous les messages sont envoyés en syslog vers un ou deux serveurs. Se référer à
la section 1.1 pour plus d'information sur les catégories de logs. La syntaxe
est la suivante : est la suivante :
log <adresse_ip> <facility> log <adresse_ip_1> <catégorie_1> [niveau_max_1]
log <adresse_ip> <facility> log <adresse_ip_2> <catégorie_2> [niveau_max_2]
ou ou
log global log global
@ -681,15 +740,6 @@ Exemple :
log 192.168.2.200 local3 log 192.168.2.200 local3
log 192.168.2.201 local4 log 192.168.2.201 local4
Les connexions sont envoyées en niveau "info". Les démarrages de service seront
envoyés en "notice", les signaux d'arrêts en "warning" et les arrêts définitifs
en "alert". Ceci est valable aussi bien pour les proxies que pour les serveurs
testés au sein des proxies. Les catégories possibles sont :
kern, user, mail, daemon, auth, syslog, lpr, news,
uucp, cron, auth2, ftp, ntp, audit, alert, cron2,
local0, local1, local2, local3, local4, local5, local6, local7
Par défaut, les informations contenues dans les logs se situent au niveau TCP Par défaut, les informations contenues dans les logs se situent au niveau TCP
uniquement. Il faut préciser l'option 'httplog' pour obtenir les détails du uniquement. Il faut préciser l'option 'httplog' pour obtenir les détails du
protocole HTTP. Dans les cas où un mécanisme de surveillance effectuant des protocole HTTP. Dans les cas où un mécanisme de surveillance effectuant des
@ -697,6 +747,32 @@ connexions et d
l'option 'dontlognull', pour ne plus obtenir une ligne de log pour les sessions l'option 'dontlognull', pour ne plus obtenir une ligne de log pour les sessions
n'ayant pas donné lieu à un échange de données (requête ou réponse). n'ayant pas donné lieu à un échange de données (requête ou réponse).
Le mot clé "capture" permet d'ajouter dans des logs HTTP des informations
capturées dans les échanges. La version 1.1.17 supporte uniquement une capture
de cookies client et serveur, ce qui permet dans bien des cas, de reconstituer
la session d'un utilisateur. La syntaxe est la suivante :
capture cookie <préfixe_cookie> len <longueur_capture>
Le premier cookie dont le nom commencera par <préfixe_cookie> sera capturé, et
transmis sous la forme "NOM=valeur", sans toutefois, excéder <longueur_capture>
caractères (64 au maximum). Lorsque le nom du cookie est fixe et connu, on peut
le suffixer du signe "=" pour s'assurer qu'aucun autre cookie ne prendra sa
place dans les logs.
Exemples :
----------
# capture du premier cookie dont le nom commence par "ASPSESSION"
capture cookie ASPSESSION len 32
# capture du premier cookie dont le nom est exactement "vgnvisitor"
capture cookie vgnvisitor= len 32
Dans les logs, le champ précédant la requête HTTP est le cookie positionné par
le serveur, précédé du cookie positionné par le client. Chacun de ces champs est
remplacé par le signe "-" lorsqu'aucun cookie n'est fourni par le client ou le
serveur.
Enfin, l'option 'forwardfor' ajoute l'adresse IP du client dans un champ Enfin, l'option 'forwardfor' ajoute l'adresse IP du client dans un champ
'X-Forwarded-For' de la requête, ce qui permet à un serveur web final de 'X-Forwarded-For' de la requête, ce qui permet à un serveur web final de
connaître l'adresse IP du client initial. connaître l'adresse IP du client initial.
@ -709,6 +785,7 @@ Exemple :
option httplog option httplog
option dontlognull option dontlognull
option forwardfor option forwardfor
capture cookie userid= len 20
4.3) Modification des entêtes HTTP 4.3) Modification des entêtes HTTP
@ -801,7 +878,7 @@ pratiquement transparente pour les applications. Le principe est simple :
- cacher ce cookie à l'application lors des requêtes ultérieures. - cacher ce cookie à l'application lors des requêtes ultérieures.
Exemple : Exemple :
------- ---------
listen application 0.0.0.0:80 listen application 0.0.0.0:80
mode http mode http
cookie SERVERID insert nocache indirect cookie SERVERID insert nocache indirect
@ -809,6 +886,42 @@ Exemple :
server 192.168.1.1:80 cookie server01 check server 192.168.1.1:80 cookie server01 check
server 192.168.1.2:80 cookie server02 check server 192.168.1.2:80 cookie server02 check
4.5) Personalisation des erreurs
--------------------------------
Certaines situations conduisent à retourner une erreur HTTP au client :
- requête invalide ou trop longue => code HTTP 400
- requête mettant trop de temps à venir => code HTTP 408
- requête interdite (bloquée par un reqideny) => code HTTP 403
- erreur interne du proxy => code HTTP 500
- le serveur a retourné une réponse incomplète ou invalide => code HTTP 502
- aucun serveur disponible pour cette requête => code HTTP 503
- le serveur n'a pas répondu dans le temps imparti => code HTTP 504
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" :
errorloc <code_HTTP> <location>
Au lieu de générer une erreur HTTP <code_HTTP> parmi les codes cités ci-dessus,
le proxy génèrera un code de redirection temporaire (HTTP 302) vers l'adresse
d'une page précisée dans <location>. Cette adresse peut être relative au site,
ou absolue. Comme cette réponse est traîtée par le navigateur du client
lui-même, il est indispensable que l'adresse fournie lui soit accessible.
Exemple :
---------
listen application 0.0.0.0:80
errorloc 400 /badrequest.html
errorloc 403 /forbidden.html
errorloc 408 /toolong.html
errorloc 500 http://haproxy.domain.net/bugreport.html
errorloc 502 http://192.168.114.58/error50x.html
errorloc 503 http://192.168.114.58/error50x.html
errorloc 504 http://192.168.114.58/error50x.html
======================= =======================
| Paramétrage système | | Paramétrage système |
======================= =======================

View File

@ -1,5 +1,7 @@
global global
log 127.0.0.1 local0 log 127.0.0.1 local0
log 127.0.0.1 local1 notice
#log loghost local0 info
maxconn 4096 maxconn 4096
chroot /tmp chroot /tmp
uid 11 uid 11
@ -36,6 +38,7 @@ listen appli2-insert 0.0.0.0:10002
cookie SERVERID insert indirect nocache cookie SERVERID insert indirect nocache
server inst1 192.168.114.56:80 cookie server01 check inter 2000 fall 3 server inst1 192.168.114.56:80 cookie server01 check inter 2000 fall 3
server inst2 192.168.114.56:81 cookie server02 check inter 2000 fall 3 server inst2 192.168.114.56:81 cookie server02 check inter 2000 fall 3
capture cookie vgnvisitor= len 32
retries 3 retries 3
redispatch redispatch
maxconn 2000 maxconn 2000
@ -62,3 +65,47 @@ listen appli3-relais 0.0.0.0:10003
clitimeout 50000 clitimeout 50000
srvtimeout 50000 srvtimeout 50000
listen appli4-backup 0.0.0.0:10004
log global
mode http
option httplog
option dontlognull
option httpchk
option persist
balance roundrobin
server inst1 192.168.114.56:80 check inter 2000 fall 3
server inst2 192.168.114.56:81 check inter 2000 fall 3 backup
retries 3
redispatch
maxconn 2000
contimeout 5000
clitimeout 50000
srvtimeout 50000
listen appli5-backup 0.0.0.0:10005
log global
mode http
option httplog
option dontlognull
option httpchk
balance roundrobin
cookie SERVERID insert indirect nocache
server inst1 192.168.114.56:80 cookie server01 check inter 2000 fall 3
server inst2 192.168.114.56:81 cookie server02 check inter 2000 fall 3
server inst3 192.168.114.57:80 backup check inter 2000 fall 3
capture cookie ASPSESSION len 32
retries 3
redispatch
maxconn 2000
contimeout 5000
clitimeout 50000
srvtimeout 50000
reqidel ^Connection: # disable keep-alive
reqadd Connection:\ close
rspidel ^Connection:
rspadd Connection:\ close
rspidel ^Set-cookie:\ IP= # do not let this cookie tell our internal IP address
errorloc 502 http://192.168.114.58/error502.html

535
haproxy.c
View File

@ -11,13 +11,27 @@
* - solaris only : sometimes, an HTTP proxy with only a dispatch address causes * - solaris only : sometimes, an HTTP proxy with only a dispatch address causes
* the proxy to terminate (no core) if the client breaks the connection during * the proxy to terminate (no core) if the client breaks the connection during
* the response. Seen on 1.1.8pre4, but never reproduced. May not be related to * the response. Seen on 1.1.8pre4, but never reproduced. May not be related to
* the snprintf() bug since requests we simple (GET / HTTP/1.0). * the snprintf() bug since requests were simple (GET / HTTP/1.0), but may be
* - cookie in insert+indirect mode sometimes segfaults ! * related to missing setsid() (fixed in 1.1.15)
* - a proxy with an invalid config will prevent the startup even if disabled. * - a proxy with an invalid config will prevent the startup even if disabled.
* - it may be nice to return HTTP 502 when a server returns no header nor data.
* *
* ChangeLog : * ChangeLog :
* *
* 2002/10/18 : 1.1.17
* - add the notion of "backup" servers, which are used only when all other
* servers are down.
* - make Set-Cookie return "" instead of "(null)" when the server has no
* cookie assigned (useful for backup servers).
* - "log" now supports an optionnal level name (info, notice, err ...) above
* which nothing is sent.
* - replaced some strncmp() with memcmp() for better efficiency.
* - added "capture cookie" option which logs client and/or server cookies
* - cleaned up/down messages and dump servers states upon SIGHUP
* - added a redirection feature for errors : "errorloc <errnum> <url>"
* - now we won't insist on connecting to a dead server, even with a cookie,
* unless option "persist" is specified.
* - added HTTP/408 response for client request time-out and HTTP/50[234] for
* server reply time-out or errors.
* 2002/09/01 : 1.1.16 * 2002/09/01 : 1.1.16
* - implement HTTP health checks when option "httpchk" is specified. * - implement HTTP health checks when option "httpchk" is specified.
* 2002/08/07 : 1.1.15 * 2002/08/07 : 1.1.15
@ -177,8 +191,8 @@
#include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv4.h>
#endif #endif
#define HAPROXY_VERSION "1.1.16" #define HAPROXY_VERSION "1.1.17"
#define HAPROXY_DATE "2002/09/01" #define HAPROXY_DATE "2002/10/18"
/* this is for libc5 for example */ /* this is for libc5 for example */
#ifndef TCP_NODELAY #ifndef TCP_NODELAY
@ -198,6 +212,7 @@
// reserved buffer space for header rewriting // reserved buffer space for header rewriting
#define MAXREWRITE 4096 #define MAXREWRITE 4096
#define REQURI_LEN 1024 #define REQURI_LEN 1024
#define CAPTURE_LEN 64
// max # args on a configuration line // max # args on a configuration line
#define MAX_LINE_ARGS 40 #define MAX_LINE_ARGS 40
@ -305,6 +320,7 @@ int strlcpy2(char *dst, const char *src, int size) {
#define sizeof_buffer sizeof(struct buffer) #define sizeof_buffer sizeof(struct buffer)
#define sizeof_fdtab sizeof(struct fdtab) #define sizeof_fdtab sizeof(struct fdtab)
#define sizeof_requri REQURI_LEN #define sizeof_requri REQURI_LEN
#define sizeof_capture CAPTURE_LEN
/* different possible states for the sockets */ /* different possible states for the sockets */
#define FD_STCLOSE 0 #define FD_STCLOSE 0
@ -344,6 +360,7 @@ int strlcpy2(char *dst, const char *src, int size) {
#define PR_O_COOK_NOC 1024 /* add a 'Cache-control' header with the cookie */ #define PR_O_COOK_NOC 1024 /* add a 'Cache-control' header with the cookie */
#define PR_O_COOK_POST 2048 /* don't insert cookies for requests other than a POST */ #define PR_O_COOK_POST 2048 /* don't insert cookies for requests other than a POST */
#define PR_O_HTTP_CHK 4096 /* use HTTP 'OPTIONS' method to check server health */ #define PR_O_HTTP_CHK 4096 /* use HTTP 'OPTIONS' method to check server health */
#define PR_O_PERSIST 8192 /* server persistence stays effective even when server is down */
/* various session flags */ /* various session flags */
@ -385,6 +402,7 @@ int strlcpy2(char *dst, const char *src, int size) {
/* server flags */ /* server flags */
#define SRV_RUNNING 1 #define SRV_RUNNING 1
#define SRV_BACKUP 2
/* what to do when a header matches a regex */ /* what to do when a header matches a regex */
#define ACT_ALLOW 0 /* allow the request */ #define ACT_ALLOW 0 /* allow the request */
@ -485,6 +503,8 @@ struct session {
long t_data; /* delay before the first data byte from the server ... */ long t_data; /* delay before the first data byte from the server ... */
unsigned long t_close; /* total session duration */ unsigned long t_close; /* total session duration */
char *uri; /* first line if log needed, NULL otherwise */ char *uri; /* first line if log needed, NULL otherwise */
char *cli_cookie; /* cookie presented by the client, in capture mode */
char *srv_cookie; /* cookie presented by the server, in capture mode */
int status; /* HTTP status from the server, negative if from proxy */ int status; /* HTTP status from the server, negative if from proxy */
long long bytes; /* number of bytes transferred from the server */ long long bytes; /* number of bytes transferred from the server */
} logs; } logs;
@ -498,6 +518,10 @@ struct proxy {
struct server *srv, *cursrv; /* known servers, current server */ struct server *srv, *cursrv; /* known servers, current server */
int nbservers; /* # of servers */ int nbservers; /* # of servers */
char *cookie_name; /* name of the cookie to look for */ char *cookie_name; /* name of the cookie to look for */
int cookie_len; /* strlen(cookie_len), computed only once */
char *capture_name; /* beginning of the name of the cookie to capture */
int capture_namelen; /* length of the cookie name to match */
int capture_len; /* length of the string to be captured */
int clitimeout; /* client I/O timeout (in milliseconds) */ int clitimeout; /* client I/O timeout (in milliseconds) */
int srvtimeout; /* server I/O timeout (in milliseconds) */ int srvtimeout; /* server I/O timeout (in milliseconds) */
int contimeout; /* connect timeout (in milliseconds) */ int contimeout; /* connect timeout (in milliseconds) */
@ -511,6 +535,7 @@ struct proxy {
struct proxy *next; struct proxy *next;
struct sockaddr_in logsrv1, logsrv2; /* 2 syslog servers */ struct sockaddr_in logsrv1, logsrv2; /* 2 syslog servers */
char logfac1, logfac2; /* log facility for both servers. -1 = disabled */ char logfac1, logfac2; /* log facility for both servers. -1 = disabled */
int loglev1, loglev2; /* log level for each server, 7 by default */
int to_log; /* things to be logged (LW_*) */ int to_log; /* things to be logged (LW_*) */
struct timeval stop_time; /* date to stop listening, when stopping != 0 */ struct timeval stop_time; /* date to stop listening, when stopping != 0 */
int nb_reqadd, nb_rspadd; int nb_reqadd, nb_rspadd;
@ -518,6 +543,22 @@ struct proxy {
struct hdr_exp *rsp_exp; /* regular expressions for response headers */ struct hdr_exp *rsp_exp; /* regular expressions for response headers */
char *req_add[MAX_NEWHDR], *rsp_add[MAX_NEWHDR]; /* headers to be added */ char *req_add[MAX_NEWHDR], *rsp_add[MAX_NEWHDR]; /* headers to be added */
int grace; /* grace time after stop request */ int grace; /* grace time after stop 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;
}; };
/* info about one given fd */ /* info about one given fd */
@ -545,10 +586,13 @@ static struct {
int mode; int mode;
char *chroot; char *chroot;
int logfac1, logfac2; int logfac1, logfac2;
int loglev1, loglev2;
struct sockaddr_in logsrv1, logsrv2; struct sockaddr_in logsrv1, logsrv2;
} global = { } global = {
logfac1 : -1, logfac1 : -1,
logfac2 : -1, logfac2 : -1,
loglev1 : 7, /* max syslog level : debug */
loglev2 : 7,
/* others NULL OK */ /* others NULL OK */
}; };
@ -563,7 +607,8 @@ void **pool_session = NULL,
**pool_buffer = NULL, **pool_buffer = NULL,
**pool_fdtab = NULL, **pool_fdtab = NULL,
**pool_requri = NULL, **pool_requri = NULL,
**pool_task = NULL; **pool_task = NULL,
**pool_capture = NULL;
struct proxy *proxy = NULL; /* list of all existing proxies */ struct proxy *proxy = NULL; /* list of all existing proxies */
struct fdtab *fdtab = NULL; /* array of all the file descriptors */ struct fdtab *fdtab = NULL; /* array of all the file descriptors */
@ -613,6 +658,12 @@ const char *monthname[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
#define MAX_HOSTNAME_LEN 32 #define MAX_HOSTNAME_LEN 32
static char hostname[MAX_HOSTNAME_LEN] = ""; static char hostname[MAX_HOSTNAME_LEN] = "";
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 */
const char *HTTP_400 = const char *HTTP_400 =
"HTTP/1.0 400 Bad request\r\n" "HTTP/1.0 400 Bad request\r\n"
"Cache-Control: no-cache\r\n" "Cache-Control: no-cache\r\n"
@ -627,6 +678,13 @@ const char *HTTP_403 =
"\r\n" "\r\n"
"<html><body><h1>403 Forbidden</h1>\nRequest forbidden by administrative rules.\n</body></html>\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"
"\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 = const char *HTTP_500 =
"HTTP/1.0 500 Server Error\r\n" "HTTP/1.0 500 Server Error\r\n"
"Cache-Control: no-cache\r\n" "Cache-Control: no-cache\r\n"
@ -635,11 +693,25 @@ const char *HTTP_500 =
"<html><body><h1>500 Server Error</h1>\nAn internal server error occured.\n</body></html>\n"; "<html><body><h1>500 Server Error</h1>\nAn internal server error occured.\n</body></html>\n";
const char *HTTP_502 = const char *HTTP_502 =
"HTTP/1.0 502 Proxy Error\r\n" "HTTP/1.0 502 Bad Gateway\r\n"
"Cache-Control: no-cache\r\n" "Cache-Control: no-cache\r\n"
"Connection: close\r\n" "Connection: close\r\n"
"\r\n" "\r\n"
"<html><body><h1>502 Proxy Error</h1>\nNo server is available to handle this request.\n</body></html>\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"
"\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"
"\r\n"
"<html><body><h1>504 Gateway Time-out</h1>\nThe server didn't respond in time.\n</body></html>\n";
/*********************************************************************/ /*********************************************************************/
/* statistics ******************************************************/ /* statistics ******************************************************/
@ -826,7 +898,7 @@ void send_log(struct proxy *p, int level, char *message, ...) {
int fac_level; int fac_level;
int hdr_len, data_len; int hdr_len, data_len;
struct sockaddr_in *sa[2]; struct sockaddr_in *sa[2];
int facilities[2]; int facilities[2], loglevel[2];
int nbloggers = 0; int nbloggers = 0;
char *log_ptr; char *log_ptr;
@ -870,27 +942,35 @@ void send_log(struct proxy *p, int level, char *message, ...) {
if (global.logfac1 >= 0) { if (global.logfac1 >= 0) {
sa[nbloggers] = &global.logsrv1; sa[nbloggers] = &global.logsrv1;
facilities[nbloggers] = global.logfac1; facilities[nbloggers] = global.logfac1;
loglevel[nbloggers] = global.loglev1;
nbloggers++; nbloggers++;
} }
if (global.logfac2 >= 0) { if (global.logfac2 >= 0) {
sa[nbloggers] = &global.logsrv2; sa[nbloggers] = &global.logsrv2;
facilities[nbloggers] = global.logfac2; facilities[nbloggers] = global.logfac2;
loglevel[nbloggers] = global.loglev2;
nbloggers++; nbloggers++;
} }
} else { } else {
if (p->logfac1 >= 0) { if (p->logfac1 >= 0) {
sa[nbloggers] = &p->logsrv1; sa[nbloggers] = &p->logsrv1;
facilities[nbloggers] = p->logfac1; facilities[nbloggers] = p->logfac1;
loglevel[nbloggers] = p->loglev1;
nbloggers++; nbloggers++;
} }
if (p->logfac2 >= 0) { if (p->logfac2 >= 0) {
sa[nbloggers] = &p->logsrv2; sa[nbloggers] = &p->logsrv2;
facilities[nbloggers] = p->logfac2; facilities[nbloggers] = p->logfac2;
loglevel[nbloggers] = p->loglev2;
nbloggers++; nbloggers++;
} }
} }
while (nbloggers-- > 0) { while (nbloggers-- > 0) {
/* we can filter the level of the messages that are sent to each logger */
if (level > loglevel[nbloggers])
continue;
/* For each target, we may have a different facility. /* For each target, we may have a different facility.
* We can also have a different log level for each message. * We can also have a different log level for each message.
* This induces variations in the message header length. * This induces variations in the message header length.
@ -1330,11 +1410,38 @@ static inline void session_free(struct session *s) {
pool_free(buffer, s->rep); pool_free(buffer, s->rep);
if (s->logs.uri) if (s->logs.uri)
pool_free(requri, s->logs.uri); pool_free(requri, s->logs.uri);
if (s->logs.cli_cookie)
pool_free(capture, s->logs.cli_cookie);
if (s->logs.srv_cookie)
pool_free(capture, s->logs.srv_cookie);
pool_free(session, s); pool_free(session, s);
} }
/*
* This function tries to find a running server for the proxy <px>. A first
* pass looks for active servers, and if none is found, a second pass also
* looks for backup servers.
* If no valid server is found, NULL is returned and px->cursrv is left undefined.
*/
static inline struct server *find_server(struct proxy *px) {
struct server *srv = px->cursrv;
int ignore_backup = 1;
do {
do {
if (srv == NULL)
srv = px->srv;
if (srv->state & SRV_RUNNING
&& !((srv->state & SRV_BACKUP) && ignore_backup))
return srv;
srv = srv->next;
} while (srv != px->cursrv);
} while (ignore_backup--);
return NULL;
}
/* /*
* This function initiates a connection to the current server (s->srv) if (s->direct) * This function initiates a connection to the current server (s->srv) if (s->direct)
* is set, or to the dispatch server if (s->direct) is 0. It returns 0 if * is set, or to the dispatch server if (s->direct) is 0. It returns 0 if
@ -1351,22 +1458,16 @@ int connect_server(struct session *s) {
} }
else if (s->proxy->options & PR_O_BALANCE) { else if (s->proxy->options & PR_O_BALANCE) {
if (s->proxy->options & PR_O_BALANCE_RR) { if (s->proxy->options & PR_O_BALANCE_RR) {
int retry = s->proxy->nbservers; struct server *srv;
while (retry) {
if (s->proxy->cursrv == NULL)
s->proxy->cursrv = s->proxy->srv;
if (s->proxy->cursrv->state & SRV_RUNNING)
break;
s->proxy->cursrv = s->proxy->cursrv->next;
retry--;
}
if (retry == 0) /* no server left */ srv = find_server(s->proxy);
if (srv == NULL) /* no server left */
return -1; return -1;
s->srv = s->proxy->cursrv; s->srv_addr = srv->addr;
s->srv_addr = s->srv->addr; s->srv = srv;
s->proxy->cursrv = s->proxy->cursrv->next; s->proxy->cursrv = srv->next;
} }
else /* unknown balancing algorithm */ else /* unknown balancing algorithm */
return -1; return -1;
@ -1798,7 +1899,7 @@ int event_srv_write(int fd) {
* and the request is cleared so that no server connection can be initiated. * 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 ...). * 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 reply buffer must be empty before this. * 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, int len, const char *msg) {
FD_CLR(s->cli_fd, StaticReadEvent); FD_CLR(s->cli_fd, StaticReadEvent);
@ -1808,6 +1909,7 @@ void client_retnclose(struct session *s, int len, const char *msg) {
s->cli_state = CL_STSHUTR; s->cli_state = CL_STSHUTR;
strcpy(s->rep->data, msg); strcpy(s->rep->data, msg);
s->rep->l = len; s->rep->l = len;
s->rep->r = s->rep->h = s->rep->lr = s->rep->w = s->rep->data;
s->rep->r += len; s->rep->r += len;
s->req->l = 0; s->req->l = 0;
} }
@ -1815,11 +1917,12 @@ void client_retnclose(struct session *s, int len, const char *msg) {
/* /*
* returns a message into the rep buffer, and flushes the req buffer. * returns a message into the rep buffer, and flushes the req buffer.
* The reply buffer must be empty before this. * The reply buffer doesn't need to be empty before this.
*/ */
void client_return(struct session *s, int len, const char *msg) { void client_return(struct session *s, int len, const char *msg) {
strcpy(s->rep->data, msg); strcpy(s->rep->data, msg);
s->rep->l = len; s->rep->l = len;
s->rep->r = s->rep->h = s->rep->lr = s->rep->w = s->rep->data;
s->rep->r += len; s->rep->r += len;
s->req->l = 0; s->req->l = 0;
} }
@ -1855,7 +1958,7 @@ void sess_log(struct session *s) {
if (p->to_log & LW_DATE) { if (p->to_log & LW_DATE) {
struct tm *tm = localtime(&s->logs.tv_accept.tv_sec); struct tm *tm = localtime(&s->logs.tv_accept.tv_sec);
send_log(p, LOG_INFO, "%d.%d.%d.%d:%d [%02d/%s/%04d:%02d:%02d:%02d] %s %s %d/%d/%d/%d %d %lld \"%s\"\n", send_log(p, LOG_INFO, "%d.%d.%d.%d:%d [%02d/%s/%04d:%02d:%02d:%02d] %s %s %d/%d/%d/%d %d %lld %s %s \"%s\"\n",
pn[0], pn[1], pn[2], pn[3], ntohs(s->cli_addr.sin_port), pn[0], pn[1], pn[2], pn[3], ntohs(s->cli_addr.sin_port),
tm->tm_mday, monthname[tm->tm_mon], tm->tm_year+1900, tm->tm_mday, monthname[tm->tm_mon], tm->tm_year+1900,
tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_hour, tm->tm_min, tm->tm_sec,
@ -1865,10 +1968,12 @@ void sess_log(struct session *s) {
(s->logs.t_data >= 0) ? s->logs.t_data - s->logs.t_connect : -1, (s->logs.t_data >= 0) ? s->logs.t_data - s->logs.t_connect : -1,
s->logs.t_close, s->logs.t_close,
s->logs.status, s->logs.bytes, s->logs.status, s->logs.bytes,
s->logs.cli_cookie ? s->logs.cli_cookie : "-",
s->logs.srv_cookie ? s->logs.srv_cookie : "-",
uri); uri);
} }
else { else {
send_log(p, LOG_INFO, "%d.%d.%d.%d:%d %s %s %d/%d/%d/%d %d %lld \"%s\"\n", send_log(p, LOG_INFO, "%d.%d.%d.%d:%d %s %s %d/%d/%d/%d %d %lld %s %s \"%s\"\n",
pn[0], pn[1], pn[2], pn[3], ntohs(s->cli_addr.sin_port), pn[0], pn[1], pn[2], pn[3], ntohs(s->cli_addr.sin_port),
pxid, srv, pxid, srv,
s->logs.t_request, s->logs.t_request,
@ -1876,6 +1981,8 @@ void sess_log(struct session *s) {
(s->logs.t_data >= 0) ? s->logs.t_data - s->logs.t_connect : -1, (s->logs.t_data >= 0) ? s->logs.t_data - s->logs.t_connect : -1,
s->logs.t_close, s->logs.t_close,
s->logs.status, s->logs.bytes, s->logs.status, s->logs.bytes,
s->logs.cli_cookie ? s->logs.cli_cookie : "-",
s->logs.srv_cookie ? s->logs.srv_cookie : "-",
uri); uri);
} }
@ -1962,6 +2069,8 @@ int event_accept(int fd) {
s->logs.t_data = -1; s->logs.t_data = -1;
s->logs.t_close = 0; s->logs.t_close = 0;
s->logs.uri = NULL; s->logs.uri = NULL;
s->logs.cli_cookie = NULL;
s->logs.srv_cookie = NULL;
s->logs.status = -1; s->logs.status = -1;
s->logs.bytes = 0; s->logs.bytes = 0;
@ -2173,7 +2282,7 @@ int buffer_replace(struct buffer *b, char *pos, char *end, char *str) {
return delta; return delta;
} }
/* same except that the string len is given, which allows str to be NULL if /* same except that the string length is given, which allows str to be NULL if
* len is 0. * len is 0.
*/ */
int buffer_replace2(struct buffer *b, char *pos, char *end, char *str, int len) { int buffer_replace2(struct buffer *b, char *pos, char *end, char *str, int len) {
@ -2280,8 +2389,7 @@ int process_cli(struct session *t) {
if (t->flags & SN_CLDENY) { if (t->flags & SN_CLDENY) {
/* no need to go further */ /* no need to go further */
t->logs.status = 403; t->logs.status = 403;
if (t->proxy->mode == PR_MODE_HTTP) client_retnclose(t, t->proxy->errmsg.len403, t->proxy->errmsg.msg403);
client_retnclose(t, strlen(HTTP_403), HTTP_403);
return 1; return 1;
} }
@ -2338,16 +2446,14 @@ int process_cli(struct session *t) {
* req->r = end of data (not used at this stage) * req->r = end of data (not used at this stage)
*/ */
if (t->logs.logwait & LW_REQ && if (t->logs.logwait & LW_REQ) {
t->proxy->mode & PR_MODE_HTTP) {
/* we have a complete HTTP request that we must log */ /* we have a complete HTTP request that we must log */
int urilen; int urilen;
if ((t->logs.uri = pool_alloc(requri)) == NULL) { if ((t->logs.uri = pool_alloc(requri)) == NULL) {
Alert("HTTP logging : out of memory.\n"); Alert("HTTP logging : out of memory.\n");
t->logs.status = 500; t->logs.status = 500;
if (t->proxy->mode == PR_MODE_HTTP) client_retnclose(t, t->proxy->errmsg.len500, t->proxy->errmsg.msg500);
client_retnclose(t, strlen(HTTP_500), HTTP_500);
return 1; return 1;
} }
@ -2422,9 +2528,9 @@ int process_cli(struct session *t) {
* remove it. If no application cookie persists in the header, we * remove it. If no application cookie persists in the header, we
* *MUST* delete it * *MUST* delete it
*/ */
if (!delete_header && (t->proxy->cookie_name != NULL) if (!delete_header && (t->proxy->cookie_name != NULL || t->proxy->capture_name != NULL)
&& !(t->flags & SN_CLDENY) && (ptr >= req->h + 8) && !(t->flags & SN_CLDENY) && (ptr >= req->h + 8)
&& (strncmp(req->h, "Cookie: ", 8) == 0)) { && (memcmp(req->h, "Cookie: ", 8) == 0)) {
char *p1, *p2, *p3, *p4; char *p1, *p2, *p3, *p4;
char *del_colon, *del_cookie, *colon; char *del_colon, *del_cookie, *colon;
int app_cookies; int app_cookies;
@ -2472,42 +2578,63 @@ int process_cli(struct session *t) {
if (*p1 == '$') { if (*p1 == '$') {
/* skip this one */ /* skip this one */
} }
else if ((p2 - p1 == strlen(t->proxy->cookie_name)) && else {
(memcmp(p1, t->proxy->cookie_name, p2 - p1) == 0)) { /* first, let's see if we want to capture it */
/* Cool... it's the right one */ if (t->proxy->capture_name != NULL &&
struct server *srv = t->proxy->srv; t->logs.cli_cookie == NULL &&
(p4 - p1 >= t->proxy->capture_namelen) &&
memcmp(p1, t->proxy->capture_name, t->proxy->capture_namelen) == 0) {
int log_len = p4 - p1;
while (srv && if ((t->logs.cli_cookie = pool_alloc(capture)) == NULL) {
((srv->cklen != p4 - p3) || memcmp(p3, srv->cookie, p4 - p3))) { Alert("HTTP logging : out of memory.\n");
srv = srv->next; }
if (log_len > t->proxy->capture_len)
log_len = t->proxy->capture_len;
memcpy(t->logs.cli_cookie, p1, log_len);
t->logs.cli_cookie[log_len] = 0;
} }
if (srv) { /* we found the server */ if ((p2 - p1 == t->proxy->cookie_len) && (t->proxy->cookie_name != NULL) &&
t->flags |= SN_DIRECT; (memcmp(p1, t->proxy->cookie_name, p2 - p1) == 0)) {
t->srv = srv; /* Cool... it's the right one */
} struct server *srv = t->proxy->srv;
/* if this cookie was set in insert+indirect mode, then it's better that the
* server never sees it. while (srv &&
*/ ((srv->cklen != p4 - p3) || memcmp(p3, srv->cookie, p4 - p3))) {
if (del_cookie == NULL && srv = srv->next;
(t->proxy->options & (PR_O_COOK_INS | PR_O_COOK_IND)) == (PR_O_COOK_INS | PR_O_COOK_IND)) { }
if (srv &&
(srv->state & SRV_RUNNING || t->proxy->options & PR_O_PERSIST)) {
/* we found the server and it's usable */
t->flags |= SN_DIRECT;
t->srv = srv;
}
/* if this cookie was set in insert+indirect mode, then it's better that the
* server never sees it.
*/
if (del_cookie == NULL &&
(t->proxy->options & (PR_O_COOK_INS | PR_O_COOK_IND)) == (PR_O_COOK_INS | PR_O_COOK_IND)) {
del_cookie = p1; del_cookie = p1;
del_colon = colon; del_colon = colon;
}
} }
} else {
else { /* now we know that we must keep this cookie since it's
/* now we know that we must keep this cookie since it's * not ours. But if we wanted to delete our cookie
* not ours. But if we wanted to delete our cookie * earlier, we cannot remove the complete header, but we
* earlier, we cannot remove the complete header, but we * can remove the previous block itself.
* can remove the previous block itself. */
*/ app_cookies++;
app_cookies++;
if (del_cookie != NULL) {
if (del_cookie != NULL) { buffer_replace2(req, del_cookie, p1, NULL, 0);
buffer_replace2(req, del_cookie, p1, NULL, 0); p4 -= (p1 - del_cookie);
p4 -= (p1 - del_cookie); ptr -= (p1 - del_cookie);
ptr -= (p1 - del_cookie); del_cookie = del_colon = NULL;
del_cookie = del_colon = NULL; }
} }
} }
@ -2565,14 +2692,11 @@ int process_cli(struct session *t) {
*/ */
if (req->l >= req->rlim - req->data) { if (req->l >= req->rlim - req->data) {
t->logs.status = 400; t->logs.status = 400;
if (t->proxy->mode == PR_MODE_HTTP) client_retnclose(t, t->proxy->errmsg.len400, t->proxy->errmsg.msg400);
client_retnclose(t, strlen(HTTP_400), HTTP_400);
return 1; return 1;
} }
else if (t->res_cr == RES_ERROR || t->res_cr == RES_NULL else if (t->res_cr == RES_ERROR || t->res_cr == RES_NULL) {
|| tv_cmp2_ms(&t->crexpire, &now) <= 0) { /* read error, or last read : give up.
/* read timeout, read error, or last read : give up.
* since we are in header mode, if there's no space left for headers, we * since we are in header mode, if there's no space left for headers, we
* won't be able to free more later, so the session will never terminate. * won't be able to free more later, so the session will never terminate.
*/ */
@ -2581,6 +2705,14 @@ int process_cli(struct session *t) {
t->cli_state = CL_STCLOSE; t->cli_state = CL_STCLOSE;
return 1; return 1;
} }
else if (tv_cmp2_ms(&t->crexpire, &now) <= 0) {
/* read timeout : give up with an error message.
*/
t->logs.status = 408;
client_retnclose(t, t->proxy->errmsg.len408, t->proxy->errmsg.msg408);
return 1;
}
return t->cli_state != CL_STHEADERS; return t->cli_state != CL_STHEADERS;
} }
@ -2766,9 +2898,9 @@ int process_srv(struct session *t) {
/* if conn_retries < 0 or other error, let's abort */ /* if conn_retries < 0 or other error, let's abort */
tv_eternity(&t->cnexpire); tv_eternity(&t->cnexpire);
t->srv_state = SV_STCLOSE; t->srv_state = SV_STCLOSE;
t->logs.status = 502; t->logs.status = 503;
if (t->proxy->mode == PR_MODE_HTTP) if (t->proxy->mode == PR_MODE_HTTP)
client_return(t, strlen(HTTP_502), HTTP_502); client_return(t, t->proxy->errmsg.len503, t->proxy->errmsg.msg503);
} }
} }
return 1; return 1;
@ -2797,9 +2929,9 @@ int process_srv(struct session *t) {
/* if conn_retries < 0 or other error, let's abort */ /* if conn_retries < 0 or other error, let's abort */
tv_eternity(&t->cnexpire); tv_eternity(&t->cnexpire);
t->srv_state = SV_STCLOSE; t->srv_state = SV_STCLOSE;
t->logs.status = 502; t->logs.status = 503;
if (t->proxy->mode == PR_MODE_HTTP) if (t->proxy->mode == PR_MODE_HTTP)
client_return(t, strlen(HTTP_502), HTTP_502); client_return(t, t->proxy->errmsg.len503, t->proxy->errmsg.msg503);
return 1; return 1;
} }
else { /* no error or write 0 */ else { /* no error or write 0 */
@ -2855,7 +2987,8 @@ int process_srv(struct session *t) {
* requests and this one isn't. * requests and this one isn't.
*/ */
len = sprintf(trash, "Set-Cookie: %s=%s; path=/\r\n", len = sprintf(trash, "Set-Cookie: %s=%s; path=/\r\n",
t->proxy->cookie_name, t->srv->cookie); t->proxy->cookie_name,
t->srv->cookie ? t->srv->cookie : "");
/* Here, we will tell an eventual cache on the client side that we don't /* Here, we will tell an eventual cache on the client side that we don't
* want it to cache this reply because HTTP/1.0 caches also cache cookies ! * want it to cache this reply because HTTP/1.0 caches also cache cookies !
@ -2963,9 +3096,10 @@ int process_srv(struct session *t) {
} }
/* check for server cookies */ /* check for server cookies */
if (!delete_header && (t->proxy->options & PR_O_COOK_ANY) if (!delete_header /*&& (t->proxy->options & PR_O_COOK_ANY)*/
&& (t->proxy->cookie_name != NULL) && (ptr >= rep->h + 12) && (t->proxy->cookie_name != NULL || t->proxy->capture_name != NULL)
&& (strncmp(rep->h, "Set-Cookie: ", 12) == 0)) { && (ptr >= rep->h + 12)
&& (memcmp(rep->h, "Set-Cookie: ", 12) == 0)) {
char *p1, *p2, *p3, *p4; char *p1, *p2, *p3, *p4;
p1 = rep->h + 12; /* first char after 'Set-Cookie: ' */ p1 = rep->h + 12; /* first char after 'Set-Cookie: ' */
@ -2998,9 +3132,26 @@ int process_srv(struct session *t) {
* and its value between p3 and p4. * and its value between p3 and p4.
* we can process it. * we can process it.
*/ */
if ((p2 - p1 == strlen(t->proxy->cookie_name)) && /* first, let's see if we want to capture it */
(strncmp(p1, t->proxy->cookie_name, p2 - p1) == 0)) { if (t->proxy->capture_name != NULL &&
t->logs.srv_cookie == NULL &&
(p4 - p1 >= t->proxy->capture_namelen) &&
memcmp(p1, t->proxy->capture_name, t->proxy->capture_namelen) == 0) {
int log_len = p4 - p1;
if ((t->logs.srv_cookie = pool_alloc(capture)) == NULL) {
Alert("HTTP logging : out of memory.\n");
}
if (log_len > t->proxy->capture_len)
log_len = t->proxy->capture_len;
memcpy(t->logs.srv_cookie, p1, log_len);
t->logs.srv_cookie[log_len] = 0;
}
if ((p2 - p1 == t->proxy->cookie_len) && (t->proxy->cookie_name != NULL) &&
(memcmp(p1, t->proxy->cookie_name, p2 - p1) == 0)) {
/* Cool... it's the right one */ /* Cool... it's the right one */
/* If the cookie is in insert mode on a known server, we'll delete /* If the cookie is in insert mode on a known server, we'll delete
@ -3052,27 +3203,37 @@ int process_srv(struct session *t) {
tv_eternity(&t->srexpire); tv_eternity(&t->srexpire);
} }
/* read or write error */ /* read error, write error */
if (t->res_sw == RES_ERROR || t->res_sr == RES_ERROR) { if (t->res_sw == RES_ERROR || t->res_sr == RES_ERROR) {
tv_eternity(&t->srexpire); tv_eternity(&t->srexpire);
tv_eternity(&t->swexpire); tv_eternity(&t->swexpire);
fd_delete(t->srv_fd); fd_delete(t->srv_fd);
t->srv_state = SV_STCLOSE; t->srv_state = SV_STCLOSE;
t->logs.status = 502; t->logs.status = 502;
client_return(t, strlen(HTTP_502), HTTP_502); client_return(t, t->proxy->errmsg.len502, t->proxy->errmsg.msg502);
return 1; return 1;
} }
/* read timeout, last read, or end of client write /* end of client write or end of server read.
* since we are in header mode, if there's no space left for headers, we * since we are in header mode, if there's no space left for headers, we
* won't be able to free more later, so the session will never terminate. * won't be able to free more later, so the session will never terminate.
*/ */
else if (t->res_sr == RES_NULL || c == CL_STSHUTW || c == CL_STCLOSE else if (t->res_sr == RES_NULL || c == CL_STSHUTW || c == CL_STCLOSE || rep->l >= rep->rlim - rep->data) {
|| rep->l >= rep->rlim - rep->data || tv_cmp2_ms(&t->srexpire, &now) <= 0) {
FD_CLR(t->srv_fd, StaticReadEvent); FD_CLR(t->srv_fd, StaticReadEvent);
tv_eternity(&t->srexpire); tv_eternity(&t->srexpire);
shutdown(t->srv_fd, SHUT_RD); shutdown(t->srv_fd, SHUT_RD);
t->srv_state = SV_STSHUTR; t->srv_state = SV_STSHUTR;
return 1; return 1;
}
/* read timeout : return a 504 to the client.
*/
else if (FD_ISSET(t->srv_fd, StaticReadEvent) && tv_cmp2_ms(&t->srexpire, &now) <= 0) {
tv_eternity(&t->srexpire);
tv_eternity(&t->swexpire);
fd_delete(t->srv_fd);
t->srv_state = SV_STCLOSE;
t->logs.status = 504;
client_return(t, t->proxy->errmsg.len504, t->proxy->errmsg.msg504);
return 1;
} }
/* write timeout, or last client read and buffer empty */ /* write timeout, or last client read and buffer empty */
@ -3081,7 +3242,7 @@ int process_srv(struct session *t) {
* some work to do on the headers. * some work to do on the headers.
*/ */
else if (((/*c == CL_STSHUTR ||*/ c == CL_STCLOSE) && (req->l == 0)) || else if (((/*c == CL_STSHUTR ||*/ c == CL_STCLOSE) && (req->l == 0)) ||
(tv_cmp2_ms(&t->swexpire, &now) <= 0)) { (FD_ISSET(t->srv_fd, StaticWriteEvent) && tv_cmp2_ms(&t->swexpire, &now) <= 0)) {
FD_CLR(t->srv_fd, StaticWriteEvent); FD_CLR(t->srv_fd, StaticWriteEvent);
tv_eternity(&t->swexpire); tv_eternity(&t->swexpire);
shutdown(t->srv_fd, SHUT_WR); shutdown(t->srv_fd, SHUT_WR);
@ -3366,7 +3527,7 @@ int process_chk(struct task *t) {
else { else {
if (s->health == s->rise) { if (s->health == s->rise) {
if (!(global.mode & MODE_QUIET)) if (!(global.mode & MODE_QUIET))
Warning("server %s DOWN.\n", s->id); Warning("server %s/%s DOWN.\n", s->proxy->id, s->id);
send_log(s->proxy, LOG_ALERT, "Server %s/%s is DOWN.\n", s->proxy->id, s->id); send_log(s->proxy, LOG_ALERT, "Server %s/%s is DOWN.\n", s->proxy->id, s->id);
} }
@ -3388,7 +3549,7 @@ int process_chk(struct task *t) {
if (s->health >= s->rise) { if (s->health >= s->rise) {
if (s->health == s->rise) { if (s->health == s->rise) {
if (!(global.mode & MODE_QUIET)) if (!(global.mode & MODE_QUIET))
Warning("server %s UP.\n", s->id); Warning("server %s/%s UP.\n", s->proxy->id, s->id);
send_log(s->proxy, LOG_NOTICE, "Server %s/%s is UP.\n", s->proxy->id, s->id); send_log(s->proxy, LOG_NOTICE, "Server %s/%s is UP.\n", s->proxy->id, s->id);
} }
@ -3408,7 +3569,7 @@ int process_chk(struct task *t) {
else { else {
if (s->health == s->rise) { if (s->health == s->rise) {
if (!(global.mode & MODE_QUIET)) if (!(global.mode & MODE_QUIET))
Warning("server %s DOWN.\n", s->id); Warning("server %s/%s DOWN.\n", s->proxy->id, s->id);
send_log(s->proxy, LOG_ALERT, "Server %s/%s is DOWN.\n", s->proxy->id, s->id); send_log(s->proxy, LOG_ALERT, "Server %s/%s is DOWN.\n", s->proxy->id, s->id);
} }
@ -3714,6 +3875,33 @@ void sig_soft_stop(int sig) {
} }
/*
* this function dumps every server's state when the process receives SIGHUP.
*/
void sig_dump_state(int sig) {
struct proxy *p = proxy;
Warning("SIGHUP received, dumping servers states.\n");
while (p) {
struct server *s = p->srv;
send_log(p, LOG_NOTICE, "SIGUP received, dumping servers states.\n");
while (s) {
if (s->state & SRV_RUNNING) {
Warning("SIGHUP: server %s/%s is UP.\n", p->id, s->id);
send_log(p, LOG_NOTICE, "SIGUP: server %s/%s is UP.\n", p->id, s->id);
}
else {
Warning("SIGHUP: server %s/%s is DOWN.\n", p->id, s->id);
send_log(p, LOG_NOTICE, "SIGHUP: server %s/%s is DOWN.\n", p->id, s->id);
}
s = s->next;
}
p = p->next;
}
signal(sig, sig_dump_state);
}
void dump(int sig) { void dump(int sig) {
struct task *t, *tnext; struct task *t, *tnext;
struct session *s; struct session *s;
@ -3830,7 +4018,7 @@ int cfg_parse_global(char *file, int linenum, char **args) {
} }
else if (!strcmp(args[0], "log")) { /* syslog server address */ else if (!strcmp(args[0], "log")) { /* syslog server address */
struct sockaddr_in *sa; struct sockaddr_in *sa;
int facility; int facility, level;
if (*(args[1]) == 0 || *(args[2]) == 0) { if (*(args[1]) == 0 || *(args[2]) == 0) {
Alert("parsing [%s:%d] : <log> expects <address> and <facility> as arguments.\n", file, linenum); Alert("parsing [%s:%d] : <log> expects <address> and <facility> as arguments.\n", file, linenum);
@ -3845,7 +4033,17 @@ int cfg_parse_global(char *file, int linenum, char **args) {
Alert("parsing [%s:%d] : unknown log facility <%s>\n", file, linenum, args[2]); Alert("parsing [%s:%d] : unknown log facility <%s>\n", file, linenum, args[2]);
exit(1); exit(1);
} }
level = 7; /* max syslog level = debug */
if (*(args[3])) {
while (level >= 0 && strcmp(log_levels[level], args[3]))
level--;
if (level < 0) {
Alert("parsing [%s:%d] : unknown optionnal log level <%s>\n", file, linenum, args[3]);
exit(1);
}
}
sa = str2sa(args[1]); sa = str2sa(args[1]);
if (!sa->sin_port) if (!sa->sin_port)
sa->sin_port = htons(SYSLOG_PORT); sa->sin_port = htons(SYSLOG_PORT);
@ -3853,10 +4051,12 @@ int cfg_parse_global(char *file, int linenum, char **args) {
if (global.logfac1 == -1) { if (global.logfac1 == -1) {
global.logsrv1 = *sa; global.logsrv1 = *sa;
global.logfac1 = facility; global.logfac1 = facility;
global.loglev1 = level;
} }
else if (global.logfac2 == -1) { else if (global.logfac2 == -1) {
global.logsrv2 = *sa; global.logsrv2 = *sa;
global.logfac2 = facility; global.logfac2 = facility;
global.loglev2 = level;
} }
else { else {
Alert("parsing [%s:%d] : too many syslog servers\n", file, linenum); Alert("parsing [%s:%d] : too many syslog servers\n", file, linenum);
@ -3936,6 +4136,7 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
return -1; return -1;
} }
curproxy->cookie_name = strdup(args[1]); curproxy->cookie_name = strdup(args[1]);
curproxy->cookie_len = strlen(curproxy->cookie_name);
cur_arg = 2; cur_arg = 2;
while (*(args[cur_arg])) { while (*(args[cur_arg])) {
@ -3967,6 +4168,27 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
return -1; return -1;
} }
} }
else if (!strcmp(args[0], "capture")) { /* name of a cookie to capture */
if (curproxy->capture_name != NULL) {
Alert("parsing [%s:%d] : capture already specified. Continuing.\n",
file, linenum);
return 0;
}
if (*(args[4]) == 0) {
Alert("parsing [%s:%d] : <capture> expects 'cookie' <cookie_name> 'len' <len>.\n",
file, linenum);
return -1;
}
curproxy->capture_name = strdup(args[2]);
curproxy->capture_namelen = strlen(curproxy->capture_name);
curproxy->capture_len = atol(args[4]);
if (curproxy->capture_len >= CAPTURE_LEN) {
Warning("parsing [%s:%d] : truncating capture length to %d bytes.\n",
file, linenum, CAPTURE_LEN - 1);
curproxy->capture_len = CAPTURE_LEN - 1;
}
}
else if (!strcmp(args[0], "contimeout")) { /* connect timeout */ else if (!strcmp(args[0], "contimeout")) { /* connect timeout */
if (curproxy->contimeout != 0) { if (curproxy->contimeout != 0) {
Alert("parsing [%s:%d] : contimeout already specified. Continuing.\n", file, linenum); Alert("parsing [%s:%d] : contimeout already specified. Continuing.\n", file, linenum);
@ -4043,6 +4265,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
/* use HTTP request to check servers' health */ /* use HTTP request to check servers' health */
curproxy->options |= PR_O_HTTP_CHK; curproxy->options |= PR_O_HTTP_CHK;
} }
else if (!strcmp(args[1], "persist")) {
/* persist on using the server specified by the cookie, even when it's down */
curproxy->options |= PR_O_PERSIST;
}
else { else {
Alert("parsing [%s:%d] : unknown option <%s>.\n", file, linenum, args[1]); Alert("parsing [%s:%d] : unknown option <%s>.\n", file, linenum, args[1]);
return -1; return -1;
@ -4136,6 +4362,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
newsrv->inter = atol(args[cur_arg + 1]); newsrv->inter = atol(args[cur_arg + 1]);
cur_arg += 2; cur_arg += 2;
} }
else if (!strcmp(args[cur_arg], "backup")) {
newsrv->state |= SRV_BACKUP;
cur_arg ++;
}
else if (!strcmp(args[cur_arg], "check")) { else if (!strcmp(args[cur_arg], "check")) {
struct task *t; struct task *t;
@ -4173,10 +4403,14 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
if (*(args[1]) && *(args[2]) == 0 && !strcmp(args[1], "global")) { if (*(args[1]) && *(args[2]) == 0 && !strcmp(args[1], "global")) {
curproxy->logfac1 = global.logfac1; curproxy->logfac1 = global.logfac1;
curproxy->logsrv1 = global.logsrv1; curproxy->logsrv1 = global.logsrv1;
curproxy->loglev1 = global.loglev1;
curproxy->logfac2 = global.logfac2; curproxy->logfac2 = global.logfac2;
curproxy->logsrv2 = global.logsrv2; curproxy->logsrv2 = global.logsrv2;
curproxy->loglev2 = global.loglev2;
} }
else if (*(args[1]) && *(args[2])) { else if (*(args[1]) && *(args[2])) {
int level;
for (facility = 0; facility < NB_LOG_FACILITIES; facility++) for (facility = 0; facility < NB_LOG_FACILITIES; facility++)
if (!strcmp(log_facilities[facility], args[2])) if (!strcmp(log_facilities[facility], args[2]))
break; break;
@ -4186,6 +4420,16 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
exit(1); exit(1);
} }
level = 7; /* max syslog level = debug */
if (*(args[3])) {
while (level >= 0 && strcmp(log_levels[level], args[3]))
level--;
if (level < 0) {
Alert("parsing [%s:%d] : unknown optionnal log level <%s>\n", file, linenum, args[3]);
exit(1);
}
}
sa = str2sa(args[1]); sa = str2sa(args[1]);
if (!sa->sin_port) if (!sa->sin_port)
sa->sin_port = htons(SYSLOG_PORT); sa->sin_port = htons(SYSLOG_PORT);
@ -4193,10 +4437,12 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
if (curproxy->logfac1 == -1) { if (curproxy->logfac1 == -1) {
curproxy->logsrv1 = *sa; curproxy->logsrv1 = *sa;
curproxy->logfac1 = facility; curproxy->logfac1 = facility;
curproxy->loglev1 = level;
} }
else if (curproxy->logfac2 == -1) { else if (curproxy->logfac2 == -1) {
curproxy->logsrv2 = *sa; curproxy->logsrv2 = *sa;
curproxy->logfac2 = facility; curproxy->logfac2 = facility;
curproxy->loglev2 = level;
} }
else { else {
Alert("parsing [%s:%d] : too many syslog servers\n", file, linenum); Alert("parsing [%s:%d] : too many syslog servers\n", file, linenum);
@ -4441,6 +4687,80 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
curproxy->rsp_add[curproxy->nb_rspadd++] = strdup(args[1]); curproxy->rsp_add[curproxy->nb_rspadd++] = strdup(args[1]);
} }
else if (!strcmp(args[0], "errorloc")) { /* error location */
int errnum;
char *err;
if (*(args[2]) == 0) {
Alert("parsing [%s:%d] : <errorloc> expects <error> and <url> as arguments.\n", file, linenum);
return -1;
}
errnum = atol(args[1]);
err = malloc(strlen(HTTP_302) + strlen(args[2]) + 5);
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 = strlen(err);
}
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 = strlen(err);
}
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 = strlen(err);
}
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 = strlen(err);
}
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 = strlen(err);
}
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 = strlen(err);
}
else if (errnum == 504) {
if (curproxy->errmsg.msg504) {
Warning("parsing [%s:%d] : error %d already defined.\n", file, linenum, errnum);
free(curproxy->errmsg.msg504);
}
curproxy->errmsg.msg504 = err;
curproxy->errmsg.len504 = strlen(err);
}
else {
Warning("parsing [%s:%d] : error %d relocation will be ignored.\n", file, linenum, errnum);
free(err);
}
}
else { else {
Alert("parsing [%s:%d] : unknown keyword <%s> in <listen> section\n", file, linenum, args[0]); Alert("parsing [%s:%d] : unknown keyword <%s> in <listen> section\n", file, linenum, args[0]);
return -1; return -1;
@ -4637,6 +4957,34 @@ int readcfgfile(char *file) {
} }
} }
} }
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);
}
curproxy = curproxy->next; curproxy = curproxy->next;
} }
if (cfgerr > 0) { if (cfgerr > 0) {
@ -4855,6 +5203,7 @@ int main(int argc, char **argv) {
signal(SIGQUIT, dump); signal(SIGQUIT, dump);
signal(SIGUSR1, sig_soft_stop); signal(SIGUSR1, sig_soft_stop);
signal(SIGHUP, sig_dump_state);
/* on very high loads, a sigpipe sometimes happen just between the /* on very high loads, a sigpipe sometimes happen just between the
* getsockopt() which tells "it's OK to write", and the following write :-( * getsockopt() which tells "it's OK to write", and the following write :-(

View File

@ -36,6 +36,18 @@ function do_stop {
if [ ! -z "$pids" ]; then if [ ! -z "$pids" ]; then
echo "Asking $PNAME to terminate gracefully..." echo "Asking $PNAME to terminate gracefully..."
kill -USR1 $pids kill -USR1 $pids
echo "(use kill $pids to stop immediately)."
fi
}
# dump status
function do_status {
pids=`pidof -o $$ -- $PNAME`
if [ ! -z "$pids" ]; then
echo "Dumping $PNAME status in logs."
kill -HUP $pids
else
echo "Process $PNAME is not running."
fi fi
} }