From 036e1cef5161f88b76dd99e7c3f24da520cec043 Mon Sep 17 00:00:00 2001 From: willy tarreau Date: Sat, 17 Dec 2005 13:46:33 +0100 Subject: [PATCH] * released 1.1.18 * Haproxy can be compiled with PCRE regex instead of libc regex, by setting REGEX=pcre on the make command line. * HTTP health-checks now use "OPTIONS *" instead of "OPTIONS /". * when explicit source address binding is required, it is now also used for health-checks. * added 'reqpass' and 'reqipass' to allow certain headers but not the request itself. * factored several strings to reduce binary size by about 2 kB. * replaced setreuid() and setregid() with more standard setuid() and setgid(). * added 4 status flags to the log line indicating who ended the connection first, the sessions state, the validity of the cookie, and action taken on the set-cookie header. * rearranged the changelog and removed it from haproxy.c * large documentation updates --- CHANGELOG | 330 ++++++++++++---------- Makefile | 18 +- doc/haproxy.txt | 121 ++++++-- haproxy.c | 730 +++++++++++++++++++++++++++++------------------- 4 files changed, 735 insertions(+), 464 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 19ea09e2b..da3392b75 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,145 +1,185 @@ -* 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 " - * - 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 - * - implement HTTP health checks when option "httpchk" is specified. - * 2002/08/07 : 1.1.15 - * - replaced setpgid()/setpgrp() with setsid() for better portability, because - * setpgrp() doesn't have the same meaning under Solaris, Linux, and OpenBSD. - * 2002/07/20 : 1.1.14 - * - added "postonly" cookie mode - * 2002/07/15 : 1.1.13 - * - tv_diff used inverted parameters which led to negative times ! - * 2002/07/13 : 1.1.12 - * - fixed stats monitoring, and optimized some tv_* for most common cases. - * - replaced temporary 'newhdr' with 'trash' to reduce stack size - * - made HTTP errors more HTML-fiendly. - * - renamed strlcpy() to strlcpy2() because of a slightly difference between - * their behaviour (return value), to avoid confusion. - * - restricted HTTP messages to HTTP proxies only - * - added a 502 message when the connection has been refused by the server, - * to prevent clients from believing this is a zero-byte HTTP 0.9 reply. - * - changed 'Cache-control:' from 'no-cache="set-cookie"' to 'private' when - * inserting a cookie, because some caches (apache) don't understand it. - * - fixed processing of server headers when client is in SHUTR state - * 2002/07/04 : - * - automatically close fd's 0,1 and 2 when going daemon ; setpgrp() after - * setpgid() - * 2002/06/04 : 1.1.11 - * - fixed multi-cookie handling in client request to allow clean deletion - * in insert+indirect mode. Now, only the server cookie is deleted and not - * all the header. Should now be compliant to RFC2109. - * - added a "nocache" option to "cookie" to specify that we explicitly want - * to add a "cache-control" header when we add a cookie. - * It is also possible to add an "Expires: " to keep compatibility - * with old/broken caches. - * 2002/05/10 : 1.1.10 - * - if a cookie is used in insert+indirect mode, it's desirable that the - * the servers don't see it. It was not possible to remove it correctly - * with regexps, so now it's removed automatically. - * 2002/04/19 : 1.1.9 - * - don't use snprintf()'s return value as an end of message since it may - * be larger. This caused bus errors and segfaults in internal libc's - * getenv() during localtime() in send_log(). - * - removed dead insecure send_syslog() function and all references to it. - * - fixed warnings on Solaris due to buggy implementation of isXXXX(). - * 2002/04/18 : 1.1.8 - * - option "dontlognull" - * - fixed "double space" bug in config parser - * - fixed an uninitialized server field in case of dispatch - * with no existing server which could cause a segfault during - * logging. - * - the pid logged was always the father's, which was wrong for daemons. - * - fixed wrong level "LOG_INFO" for message "proxy started". - * 2002/04/13 : - * - http logging is now complete : - * - ip:port, date, proxy, server - * - req_time, conn_time, hdr_time, tot_time - * - status, size, request - * - source address - * 2002/04/12 : 1.1.7 - * - added option forwardfor - * - added reqirep, reqidel, reqiallow, reqideny, rspirep, rspidel - * - added "log global" in "listen" section. - * 2002/04/09 : - * - added a new "global" section : - * - logs - * - debug, quiet, daemon modes - * - uid, gid, chroot, nbproc, maxconn - * 2002/04/08 : 1.1.6 - * - regex are now chained and not limited anymore. - * - unavailable server now returns HTTP/502. - * - increased per-line args limit to 40 - * - added reqallow/reqdeny to block some request on matches - * - added HTTP 400/403 responses - * 2002/04/03 : 1.1.5 - * - connection logging displayed incorrect source address. - * - added proxy start/stop and server up/down log events. - * - replaced log message short buffers with larger trash. - * - enlarged buffer to 8 kB and replace buffer to 4 kB. - * 2002/03/25 : 1.1.4 - * - made rise/fall/interval time configurable - * 2002/03/22 : 1.1.3 - * - fixed a bug : cr_expire and cw_expire were inverted in CL_STSHUT[WR] - * which could lead to loops. - * 2002/03/21 : 1.1.2 - * - fixed a bug in buffer management where we could have a loop - * between event_read() and process_{cli|srv} if R==BUFSIZE-MAXREWRITE. - * => implemented an adjustable buffer limit. - * - fixed a bug : expiration of tasks in wait queue timeout is used again, - * and running tasks are skipped. - * - added some debug lines for accept events. - * - send warnings for servers up/down. - * 2002/03/12 : 1.1.1 - * - fixed a bug in total failure handling - * - fixed a bug in timestamp comparison within same second (tv_cmp_ms) - * 2002/03/10 : 1.1.0 - * - fixed a few timeout bugs - * - rearranged the task scheduler subsystem to improve performance, - * add new tasks, and make it easier to later port to librt ; - * - allow multiple accept() for one select() wake up ; - * - implemented internal load balancing with basic health-check ; - * - cookie insertion and header add/replace/delete, with better strings - * support. - * 2002/03/08 - * - reworked buffer handling to fix a few rewrite bugs, and - * improve overall performance. - * - implement the "purge" option to delete server cookies in direct mode. - * 2002/03/07 - * - fixed some error cases where the maxfd was not decreased. - * 2002/02/26 - * - now supports transparent proxying, at least on linux 2.4. - * 2002/02/12 - * - soft stop works again (fixed select timeout computation). - * - it seems that TCP proxies sometimes cannot timeout. - * - added a "quiet" mode. - * - enforce file descriptor limitation on socket() and accept(). - * 2001/12/30 : release of version 1.0.2 : fixed a bug in header processing - * 2001/12/19 : release of version 1.0.1 : no MSG_NOSIGNAL on solaris - * 2001/12/16 : release of version 1.0.0. - * 2001/12/16 : added syslog capability for each accepted connection. - * 2001/11/19 : corrected premature end of files and occasional SIGPIPE. - * 2001/10/31 : added health-check type servers (mode health) which replies OK then closes. - * 2001/10/30 : added the ability to support standard TCP proxies and HTTP proxies - * with or without cookies (use keyword http for this). - * 2001/09/01 : added client/server header replacing with regexps. - * eg: - * cliexp ^(Host:\ [^:]*).* Host:\ \1:80 - * srvexp ^Server:\ .* Server:\ Apache - * 2000/11/29 : first fully working release with complete FSMs and timeouts. - * 2000/11/28 : major rewrite - * 2000/11/26 : first write +ChangeLog : +=========== + +2003/04/02 : 1.1.18 + - Haproxy can be compiled with PCRE regex instead of libc regex, by setting + REGEX=pcre on the make command line. + - HTTP health-checks now use "OPTIONS *" instead of "OPTIONS /". + - when explicit source address binding is required, it is now also used for + health-checks. + - added 'reqpass' and 'reqipass' to allow certain headers but not the request + itself. + - factored several strings to reduce binary size by about 2 kB. + - replaced setreuid() and setregid() with more standard setuid() and setgid(). + - added 4 status flags to the log line indicating who ended the connection + first, the sessions state, the validity of the cookie, and action taken on + the set-cookie header. + +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 " + - 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 + - implement HTTP health checks when option "httpchk" is specified. + +2002/08/07 : 1.1.15 + - replaced setpgid()/setpgrp() with setsid() for better portability, because + setpgrp() doesn't have the same meaning under Solaris, Linux, and OpenBSD. + +2002/07/20 : 1.1.14 + - added "postonly" cookie mode + +2002/07/15 : 1.1.13 + - tv_diff used inverted parameters which led to negative times ! + +2002/07/13 : 1.1.12 + - fixed stats monitoring, and optimized some tv_* for most common cases. + - replaced temporary 'newhdr' with 'trash' to reduce stack size + - made HTTP errors more HTML-fiendly. + - renamed strlcpy() to strlcpy2() because of a slightly difference between + their behaviour (return value), to avoid confusion. + - restricted HTTP messages to HTTP proxies only + - added a 502 message when the connection has been refused by the server, + to prevent clients from believing this is a zero-byte HTTP 0.9 reply. + - changed 'Cache-control:' from 'no-cache="set-cookie"' to 'private' when + inserting a cookie, because some caches (apache) don't understand it. + - fixed processing of server headers when client is in SHUTR state + +2002/07/04 : + - automatically close fd's 0,1 and 2 when going daemon ; setpgrp() after + setpgid() + +2002/06/04 : 1.1.11 + - fixed multi-cookie handling in client request to allow clean deletion + in insert+indirect mode. Now, only the server cookie is deleted and not + all the header. Should now be compliant to RFC2109. + - added a "nocache" option to "cookie" to specify that we explicitly want + to add a "cache-control" header when we add a cookie. + It is also possible to add an "Expires: " to keep compatibility + with old/broken caches. + +2002/05/10 : 1.1.10 + - if a cookie is used in insert+indirect mode, it's desirable that the + the servers don't see it. It was not possible to remove it correctly + with regexps, so now it's removed automatically. + +2002/04/19 : 1.1.9 + - don't use snprintf()'s return value as an end of message since it may + be larger. This caused bus errors and segfaults in internal libc's + getenv() during localtime() in send_log(). + - removed dead insecure send_syslog() function and all references to it. + - fixed warnings on Solaris due to buggy implementation of isXXXX(). + +2002/04/18 : 1.1.8 + - option "dontlognull" + - fixed "double space" bug in config parser + - fixed an uninitialized server field in case of dispatch + with no existing server which could cause a segfault during + logging. + - the pid logged was always the father's, which was wrong for daemons. + - fixed wrong level "LOG_INFO" for message "proxy started". + +2002/04/13 : + - http logging is now complete : + - ip:port, date, proxy, server + - req_time, conn_time, hdr_time, tot_time + - status, size, request + - source address + +2002/04/12 : 1.1.7 + - added option forwardfor + - added reqirep, reqidel, reqiallow, reqideny, rspirep, rspidel + - added "log global" in "listen" section. + +2002/04/09 : + - added a new "global" section : + - logs + - debug, quiet, daemon modes + - uid, gid, chroot, nbproc, maxconn + +2002/04/08 : 1.1.6 + - regex are now chained and not limited anymore. + - unavailable server now returns HTTP/502. + - increased per-line args limit to 40 + - added reqallow/reqdeny to block some request on matches + - added HTTP 400/403 responses + +2002/04/03 : 1.1.5 + - connection logging displayed incorrect source address. + - added proxy start/stop and server up/down log events. + - replaced log message short buffers with larger trash. + - enlarged buffer to 8 kB and replace buffer to 4 kB. + +2002/03/25 : 1.1.4 + - made rise/fall/interval time configurable + +2002/03/22 : 1.1.3 + - fixed a bug : cr_expire and cw_expire were inverted in CL_STSHUT[WR] + which could lead to loops. + +2002/03/21 : 1.1.2 + - fixed a bug in buffer management where we could have a loop + between event_read() and process_{cli|srv} if R==BUFSIZE-MAXREWRITE. + => implemented an adjustable buffer limit. + - fixed a bug : expiration of tasks in wait queue timeout is used again, + and running tasks are skipped. + - added some debug lines for accept events. + - send warnings for servers up/down. + +2002/03/12 : 1.1.1 + - fixed a bug in total failure handling + - fixed a bug in timestamp comparison within same second (tv_cmp_ms) + +2002/03/10 : 1.1.0 + - fixed a few timeout bugs + - rearranged the task scheduler subsystem to improve performance, + add new tasks, and make it easier to later port to librt ; + - allow multiple accept() for one select() wake up ; + - implemented internal load balancing with basic health-check ; + - cookie insertion and header add/replace/delete, with better strings + support. + +2002/03/08 + - reworked buffer handling to fix a few rewrite bugs, and + improve overall performance. + - implement the "purge" option to delete server cookies in direct mode. + +2002/03/07 + - fixed some error cases where the maxfd was not decreased. + +2002/02/26 + - now supports transparent proxying, at least on linux 2.4. + +2002/02/12 + - soft stop works again (fixed select timeout computation). + - it seems that TCP proxies sometimes cannot timeout. + - added a "quiet" mode. + - enforce file descriptor limitation on socket() and accept(). + +2001/12/30 : release of version 1.0.2 : fixed a bug in header processing +2001/12/19 : release of version 1.0.1 : no MSG_NOSIGNAL on solaris +2001/12/16 : release of version 1.0.0. +2001/12/16 : added syslog capability for each accepted connection. +2001/11/19 : corrected premature end of files and occasional SIGPIPE. +2001/10/31 : added health-check type servers (mode health) which replies OK then closes. +2001/10/30 : added the ability to support standard TCP proxies and HTTP proxies + with or without cookies (use keyword http for this). +2001/09/01 : added client/server header replacing with regexps. + eg: + cliexp ^(Host:\ [^:]*).* Host:\ \1:80 + srvexp ^Server:\ .* Server:\ Apache +2000/11/29 : first fully working release with complete FSMs and timeouts. +2000/11/28 : major rewrite +2000/11/26 : first write diff --git a/Makefile b/Makefile index 70d5008ca..959d8660f 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,14 @@ TARGET = linux24 CC = gcc LD = gcc +# By default, we use libc's regex. +REGEX=libc +#REGEX=pcre + +# This is the directory hosting include/pcre.h and lib/libpcre.* when REGEX=pcre +PCREDIR := $(shell pcre-config --prefix) +#PCREDIR=/usr/local + # This is for Linux 2.4 with netfilter COPTS.linux24 = -O2 -DNETFILTER LIBS.linux24 = @@ -30,11 +38,17 @@ LIBS.solarisv9 = -lnsl -lsocket COPTS.openbsd = -O2 LIBS.openbsd = +COPTS.libc= +LIBS.libs= + +COPTS.pcre=-DUSE_PCRE -I$(PCREDIR)/include +LIBS.pcre=-L$(PCREDIR)/lib -lpcreposix -lpcre + #DEBUG = DEBUG = -g -COPTS=$(COPTS.$(TARGET)) -LIBS=$(LIBS.$(TARGET)) +COPTS=$(COPTS.$(TARGET)) $(COPTS.$(REGEX)) +LIBS=$(LIBS.$(TARGET)) $(LIBS.$(REGEX)) # - use -DSTATTIME=0 to disable statistics, else specify an interval in # milliseconds. diff --git a/doc/haproxy.txt b/doc/haproxy.txt index 18090baeb..aea6d0cb7 100644 --- a/doc/haproxy.txt +++ b/doc/haproxy.txt @@ -1,9 +1,9 @@ H A - P r o x y --------------- - version 1.1.17 + version 1.1.18 willy tarreau - 2002/10/25 + 2003/04/06 ================ | Introduction | @@ -119,6 +119,8 @@ Les cat uucp, cron, auth2, ftp, ntp, audit, alert, cron2, local0, local1, local2, local3, local4, local5, local6, local7 +Conformément à la RFC3164, les messages émis sont limités à 1024 caractères. + Exemple : --------- global @@ -437,7 +439,9 @@ sp plus généralement pour permettre aux serveurs de trouver le chemin de retour dans des contextes de routage difficiles. Si l'adresse est 0.0.0.0, elle sera choisie librement par le systeme. Si le port est 0, il -sera choisi librement par le système. +sera choisi librement par le système. Il est à noter que depuis la version +1.1.18, les tests de bon foncitonnement des serveurs seront aussi effectués à +partir de la source spécifiée par ce paramètre. Exemples : ---------- @@ -583,12 +587,15 @@ param Le mode par défaut consiste à établir des connexions TCP uniquement. Dans certains cas de pannes, des serveurs peuvent continuer à accepter les connexions sans les traiter. Depuis la version 1.1.16, haproxy est en mesure d'envoyer des -requêtes HTTP courtes et très peu coûteuses : "OPTIONS / HTTP/1.0". Elles -présentent l'avantage d'être facilement extractibles des logs, et de ne pas -induire d'accès aux fichiers côté serveur. Seules les réponses 2xx et 3xx sont -considérées valides, les autres (y compris non-réponses) aboutissent à un échec. -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". +requêtes HTTP courtes et très peu coûteuses. Les versions 1.1.16 et 1.1.17 +utilisent "OPTIONS / HTTP/1.0". Depuis la version 1.1.18, les requêtes ont été +changées en "OPTIONS * HTTP/1.0" pour des raisons de contrôle d'accès aux +ressources. Elles présentent l'avantage d'être facilement extractibles des logs, +et de ne pas induire d'accès aux fichiers côté serveur. Seules les réponses 2xx +et 3xx sont considérées valides, les autres (y compris non-réponses) aboutissent +à un échec. 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". 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, @@ -747,6 +754,65 @@ connexions et d 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). +Exemple : +--------- + listen http_proxy 0.0.0.0:80 + mode http + option httplog + option dontlognull + log 192.168.2.200 local3 + +Depuis la version 1.1.18, un indicateur de complétude de la session a été ajouté +dans les logs HTTP. C'est un champ de 4 caractères précédant la requête HTTP, +indiquant : + - sur le premier caractère, un code précisant le premier événement qui a causé + la terminaison de la session : + + C : fermeture de la session TCP de la part du client + S : fermeture de la session TCP de la part du serveur, ou refus de connexion + P : terminaison prématurée des sessions par le proxy, pour cas d'erreur + interne ou de configuration (ex: filtre d'URL) + c : expiration du délai d'attente côté client : clitimeout + s : expiration du délai d'attente côté serveur: srvtimeout et contimeout + - : terminaison normale. + + - sur le second caractère, l'état d'avancement de la session HTTP lors de la + fermeture : + + R : terminaison en attendant la réception totale de la requête du client + C : terminaison en attendant la connexion vers le serveur + H : terminaison en attendant la réception totale des entêtes du serveur + D : terminaison durant le transfert des données du serveur vers le client + L : terminaison durant le transfert des dernières données du proxy vers + le client, alors que le serveur a déjà fini. + - : terminaison normale, après fin de transfert des données + + - le troisième caractère indique l'éventuelle identification d'un cookie de + persistence : + + N : aucun cookie de persistence n'a été présenté. + I : le client a présenté un cookie ne correspondant à aucun serveur + connu. + D : le client a présenté un cookie correspondant à un serveur hors + d'usage. Suivant l'option 'persist', il a été renvoyé vers un + autre serveur ou a tout de même tenté de se connecter sur celui + correspondant au cookie. + V : le client a présenté un cookie valide et a pu se connecter au + serveur correspondant. + - : non appliquable + + - le dernier caractère indique l'éventuel traitement effectué sur un cookie de + persistence retrourné par le serveur : + + N : aucun cookie de persistence n'a été fourni par le serveur. + P : un cookie cookie de persistence n'a été fourni par le serveur. + I : aucun cookie n'a été fourni par le serveur, il a été inséré par le + proxy. + D : le cookie présenté par le serveur a été supprimé par le proxy pour + ne pas être retourné au client. + R : le cookie retourné par le serveur a été modifié par le proxy. + - : non appliquable + 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 @@ -768,10 +834,10 @@ Exemples : # 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. +Dans les logs, le champ précédant l'indicateur de complétude contient 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 'X-Forwarded-For' de la requête, ce qui permet à un serveur web final de @@ -804,30 +870,33 @@ La syntaxe est : reqirep idem sans distinction majuscules/minuscules reqdel pour supprimer un en-tête dans la requête reqidel idem sans distinction majuscules/minuscules - reqallow autoriser une requête qui valide + reqallow autoriser la requête si un entête valide reqiallow idem sans distinction majuscules/minuscules - reqdeny interdire une requête qui valide + reqdeny interdire la requête si un entête valide reqideny idem sans distinction majuscules/minuscules + reqpass inhibe ces actions sur les entêtes validant + reqipass idem sans distinction majuscules/minuscules - rspadd pour ajouter un en-tête dans la réponse - rsprep pour modifier la réponse - rspirep idem sans distinction majuscules/minuscules - rspdel pour supprimer un en-tête dans la réponse - rspidel idem sans distinction majuscules/minuscules + rspadd pour ajouter un en-tête dans la réponse + rsprep pour modifier la réponse + rspirep idem sans distinction majuscules/minuscules + rspdel pour supprimer un en-tête dans la réponse + rspidel idem sans distinction majuscules/minuscules - est une expression régulière compatible GNU regexp supportant -le groupage par parenthèses (sans les '\'). Les espaces et autres -séparateurs doivent êtres précédés d'un '\' pour ne pas être confondus -avec la fin de la chaîne. De plus, certains caractères spéciaux peuvent -être précédés d'un backslach ('\') : + est une expression régulière compatible POSIX regexp supportant le +groupage par parenthèses (sans les '\'). Les espaces et autres séparateurs +doivent êtres précédés d'un '\' pour ne pas être confondus avec la fin de la +chaîne. De plus, certains caractères spéciaux peuvent être précédés d'un +backslach ('\') : \t pour une tabulation \r pour un retour charriot \n pour un saut de ligne \ pour différencier un espace d'un séparateur \# pour différencier un dièse d'un commentaire - \\ pour un backslash + \\ pour utiliser un backslash dans la regex + \\\\ pour utiliser un backslash dans le texte \xXX pour un caractère spécifique XX (comme en C) diff --git a/haproxy.c b/haproxy.c index e34346098..42e9e87a0 100644 --- a/haproxy.c +++ b/haproxy.c @@ -1,6 +1,6 @@ /* * HA-Proxy : High Availability-enabled HTTP/TCP proxy - * 2000-2002 - Willy Tarreau - willy AT meta-x DOT org. + * 2000-2003 - Willy Tarreau - willy AT meta-x DOT org. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -15,151 +15,7 @@ * related to missing setsid() (fixed in 1.1.15) * - a proxy with an invalid config will prevent the startup even if disabled. * - * 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 " - * - 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 - * - implement HTTP health checks when option "httpchk" is specified. - * 2002/08/07 : 1.1.15 - * - replaced setpgid()/setpgrp() with setsid() for better portability, because - * setpgrp() doesn't have the same meaning under Solaris, Linux, and OpenBSD. - * 2002/07/20 : 1.1.14 - * - added "postonly" cookie mode - * 2002/07/15 : 1.1.13 - * - tv_diff used inverted parameters which led to negative times ! - * 2002/07/13 : 1.1.12 - * - fixed stats monitoring, and optimized some tv_* for most common cases. - * - replaced temporary 'newhdr' with 'trash' to reduce stack size - * - made HTTP errors more HTML-fiendly. - * - renamed strlcpy() to strlcpy2() because of a slightly difference between - * their behaviour (return value), to avoid confusion. - * - restricted HTTP messages to HTTP proxies only - * - added a 502 message when the connection has been refused by the server, - * to prevent clients from believing this is a zero-byte HTTP 0.9 reply. - * - changed 'Cache-control:' from 'no-cache="set-cookie"' to 'private' when - * inserting a cookie, because some caches (apache) don't understand it. - * - fixed processing of server headers when client is in SHUTR state - * 2002/07/04 : - * - automatically close fd's 0,1 and 2 when going daemon ; setpgrp() after - * setpgid() - * 2002/06/04 : 1.1.11 - * - fixed multi-cookie handling in client request to allow clean deletion - * in insert+indirect mode. Now, only the server cookie is deleted and not - * all the header. Should now be compliant to RFC2109. - * - added a "nocache" option to "cookie" to specify that we explicitly want - * to add a "cache-control" header when we add a cookie. - * It is also possible to add an "Expires: " to keep compatibility - * with old/broken caches. - * 2002/05/10 : 1.1.10 - * - if a cookie is used in insert+indirect mode, it's desirable that the - * the servers don't see it. It was not possible to remove it correctly - * with regexps, so now it's removed automatically. - * 2002/04/19 : 1.1.9 - * - don't use snprintf()'s return value as an end of message since it may - * be larger. This caused bus errors and segfaults in internal libc's - * getenv() during localtime() in send_log(). - * - removed dead insecure send_syslog() function and all references to it. - * - fixed warnings on Solaris due to buggy implementation of isXXXX(). - * 2002/04/18 : 1.1.8 - * - option "dontlognull" - * - fixed "double space" bug in config parser - * - fixed an uninitialized server field in case of dispatch - * with no existing server which could cause a segfault during - * logging. - * - the pid logged was always the father's, which was wrong for daemons. - * - fixed wrong level "LOG_INFO" for message "proxy started". - * 2002/04/13 : - * - http logging is now complete : - * - ip:port, date, proxy, server - * - req_time, conn_time, hdr_time, tot_time - * - status, size, request - * - source address - * 2002/04/12 : 1.1.7 - * - added option forwardfor - * - added reqirep, reqidel, reqiallow, reqideny, rspirep, rspidel - * - added "log global" in "listen" section. - * 2002/04/09 : - * - added a new "global" section : - * - logs - * - debug, quiet, daemon modes - * - uid, gid, chroot, nbproc, maxconn - * 2002/04/08 : 1.1.6 - * - regex are now chained and not limited anymore. - * - unavailable server now returns HTTP/502. - * - increased per-line args limit to 40 - * - added reqallow/reqdeny to block some request on matches - * - added HTTP 400/403 responses - * 2002/04/03 : 1.1.5 - * - connection logging displayed incorrect source address. - * - added proxy start/stop and server up/down log events. - * - replaced log message short buffers with larger trash. - * - enlarged buffer to 8 kB and replace buffer to 4 kB. - * 2002/03/25 : 1.1.4 - * - made rise/fall/interval time configurable - * 2002/03/22 : 1.1.3 - * - fixed a bug : cr_expire and cw_expire were inverted in CL_STSHUT[WR] - * which could lead to loops. - * 2002/03/21 : 1.1.2 - * - fixed a bug in buffer management where we could have a loop - * between event_read() and process_{cli|srv} if R==BUFSIZE-MAXREWRITE. - * => implemented an adjustable buffer limit. - * - fixed a bug : expiration of tasks in wait queue timeout is used again, - * and running tasks are skipped. - * - added some debug lines for accept events. - * - send warnings for servers up/down. - * 2002/03/12 : 1.1.1 - * - fixed a bug in total failure handling - * - fixed a bug in timestamp comparison within same second (tv_cmp_ms) - * 2002/03/10 : 1.1.0 - * - fixed a few timeout bugs - * - rearranged the task scheduler subsystem to improve performance, - * add new tasks, and make it easier to later port to librt ; - * - allow multiple accept() for one select() wake up ; - * - implemented internal load balancing with basic health-check ; - * - cookie insertion and header add/replace/delete, with better strings - * support. - * 2002/03/08 - * - reworked buffer handling to fix a few rewrite bugs, and - * improve overall performance. - * - implement the "purge" option to delete server cookies in direct mode. - * 2002/03/07 - * - fixed some error cases where the maxfd was not decreased. - * 2002/02/26 - * - now supports transparent proxying, at least on linux 2.4. - * 2002/02/12 - * - soft stop works again (fixed select timeout computation). - * - it seems that TCP proxies sometimes cannot timeout. - * - added a "quiet" mode. - * - enforce file descriptor limitation on socket() and accept(). - * 2001/12/30 : release of version 1.0.2 : fixed a bug in header processing - * 2001/12/19 : release of version 1.0.1 : no MSG_NOSIGNAL on solaris - * 2001/12/16 : release of version 1.0.0. - * 2001/12/16 : added syslog capability for each accepted connection. - * 2001/11/19 : corrected premature end of files and occasional SIGPIPE. - * 2001/10/31 : added health-check type servers (mode health) which replies OK then closes. - * 2001/10/30 : added the ability to support standard TCP proxies and HTTP proxies - * with or without cookies (use keyword http for this). - * 2001/09/01 : added client/server header replacing with regexps. - * eg: - * cliexp ^(Host:\ [^:]*).* Host:\ \1:80 - * srvexp ^Server:\ .* Server:\ Apache - * 2000/11/29 : first fully working release with complete FSMs and timeouts. - * 2000/11/28 : major rewrite - * 2000/11/26 : first write + * ChangeLog has moved to the CHANGELOG file. * * TODO: * - handle properly intermediate incomplete server headers. Done ? @@ -191,8 +47,8 @@ #include #endif -#define HAPROXY_VERSION "1.1.17" -#define HAPROXY_DATE "2002/10/18" +#define HAPROXY_VERSION "1.1.18" +#define HAPROXY_DATE "2003/04/02" /* this is for libc5 for example */ #ifndef TCP_NODELAY @@ -364,12 +220,43 @@ int strlcpy2(char *dst, const char *src, int size) { /* various session flags */ -#define SN_DIRECT 1 /* connection made on the server matching the client cookie */ -#define SN_CLDENY 2 /* a client header matches a deny regex */ -#define SN_CLALLOW 4 /* a client header matches an allow regex */ -#define SN_SVDENY 8 /* a server header matches a deny regex */ -#define SN_SVALLOW 16 /* a server header matches an allow regex */ -#define SN_POST 32 /* the request was an HTTP POST */ +#define SN_DIRECT 0x00000001 /* connection made on the server matching the client cookie */ +#define SN_CLDENY 0x00000002 /* a client header matches a deny regex */ +#define SN_CLALLOW 0x00000004 /* a client header matches an allow regex */ +#define SN_SVDENY 0x00000008 /* a server header matches a deny regex */ +#define SN_SVALLOW 0x00000010 /* a server header matches an allow regex */ +#define SN_POST 0x00000020 /* the request was an HTTP POST */ + +#define SN_CK_NONE 0x00000000 /* this session had no cookie */ +#define SN_CK_INVALID 0x00000040 /* this session had a cookie which matches no server */ +#define SN_CK_DOWN 0x00000080 /* this session had cookie matching a down server */ +#define SN_CK_VALID 0x000000C0 /* this session had cookie matching a valid server */ +#define SN_CK_MASK 0x000000C0 /* mask to get this session's cookie flags */ +#define SN_CK_SHIFT 6 /* bit shift */ + +#define SN_ERR_CLITO 0x00000100 /* client time-out */ +#define SN_ERR_CLICL 0x00000200 /* client closed (read/write error) */ +#define SN_ERR_SRVTO 0x00000300 /* server time-out, connect time-out */ +#define SN_ERR_SRVCL 0x00000400 /* server closed (connect/read/write error) */ +#define SN_ERR_PRXCOND 0x00000500 /* the proxy decided to close (deny...) */ +#define SN_ERR_MASK 0x00000700 /* mask to get only session error flags */ +#define SN_ERR_SHIFT 8 /* bit shift */ + +#define SN_FINST_R 0x00001000 /* session ended during client request */ +#define SN_FINST_C 0x00002000 /* session ended during server connect */ +#define SN_FINST_H 0x00003000 /* session ended during server headers */ +#define SN_FINST_D 0x00004000 /* session ended during data phase */ +#define SN_FINST_L 0x00005000 /* session ended while pushing last data to client */ +#define SN_FINST_MASK 0x00007000 /* mask to get only final session state flags */ +#define SN_FINST_SHIFT 12 /* bit shift */ + +#define SN_SCK_NONE 0x00000000 /* no set-cookie seen for the server cookie */ +#define SN_SCK_DELETED 0x00010000 /* existing set-cookie deleted or changed */ +#define SN_SCK_INSERTED 0x00020000 /* new set-cookie inserted or changed existing one */ +#define SN_SCK_SEEN 0x00040000 /* set-cookie seen for the server cookie */ +#define SN_SCK_MASK 0x00070000 /* mask to get the set-cookie field */ +#define SN_SCK_SHIFT 16 /* bit shift */ + /* different possible states for the client side */ #define CL_STHEADERS 0 @@ -409,6 +296,7 @@ int strlcpy2(char *dst, const char *src, int size) { #define ACT_REPLACE 1 /* replace the matching header */ #define ACT_REMOVE 2 /* remove the matching header */ #define ACT_DENY 3 /* deny the request */ +#define ACT_PASS 4 /* pass this header without allowing or denying the request */ /* configuration sections */ #define CFG_NONE 0 @@ -630,7 +518,7 @@ static regmatch_t pmatch[MAX_MATCH]; /* rm_so, rm_eo for regular expressions */ static char trash[BUFSIZE]; /* - * Syslog facilities and levels + * Syslog facilities and levels. Conforming to RFC3164. */ #define MAX_SYSLOG_LEN 1024 @@ -655,6 +543,14 @@ const char *log_levels[NB_LOG_LEVELS] = { const char *monthname[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + +const char sess_term_cond[8] = "-cCsSP67"; /* normal, CliTo, CliErr, SrvTo, SrvErr, PxErr, unknown */ +const char sess_fin_state[8] = "-RCHDL67"; /* cliRequest, srvConnect, srvHeader, Data, Last, unknown */ +const char sess_cookie[4] = "NIDV"; /* No cookie, Invalid cookie, cookie for a Down server, Valid cookie */ +const char sess_set_cookie[8] = "N1I3PD5R"; /* No set-cookie, unknown, Set-Cookie Inserted, unknown, + Set-cookie seen and left unchanged (passive), Set-cookie Deleted, + unknown, Set-cookie Rewritten */ + #define MAX_HOSTNAME_LEN 32 static char hostname[MAX_HOSTNAME_LEN] = ""; @@ -869,7 +765,7 @@ struct sockaddr_in *str2sa(char *str) { struct hostent *he; if ((he = gethostbyname(str)) == NULL) { - Alert("Invalid server name: <%s>\n", str); + Alert("Invalid server name: '%s'\n", str); } else sa.sin_addr = *(struct in_addr *) *(he->h_addr_list); @@ -1958,7 +1854,7 @@ void sess_log(struct session *s) { if (p->to_log & LW_DATE) { 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 %s \"%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 %c%c%c%c \"%s\"\n", 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_hour, tm->tm_min, tm->tm_sec, @@ -1970,10 +1866,14 @@ void sess_log(struct session *s) { s->logs.status, s->logs.bytes, s->logs.cli_cookie ? s->logs.cli_cookie : "-", s->logs.srv_cookie ? s->logs.srv_cookie : "-", + sess_term_cond[(s->flags & SN_ERR_MASK) >> SN_ERR_SHIFT], + sess_fin_state[(s->flags & SN_FINST_MASK) >> SN_FINST_SHIFT], + (p->options & PR_O_COOK_ANY) ? sess_cookie[(s->flags & SN_CK_MASK) >> SN_CK_SHIFT] : '-', + (p->options & PR_O_COOK_ANY) ? sess_set_cookie[(s->flags & SN_SCK_MASK) >> SN_SCK_SHIFT] : '-', uri); } else { - send_log(p, LOG_INFO, "%d.%d.%d.%d:%d %s %s %d/%d/%d/%d %d %lld %s %s \"%s\"\n", + send_log(p, LOG_INFO, "%d.%d.%d.%d:%d %s %s %d/%d/%d/%d %d %lld %s %s %c%c%c%c \"%s\"\n", pn[0], pn[1], pn[2], pn[3], ntohs(s->cli_addr.sin_port), pxid, srv, s->logs.t_request, @@ -1983,6 +1883,10 @@ void sess_log(struct session *s) { s->logs.status, s->logs.bytes, s->logs.cli_cookie ? s->logs.cli_cookie : "-", s->logs.srv_cookie ? s->logs.srv_cookie : "-", + sess_term_cond[(s->flags & SN_ERR_MASK) >> SN_ERR_SHIFT], + sess_fin_state[(s->flags & SN_FINST_MASK) >> SN_FINST_SHIFT], + (p->options & PR_O_COOK_ANY) ? sess_cookie[(s->flags & SN_CK_MASK) >> SN_CK_SHIFT] : '-', + (p->options & PR_O_COOK_ANY) ? sess_set_cookie[(s->flags & SN_SCK_MASK) >> SN_SCK_SHIFT] : '-', uri); } @@ -2188,13 +2092,13 @@ int event_srv_chk_w(int fd) { else { if (s->proxy->options & PR_O_HTTP_CHK) { int ret; - /* we want to check if this host replies to "OPTIONS / HTTP/1.0" + /* we want to check if this host replies to "OPTIONS * HTTP/1.0" * so we'll send the request, and won't wake the checker up now. */ #ifndef MSG_NOSIGNAL - ret = send(fd, "OPTIONS / HTTP/1.0\r\n\r\n", 22, MSG_DONTWAIT); + ret = send(fd, "OPTIONS * HTTP/1.0\r\n\r\n", 22, MSG_DONTWAIT); #else - ret = send(fd, "OPTIONS / HTTP/1.0\r\n\r\n", 22, MSG_DONTWAIT | MSG_NOSIGNAL); + ret = send(fd, "OPTIONS * HTTP/1.0\r\n\r\n", 22, MSG_DONTWAIT | MSG_NOSIGNAL); #endif if (ret == 22) { FD_SET(fd, StaticReadEvent); /* prepare for reading reply */ @@ -2390,6 +2294,10 @@ int process_cli(struct session *t) { /* no need to go further */ t->logs.status = 403; client_retnclose(t, t->proxy->errmsg.len403, t->proxy->errmsg.msg403); + if (!(t->flags & SN_ERR_MASK)) + t->flags |= SN_ERR_PRXCOND; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= SN_FINST_R; return 1; } @@ -2454,6 +2362,10 @@ int process_cli(struct session *t) { Alert("HTTP logging : out of memory.\n"); t->logs.status = 500; client_retnclose(t, t->proxy->errmsg.len500, t->proxy->errmsg.msg500); + if (!(t->flags & SN_ERR_MASK)) + t->flags |= SN_ERR_PRXCOND; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= SN_FINST_R; return 1; } @@ -2508,6 +2420,8 @@ int process_cli(struct session *t) { if (!(t->flags & SN_CLALLOW)) t->flags |= SN_CLDENY; break; + case ACT_PASS: /* we simply don't deny this one */ + break; } break; } @@ -2606,12 +2520,21 @@ int process_cli(struct session *t) { srv = srv->next; } - if (srv && - (srv->state & SRV_RUNNING || t->proxy->options & PR_O_PERSIST)) { + if (!srv) { + t->flags &= ~SN_CK_MASK; + t->flags |= SN_CK_INVALID; + } + else if (srv->state & SRV_RUNNING || t->proxy->options & PR_O_PERSIST) { /* we found the server and it's usable */ - t->flags |= SN_DIRECT; + t->flags &= ~SN_CK_MASK; + t->flags |= SN_CK_VALID | SN_DIRECT; t->srv = srv; } + else { + t->flags &= ~SN_CK_MASK; + t->flags |= SN_CK_DOWN; + } + /* if this cookie was set in insert+indirect mode, then it's better that the * server never sees it. */ @@ -2693,16 +2616,21 @@ int process_cli(struct session *t) { if (req->l >= req->rlim - req->data) { t->logs.status = 400; client_retnclose(t, t->proxy->errmsg.len400, t->proxy->errmsg.msg400); + if (!(t->flags & SN_ERR_MASK)) + t->flags |= SN_ERR_PRXCOND; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= SN_FINST_R; return 1; } else if (t->res_cr == RES_ERROR || t->res_cr == RES_NULL) { - /* read error, or last read : give up. - * 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. - */ + /* read error, or last read : give up. */ tv_eternity(&t->crexpire); fd_delete(t->cli_fd); t->cli_state = CL_STCLOSE; + if (!(t->flags & SN_ERR_MASK)) + t->flags |= SN_ERR_CLICL; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= SN_FINST_R; return 1; } else if (tv_cmp2_ms(&t->crexpire, &now) <= 0) { @@ -2711,6 +2639,10 @@ int process_cli(struct session *t) { */ t->logs.status = 408; client_retnclose(t, t->proxy->errmsg.len408, t->proxy->errmsg.msg408); + if (!(t->flags & SN_ERR_MASK)) + t->flags |= SN_ERR_CLITO; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= SN_FINST_R; return 1; } @@ -2723,11 +2655,14 @@ int process_cli(struct session *t) { tv_eternity(&t->cwexpire); fd_delete(t->cli_fd); t->cli_state = CL_STCLOSE; + if (!(t->flags & SN_ERR_MASK)) + t->flags |= SN_ERR_CLICL; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= SN_FINST_D; return 1; } - /* read timeout, last read, or end of server write */ - else if (t->res_cr == RES_NULL || s == SV_STSHUTW || s == SV_STCLOSE - || tv_cmp2_ms(&t->crexpire, &now) <= 0) { + /* last read, or end of server write */ + else if (t->res_cr == RES_NULL || s == SV_STSHUTW || s == SV_STCLOSE) { FD_CLR(t->cli_fd, StaticReadEvent); // if (req->l == 0) /* nothing to write on the server side */ // FD_CLR(t->srv_fd, StaticWriteEvent); @@ -2736,15 +2671,40 @@ int process_cli(struct session *t) { t->cli_state = CL_STSHUTR; return 1; } - /* write timeout, or last server read and buffer empty */ - else if (((s == SV_STSHUTR || s == SV_STCLOSE) && (rep->l == 0)) - ||(tv_cmp2_ms(&t->cwexpire, &now) <= 0)) { + /* last server read and buffer empty */ + else if ((s == SV_STSHUTR || s == SV_STCLOSE) && (rep->l == 0)) { FD_CLR(t->cli_fd, StaticWriteEvent); tv_eternity(&t->cwexpire); shutdown(t->cli_fd, SHUT_WR); t->cli_state = CL_STSHUTW; return 1; } + /* read timeout */ + else if (tv_cmp2_ms(&t->crexpire, &now) <= 0) { + FD_CLR(t->cli_fd, StaticReadEvent); + // if (req->l == 0) /* nothing to write on the server side */ + // FD_CLR(t->srv_fd, StaticWriteEvent); + tv_eternity(&t->crexpire); + shutdown(t->cli_fd, SHUT_RD); + t->cli_state = CL_STSHUTR; + if (!(t->flags & SN_ERR_MASK)) + t->flags |= SN_ERR_CLITO; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= SN_FINST_D; + return 1; + } + /* write timeout */ + else if (tv_cmp2_ms(&t->cwexpire, &now) <= 0) { + FD_CLR(t->cli_fd, StaticWriteEvent); + tv_eternity(&t->cwexpire); + shutdown(t->cli_fd, SHUT_WR); + t->cli_state = CL_STSHUTW; + if (!(t->flags & SN_ERR_MASK)) + t->flags |= SN_ERR_CLICL; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= SN_FINST_D; + return 1; + } if (req->l >= req->rlim - req->data) { /* no room to read more data */ @@ -2784,12 +2744,30 @@ int process_cli(struct session *t) { return 0; /* other cases change nothing */ } else if (c == CL_STSHUTR) { - if ((t->res_cw == RES_ERROR) || - ((s == SV_STSHUTR || s == SV_STCLOSE) && (rep->l == 0)) - || (tv_cmp2_ms(&t->cwexpire, &now) <= 0)) { + if (t->res_cw == RES_ERROR) { tv_eternity(&t->cwexpire); fd_delete(t->cli_fd); t->cli_state = CL_STCLOSE; + if (!(t->flags & SN_ERR_MASK)) + t->flags |= SN_ERR_CLICL; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= SN_FINST_D; + return 1; + } + else if ((s == SV_STSHUTR || s == SV_STCLOSE) && (rep->l == 0)) { + tv_eternity(&t->cwexpire); + fd_delete(t->cli_fd); + t->cli_state = CL_STCLOSE; + return 1; + } + else if (tv_cmp2_ms(&t->cwexpire, &now) <= 0) { + tv_eternity(&t->cwexpire); + fd_delete(t->cli_fd); + t->cli_state = CL_STCLOSE; + if (!(t->flags & SN_ERR_MASK)) + t->flags |= SN_ERR_CLITO; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= SN_FINST_D; return 1; } else if ((rep->l == 0) || @@ -2811,11 +2789,30 @@ int process_cli(struct session *t) { return 0; } else if (c == CL_STSHUTW) { - if (t->res_cr == RES_ERROR || t->res_cr == RES_NULL || s == SV_STSHUTW || - s == SV_STCLOSE || tv_cmp2_ms(&t->crexpire, &now) <= 0) { + if (t->res_cr == RES_ERROR) { tv_eternity(&t->crexpire); fd_delete(t->cli_fd); t->cli_state = CL_STCLOSE; + if (!(t->flags & SN_ERR_MASK)) + t->flags |= SN_ERR_CLICL; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= SN_FINST_D; + return 1; + } + else if (t->res_cr == RES_NULL || s == SV_STSHUTW || s == SV_STCLOSE) { + tv_eternity(&t->crexpire); + fd_delete(t->cli_fd); + t->cli_state = CL_STCLOSE; + return 1; + } + else if (tv_cmp2_ms(&t->crexpire, &now) <= 0) { + tv_eternity(&t->crexpire); + fd_delete(t->cli_fd); + t->cli_state = CL_STCLOSE; + if (!(t->flags & SN_ERR_MASK)) + t->flags |= SN_ERR_CLITO; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= SN_FINST_D; return 1; } else if (req->l >= req->rlim - req->data) { @@ -2875,6 +2872,10 @@ int process_srv(struct session *t) { (c == CL_STSHUTR && t->req->l == 0)) { /* give up */ tv_eternity(&t->cnexpire); t->srv_state = SV_STCLOSE; + if (!(t->flags & SN_ERR_MASK)) + t->flags |= SN_ERR_CLICL; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= SN_FINST_C; return 1; } else { /* go to SV_STCONN */ @@ -2887,6 +2888,10 @@ int process_srv(struct session *t) { if ((t->proxy->options & PR_O_REDISP) && (t->conn_retries == 0)) { t->flags &= ~SN_DIRECT; /* ignore cookie and force to use the dispatcher */ t->srv = NULL; /* it's left to the dispatcher to choose a server */ + if ((t->flags & SN_CK_MASK) == SN_CK_VALID) { + t->flags &= ~SN_CK_MASK; + t->flags |= SN_CK_DOWN; + } } if (connect_server(t) == 0) { @@ -2901,6 +2906,10 @@ int process_srv(struct session *t) { t->logs.status = 503; if (t->proxy->mode == PR_MODE_HTTP) client_return(t, t->proxy->errmsg.len503, t->proxy->errmsg.msg503); + if (!(t->flags & SN_ERR_MASK)) + t->flags |= SN_ERR_SRVCL; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= SN_FINST_C; } } return 1; @@ -2922,6 +2931,10 @@ int process_srv(struct session *t) { if ((t->proxy->options & PR_O_REDISP) && (t->conn_retries == 0)) { t->flags &= ~SN_DIRECT; /* ignore cookie and force to use the dispatcher */ t->srv = NULL; /* it's left to the dispatcher to choose a server */ + if ((t->flags & SN_CK_MASK) == SN_CK_VALID) { + t->flags &= ~SN_CK_MASK; + t->flags |= SN_CK_DOWN; + } } if (connect_server(t) == 0) return 0; /* no state changed */ @@ -2932,6 +2945,10 @@ int process_srv(struct session *t) { t->logs.status = 503; if (t->proxy->mode == PR_MODE_HTTP) client_return(t, t->proxy->errmsg.len503, t->proxy->errmsg.msg503); + if (!(t->flags & SN_ERR_MASK)) + t->flags |= SN_ERR_SRVCL; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= SN_FINST_C; return 1; } else { /* no error or write 0 */ @@ -2962,7 +2979,6 @@ int process_srv(struct session *t) { } } else if (s == SV_STHEADERS) { /* receiving server headers */ - /* now parse the partial (or complete) headers */ while (rep->lr < rep->r) { /* this loop only sees one header at each iteration */ char *ptr; @@ -2990,6 +3006,8 @@ int process_srv(struct session *t) { t->proxy->cookie_name, t->srv->cookie ? t->srv->cookie : ""); + t->flags |= SN_SCK_INSERTED; + /* 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 ! * Some caches understand the correct form: 'no-cache="set-cookie"', but @@ -3088,6 +3106,8 @@ int process_srv(struct session *t) { if (!(t->flags & SN_SVALLOW)) t->flags |= SN_SVDENY; break; + case ACT_PASS: /* we simply don't deny this one */ + break; } break; } @@ -3153,6 +3173,7 @@ int process_srv(struct session *t) { 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 */ + t->flags |= SN_SCK_SEEN; /* If the cookie is in insert mode on a known server, we'll delete * this occurrence because we'll insert another one later. @@ -3162,12 +3183,14 @@ int process_srv(struct session *t) { ((t->flags & SN_DIRECT) && (t->proxy->options & PR_O_COOK_IND))) { /* this header must be deleted */ delete_header = 1; + t->flags |= SN_SCK_DELETED; } else if ((t->srv) && (t->proxy->options & PR_O_COOK_RW)) { /* replace bytes p3->p4 with the cookie name associated * with this server since we know it. */ buffer_replace2(rep, p3, p4, t->srv->cookie, t->srv->cklen); + t->flags |= SN_SCK_INSERTED | SN_SCK_DELETED; } break; } @@ -3211,6 +3234,10 @@ int process_srv(struct session *t) { t->srv_state = SV_STCLOSE; t->logs.status = 502; client_return(t, t->proxy->errmsg.len502, t->proxy->errmsg.msg502); + if (!(t->flags & SN_ERR_MASK)) + t->flags |= SN_ERR_SRVCL; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= SN_FINST_H; return 1; } /* end of client write or end of server read. @@ -3233,22 +3260,44 @@ int process_srv(struct session *t) { t->srv_state = SV_STCLOSE; t->logs.status = 504; client_return(t, t->proxy->errmsg.len504, t->proxy->errmsg.msg504); + if (!(t->flags & SN_ERR_MASK)) + t->flags |= SN_ERR_SRVTO; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= SN_FINST_H; return 1; } - /* write timeout, or last client read and buffer empty */ + /* last client read and buffer empty */ /* FIXME!!! here, we don't want to switch to SHUTW if the * client shuts read too early, because we may still have * some work to do on the headers. + * The side-effect is that if the client completely closes its + * connection during SV_STHEADER, the connection to the server + * is kept until a response comes back or the timeout is reached. */ - else if (((/*c == CL_STSHUTR ||*/ c == CL_STCLOSE) && (req->l == 0)) || - (FD_ISSET(t->srv_fd, StaticWriteEvent) && tv_cmp2_ms(&t->swexpire, &now) <= 0)) { + else if ((/*c == CL_STSHUTR ||*/ c == CL_STCLOSE) && (req->l == 0)) { FD_CLR(t->srv_fd, StaticWriteEvent); tv_eternity(&t->swexpire); shutdown(t->srv_fd, SHUT_WR); t->srv_state = SV_STSHUTW; return 1; } + /* write timeout */ + /* FIXME!!! here, we don't want to switch to SHUTW if the + * client shuts read too early, because we may still have + * some work to do on the headers. + */ + else if (FD_ISSET(t->srv_fd, StaticWriteEvent) && tv_cmp2_ms(&t->swexpire, &now) <= 0) { + FD_CLR(t->srv_fd, StaticWriteEvent); + tv_eternity(&t->swexpire); + shutdown(t->srv_fd, SHUT_WR); + t->srv_state = SV_STSHUTW; + if (!(t->flags & SN_ERR_MASK)) + t->flags |= SN_ERR_SRVTO; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= SN_FINST_H; + return 1; + } if (req->l == 0) { if (FD_ISSET(t->srv_fd, StaticWriteEvent)) { @@ -3281,25 +3330,42 @@ int process_srv(struct session *t) { tv_eternity(&t->swexpire); fd_delete(t->srv_fd); t->srv_state = SV_STCLOSE; + if (!(t->flags & SN_ERR_MASK)) + t->flags |= SN_ERR_SRVCL; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= SN_FINST_D; return 1; } - /* read timeout, last read, or end of client write */ - else if (t->res_sr == RES_NULL || c == CL_STSHUTW || c == CL_STCLOSE - || tv_cmp2_ms(&t->srexpire, &now) <= 0) { + /* last read, or end of client write */ + else if (t->res_sr == RES_NULL || c == CL_STSHUTW || c == CL_STCLOSE) { FD_CLR(t->srv_fd, StaticReadEvent); tv_eternity(&t->srexpire); shutdown(t->srv_fd, SHUT_RD); t->srv_state = SV_STSHUTR; return 1; - } - /* write timeout, or last client read and buffer empty */ - else if (((c == CL_STSHUTR || c == CL_STCLOSE) && (req->l == 0)) - || (tv_cmp2_ms(&t->swexpire, &now) <= 0)) { + /* read timeout */ + else if (tv_cmp2_ms(&t->srexpire, &now) <= 0) { + FD_CLR(t->srv_fd, StaticReadEvent); + tv_eternity(&t->srexpire); + shutdown(t->srv_fd, SHUT_RD); + t->srv_state = SV_STSHUTR; + if (!(t->flags & SN_ERR_MASK)) + t->flags |= SN_ERR_SRVTO; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= SN_FINST_D; + return 1; + } + /* write timeout */ + else if (tv_cmp2_ms(&t->swexpire, &now) <= 0) { FD_CLR(t->srv_fd, StaticWriteEvent); tv_eternity(&t->swexpire); shutdown(t->srv_fd, SHUT_WR); t->srv_state = SV_STSHUTW; + if (!(t->flags & SN_ERR_MASK)) + t->flags |= SN_ERR_SRVTO; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= SN_FINST_D; return 1; } else if (req->l == 0) { @@ -3337,14 +3403,36 @@ int process_srv(struct session *t) { return 0; /* other cases change nothing */ } else if (s == SV_STSHUTR) { - if ((t->res_sw == RES_ERROR) || - ((c == CL_STSHUTR || c == CL_STCLOSE) && (req->l == 0)) || - (tv_cmp2_ms(&t->swexpire, &now) <= 0)) { + if (t->res_sw == RES_ERROR) { //FD_CLR(t->srv_fd, StaticWriteEvent); tv_eternity(&t->swexpire); fd_delete(t->srv_fd); //close(t->srv_fd); t->srv_state = SV_STCLOSE; + if (!(t->flags & SN_ERR_MASK)) + t->flags |= SN_ERR_SRVCL; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= SN_FINST_D; + return 1; + } + else if ((c == CL_STSHUTR || c == CL_STCLOSE) && (req->l == 0)) { + //FD_CLR(t->srv_fd, StaticWriteEvent); + tv_eternity(&t->swexpire); + fd_delete(t->srv_fd); + //close(t->srv_fd); + t->srv_state = SV_STCLOSE; + return 1; + } + else if (tv_cmp2_ms(&t->swexpire, &now) <= 0) { + //FD_CLR(t->srv_fd, StaticWriteEvent); + tv_eternity(&t->swexpire); + fd_delete(t->srv_fd); + //close(t->srv_fd); + t->srv_state = SV_STCLOSE; + if (!(t->flags & SN_ERR_MASK)) + t->flags |= SN_ERR_SRVTO; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= SN_FINST_D; return 1; } else if (req->l == 0) { @@ -3365,14 +3453,36 @@ int process_srv(struct session *t) { return 0; } else if (s == SV_STSHUTW) { - if (t->res_sr == RES_ERROR || t->res_sr == RES_NULL || - c == CL_STSHUTW || c == CL_STCLOSE || - tv_cmp2_ms(&t->srexpire, &now) <= 0) { + if (t->res_sr == RES_ERROR) { //FD_CLR(t->srv_fd, StaticReadEvent); tv_eternity(&t->srexpire); fd_delete(t->srv_fd); //close(t->srv_fd); t->srv_state = SV_STCLOSE; + if (!(t->flags & SN_ERR_MASK)) + t->flags |= SN_ERR_SRVCL; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= SN_FINST_D; + return 1; + } + else if (t->res_sr == RES_NULL || c == CL_STSHUTW || c == CL_STCLOSE) { + //FD_CLR(t->srv_fd, StaticReadEvent); + tv_eternity(&t->srexpire); + fd_delete(t->srv_fd); + //close(t->srv_fd); + t->srv_state = SV_STCLOSE; + return 1; + } + else if (tv_cmp2_ms(&t->srexpire, &now) <= 0) { + //FD_CLR(t->srv_fd, StaticReadEvent); + tv_eternity(&t->srexpire); + fd_delete(t->srv_fd); + //close(t->srv_fd); + t->srv_state = SV_STCLOSE; + if (!(t->flags & SN_ERR_MASK)) + t->flags |= SN_ERR_SRVTO; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= SN_FINST_D; return 1; } else if (rep->l == BUFSIZE) { /* no room to read more data */ @@ -3489,7 +3599,14 @@ int process_chk(struct task *t) { (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)) != -1)) { //fprintf(stderr, "process_chk: 3\n"); - if ((connect(fd, (struct sockaddr *)&s->addr, sizeof(s->addr)) != -1) || (errno == EINPROGRESS)) { + /* allow specific binding */ + if (s->proxy->options & PR_O_BIND_SRC && + bind(fd, (struct sockaddr *)&s->proxy->source_addr, sizeof(s->proxy->source_addr)) == -1) { + Alert("Cannot bind to source address before connect() for proxy %s. Aborting.\n", s->proxy->id); + close(fd); + s->result = -1; + } + else if ((connect(fd, (struct sockaddr *)&s->addr, sizeof(s->addr)) != -1) || (errno == EINPROGRESS)) { /* OK, connection in progress or established */ //fprintf(stderr, "process_chk: 4\n"); @@ -3963,55 +4080,55 @@ int cfg_parse_global(char *file, int linenum, char **args) { } else if (!strcmp(args[0], "uid")) { if (global.uid != 0) { - Alert("parsing [%s:%d] : already specified. Continuing.\n", file, linenum); + Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); return 0; } if (*(args[1]) == 0) { - Alert("parsing [%s:%d] : expects an integer argument.\n", file, linenum); + Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); return -1; } global.uid = atol(args[1]); } else if (!strcmp(args[0], "gid")) { if (global.gid != 0) { - Alert("parsing [%s:%d] : already specified. Continuing.\n", file, linenum); + Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); return 0; } if (*(args[1]) == 0) { - Alert("parsing [%s:%d] : expects an integer argument.\n", file, linenum); + Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); return -1; } global.gid = atol(args[1]); } else if (!strcmp(args[0], "nbproc")) { if (global.nbproc != 0) { - Alert("parsing [%s:%d] : already specified. Continuing.\n", file, linenum); + Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); return 0; } if (*(args[1]) == 0) { - Alert("parsing [%s:%d] : expects an integer argument.\n", file, linenum); + Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); return -1; } global.nbproc = atol(args[1]); } else if (!strcmp(args[0], "maxconn")) { if (global.maxconn != 0) { - Alert("parsing [%s:%d] : already specified. Continuing.\n", file, linenum); + Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); return 0; } if (*(args[1]) == 0) { - Alert("parsing [%s:%d] : expects an integer argument.\n", file, linenum); + Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); return -1; } global.maxconn = atol(args[1]); } else if (!strcmp(args[0], "chroot")) { if (global.chroot != NULL) { - Alert("parsing [%s:%d] : already specified. Continuing.\n", file, linenum); + Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); return 0; } if (*(args[1]) == 0) { - Alert("parsing [%s:%d] : expects a directory as an argument.\n", file, linenum); + Alert("parsing [%s:%d] : '%s' expects a directory as an argument.\n", file, linenum, args[0]); return -1; } global.chroot = strdup(args[1]); @@ -4021,7 +4138,7 @@ int cfg_parse_global(char *file, int linenum, char **args) { int facility, level; if (*(args[1]) == 0 || *(args[2]) == 0) { - Alert("parsing [%s:%d] : expects
and as arguments.\n", file, linenum); + Alert("parsing [%s:%d] : '%s' expects
and as arguments.\n", file, linenum, args[0]); return -1; } @@ -4030,7 +4147,7 @@ int cfg_parse_global(char *file, int linenum, char **args) { break; if (facility >= NB_LOG_FACILITIES) { - 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); } @@ -4039,7 +4156,7 @@ int cfg_parse_global(char *file, int linenum, char **args) { 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]); + Alert("parsing [%s:%d] : unknown optional log level '%s'\n", file, linenum, args[3]); exit(1); } } @@ -4065,7 +4182,7 @@ int cfg_parse_global(char *file, int linenum, char **args) { } else { - Alert("parsing [%s:%d] : unknown keyword <%s> in section\n", file, linenum, args[0]); + Alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section\n", file, linenum, args[0], "global"); return -1; } return 0; @@ -4081,13 +4198,13 @@ int cfg_parse_listen(char *file, int linenum, char **args) { if (!strcmp(args[0], "listen")) { /* new proxy */ if (strchr(args[2], ':') == NULL) { - Alert("parsing [%s:%d] : expects and as arguments.\n", - file, linenum); + Alert("parsing [%s:%d] : '%s' expects and as arguments.\n", + file, linenum, args[0]); return -1; } if ((curproxy = (struct proxy *)calloc(1, sizeof(struct proxy))) == NULL) { - Alert("parsing [%s:%d] : out of memory\n", file, linenum); + Alert("parsing [%s:%d] : out of memory.\n", file, linenum); return -1; } curproxy->next = proxy; @@ -4106,7 +4223,7 @@ int cfg_parse_listen(char *file, int linenum, char **args) { return 0; } else if (curproxy == NULL) { - Alert("parsing [%s:%d] : expected.\n", file, linenum); + Alert("parsing [%s:%d] : 'listen' expected.\n", file, linenum); return -1; } @@ -4115,7 +4232,7 @@ int cfg_parse_listen(char *file, int linenum, char **args) { else if (!strcmp(args[1], "tcp")) curproxy->mode = PR_MODE_TCP; else if (!strcmp(args[1], "health")) curproxy->mode = PR_MODE_HEALTH; else { - Alert("parsing [%s:%d] : unknown proxy mode <%s>.\n", file, linenum, args[1]); + Alert("parsing [%s:%d] : unknown proxy mode '%s'.\n", file, linenum, args[1]); return -1; } } @@ -4131,8 +4248,8 @@ int cfg_parse_listen(char *file, int linenum, char **args) { } if (*(args[1]) == 0) { - Alert("parsing [%s:%d] : expects as argument.\n", - file, linenum); + Alert("parsing [%s:%d] : '%s' expects as argument.\n", + file, linenum, args[0]); return -1; } curproxy->cookie_name = strdup(args[1]); @@ -4156,28 +4273,28 @@ int cfg_parse_listen(char *file, int linenum, char **args) { curproxy->options |= PR_O_COOK_POST; } else { - Alert("parsing [%s:%d] : supports 'rewrite', 'insert', 'indirect', 'nocache' and 'postonly' options.\n", - file, linenum); + Alert("parsing [%s:%d] : '%s' supports 'rewrite', 'insert', 'indirect', 'nocache' and 'postonly' options.\n", + file, linenum, args[0]); return -1; } cur_arg++; } if ((curproxy->options & (PR_O_COOK_RW|PR_O_COOK_IND)) == (PR_O_COOK_RW|PR_O_COOK_IND)) { - Alert("parsing [%s:%d] : 'rewrite' and 'indirect' mode are incompatibles.\n", + Alert("parsing [%s:%d] : cookie 'rewrite' and 'indirect' mode are incompatibles.\n", file, linenum); 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); + Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", + file, linenum, args[0]); return 0; } if (*(args[4]) == 0) { - Alert("parsing [%s:%d] : expects 'cookie' 'len' .\n", - file, linenum); + Alert("parsing [%s:%d] : '%s' expects 'cookie' 'len' .\n", + file, linenum, args[0]); return -1; } curproxy->capture_name = strdup(args[2]); @@ -4191,52 +4308,52 @@ int cfg_parse_listen(char *file, int linenum, char **args) { } else if (!strcmp(args[0], "contimeout")) { /* connect timeout */ if (curproxy->contimeout != 0) { - Alert("parsing [%s:%d] : contimeout already specified. Continuing.\n", file, linenum); + Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); return 0; } if (*(args[1]) == 0) { - Alert("parsing [%s:%d] : expects an integer as argument.\n", - file, linenum); + Alert("parsing [%s:%d] : '%s' expects an integer as argument.\n", + file, linenum, args[0]); return -1; } curproxy->contimeout = atol(args[1]); } else if (!strcmp(args[0], "clitimeout")) { /* client timeout */ if (curproxy->clitimeout != 0) { - Alert("parsing [%s:%d] : clitimeout already specified. Continuing.\n", - file, linenum); + Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", + file, linenum, args[0]); return 0; } if (*(args[1]) == 0) { - Alert("parsing [%s:%d] : expects an integer as argument.\n", - file, linenum); + Alert("parsing [%s:%d] : '%s' expects an integer as argument.\n", + file, linenum, args[0]); return -1; } curproxy->clitimeout = atol(args[1]); } else if (!strcmp(args[0], "srvtimeout")) { /* server timeout */ if (curproxy->srvtimeout != 0) { - Alert("parsing [%s:%d] : srvtimeout already specified. Continuing.\n", file, linenum); + Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); return 0; } if (*(args[1]) == 0) { - Alert("parsing [%s:%d] : expects an integer as argument.\n", - file, linenum); + Alert("parsing [%s:%d] : '%s' expects an integer as argument.\n", + file, linenum, args[0]); return -1; } curproxy->srvtimeout = atol(args[1]); } else if (!strcmp(args[0], "retries")) { /* connection retries */ if (*(args[1]) == 0) { - Alert("parsing [%s:%d] : expects an integer argument (dispatch counts for one).\n", - file, linenum); + Alert("parsing [%s:%d] : '%s' expects an integer argument (dispatch counts for one).\n", + file, linenum, args[0]); return -1; } curproxy->conn_retries = atol(args[1]); } else if (!strcmp(args[0], "option")) { if (*(args[1]) == 0) { - Alert("parsing [%s:%d] :