]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
* released 1.1.0 v1.1.0
authorwilly tarreau <willy@wtap.(none)>
Sat, 17 Dec 2005 11:48:26 +0000 (12:48 +0100)
committerwilly tarreau <willy@wtap.(none)>
Sat, 17 Dec 2005 11:48:26 +0000 (12:48 +0100)
* added OpenBSD, Linux-2.2 and Linux-2.4 targets to the Makefile
* added a Formilux init script
* 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.
* reworked buffer handling to fix a few rewrite bugs, and
  improve overall performance.
* implement the "purge" option to delete server cookies in direct mode.
* fixed some error cases where the maxfd was not decreased.
* now supports transparent proxying, at least on linux 2.4.
* 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().

Makefile
doc/haproxy.txt
examples/cfg
haproxy.c
init.d/haproxy [new file with mode: 0644]

index e0d65d1b86043af8db2fb1654dbbea4bdc18b2c1..ba598430e588db1f5ec91a2d2b56ded7b498a659 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,26 +1,39 @@
 CC = gcc
 LD = gcc
 
-# This is for Linux 2.4
-COPTS.linux = -O2
-LIBS.linux =
+# This is for Linux 2.4 with netfilter
+COPTS.linux24 = -O2 -DNETFILTER
+LIBS.linux24 =
 
-# This is for solaris 8
+# This is for Linux 2.2
+COPTS.linux22 = -O2 -DUSE_GETSOCKNAME
+LIBS.linux22 =
+
+# This is for Solaris 8
 COPTS.solaris = -O2 -fomit-frame-pointer -DSOLARIS -DHAVE_STRLCPY
 LIBS.solaris = -lnsl -lsocket
 
+# This is for OpenBSD 3.0
+COPTS.openbsd = -O2 -DHAVE_STRLCPY
+LIBS.openbsd =
+
 # Select target OS. TARGET must match a system for which COPTS and LIBS are
 # correctly defined above.
-TARGET = linux
+TARGET = linux24
+#TARGET = linux22
 #TARGET = solaris
+#TARGET = openbsd
 
-DEBUG =
-#DEBUG = -g
+#DEBUG =
+DEBUG = -g
 
 COPTS=$(COPTS.$(TARGET))
 LIBS=$(LIBS.$(TARGET))
 
-CFLAGS = -Wall $(COPTS) -DSTATTIME=0
+# - use -DSTATTIME=0 to disable statistics, else specify an interval in
+#   milliseconds.
+# - use -DTRANSPARENT to compile with transparent proxy support.
+CFLAGS = -Wall $(COPTS) $(DEBUG) -DSTATTIME=0 -DTRANSPARENT
 LDFLAGS = -g
 
 all: haproxy
index 36bd2ea3b484488a7a2793c7b026f2a03107b5ff..4f5ec1452ffbc7671d57433c787f23b6606f6e99 100644 (file)
@@ -1,9 +1,9 @@
 
                             H A - P r o x y
                             ---------------
-                             version 1.0.0
+                             version 1.1.0
                              willy tarreau
-                              2001/12/16
+                              2002/03/10
 
 ==============
 |Introduction|
 HA-Proxy est un relais TCP/HTTP offrant des facilités d'intégration en
 environnement hautement disponible. En effet, il est capable de :
   - assurer un aiguillage statique défini par des cookies ;
+  - assurer une répartition de charge avec création de cookies pour
+    assurer la persistence de session ;
   - fournir une visibilité externe de son état de santé ;
   - s'arrêter en douceur sans perte brutale de service.
+  - modifier/ajouter/supprimer des entêtes dans la requête et la réponse.
 
 Il requiert peu de ressources, et son architecture événementielle
 mono-processus lui permet facilement de gérer plusieurs milliers de
@@ -51,7 +54,7 @@ Commentaires
 
 L'analyseur du fichier de configuration ignore des lignes vides, les
 espaces, les tabulations, et tout ce qui est compris entre le symbole
-'#' et la fin de la ligne.
+'#' (s'il n'est pas précédé d'un '\'), et la fin de la ligne.
 
 
 Serveur
@@ -102,7 +105,7 @@ Un serveur peut fonctionner dans trois modes diff
 Mode TCP
 --------
 Dans ce mode, le service relaye, dès leur établissement, les
-connexions TCP vers un unique serveur distant. Aucun traitement n'est
+connexions TCP vers un ou plusieurs serveurs. Aucun traitement n'est
 effectué sur le flux. Il s'agit simplement d'une association
 <adresse_source:port_source> <adresse_destination:port_destination>.
 Pour l'utiliser, préciser le mode TCP sous la déclaration du relais :
@@ -236,6 +239,30 @@ cookie est donn
        mode http
        cookie SERVERID
 
+On peut modifier l'utilisation du cookie pour la rendre plus
+intelligente vis-à-vis des applications relayées. Il est possible,notamment
+de supprimer ou réécrire un cookie retourné par un serveur accédé en direct,
+et d'insérer un cookie dans une réponse HTTP orientée vers un serveur
+sélectionné en répartition de charge.
+
+Pour ne conserver le cookie qu'en accès indirect, donc à travers le
+dispatcheur, et le supprimer lors des accès directs :
+
+       cookie SERVERID indirect
+
+Pour réécrire le nom du serveur dans un cookie lors d'un accès direct :
+
+       cookie SERVERID rewrite
+
+Pour créer un cookie comportant le nom du serveur lors d'un accès en
+répartition de charge interne. Dans ce cas, il est indispensable que tous les
+serveurs aient un cookie renseigné.
+
+       cookie SERVERID insert
+
+Remarque: Il est possible de combiner 'insert' avec 'indirect' ou 'rewrite'
+pour s'adapter à des applications générant déjà le cookie, avec un contenu
+invalide. Il suffit pour cela de les spécifier sur la même ligne.
 
 Assignation d'un serveur à une valeur de cookie
 ===============================================
@@ -243,10 +270,12 @@ Assignation d'un serveur 
 En mode HTTP, il est possible d'associer des serveurs à des valeurs de
 cookie par le paramètre "server". La syntaxe est :
 
-    server <valeur> <adresse_ip>:<port>
+    server <identifiant> <adresse_ip>:<port> cookie <valeur>
 
-<valeur> est la valeur trouvée dans le cookie,
+<identifiant> est un nom quelconque de serveur utilisé pour
+l'identifier dans la configuration (erreurs...).
 <adresse_ip>:<port> le couple adresse-port sur lequel le serveur écoute.
+<valeur> est la valeur trouvée dans le cookie,
 
 Exemple : le cookie SERVERID peut contenir server01 ou server02
 -------
@@ -254,18 +283,64 @@ Exemple : le cookie SERVERID peut contenir server01 ou server02
        mode http
        cookie SERVERID
        dispatch 192.168.1.100:80
-       server server01 192.168.1.1:80
-       server server02 192.168.1.2:80
+       server web1 192.168.1.1:80 cookie server01
+       server web2 192.168.1.2:80 cookie server02
+
+Attention : la syntaxe a changé depuis la version 1.0.
+---------
+
+Répartiteur de charge interne
+=============================
+
+Le relais peut effectuer lui-même la répartition de charge entre les
+différents serveurs décrits pour un service donné, en mode TCP comme
+en mode HTTP. Pour cela, on précise le mot clé 'balance' dans la
+définition du service, éventuellement suivi du nom d'un algorithme de
+répartition. En version 1.1.0, seul 'roundrobin' est géré, et c'est
+aussi la valeur implicite par défaut. Il est évident qu'en cas
+d'utilisation du répartiteur interne, il ne faudra pas spécifier
+d'adresse de dispatch, et qu'il faudra au moins un serveur.
+
+Exemple : même que précédemment en répartition interne
+-------
+
+    listen http_proxy 0.0.0.0:80
+       mode http
+       cookie SERVERID
+       balance roundrobin
+       server web1 192.168.1.1:80 cookie server01
+       server web2 192.168.1.2:80 cookie server02
+
+
+Surveillance des serveurs
+=========================
+
+A cette date, l'état des serveurs n'est testé que par établissement
+de connexion TCP toutes les 2 secondes, avec 3 essais pour déclarer
+un serveur en panne, 2 pour le déclarer utilisable. Un serveur hors
+d'usage ne sera pas utilisé 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.
 
+Exemple : même que précédemment avec surveillance
+-------
 
-Reconnexion vers le répartiteur
-===============================
+    listen http_proxy 0.0.0.0:80
+       mode http
+       cookie SERVERID
+       balance roundrobin
+       server web1 192.168.1.1:80 cookie server01 check
+       server web2 192.168.1.2:80 cookie server02 check
+
+
+Reconnexion vers un répartiteur en cas d'échec direct
+=====================================================
 
 En mode HTTP, si un serveur défini par un cookie ne répond plus, les
 clients seront définitivement aiguillés dessus à cause de leur cookie,
 et de ce fait, définitivement privés de service. La spécification du
-paramètre "redisp" autorise dans ce cas à renvoyer les connexions
-échouées vers l'adresse de répartition (dispatch) afin d'assigner un
+paramètre "redispatch" autorise dans ce cas à renvoyer les connexions
+échouées vers le répartiteur (externe ou interne) afin d'assigner un
 nouveau serveur à ces clients.
 
 Exemple :
@@ -274,9 +349,31 @@ Exemple :
        mode http
        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.
+
+Fonctionnement en mode transparent
+==================================
+
+En mode HTTP, le mot clé "transparent" permet d'intercepter des
+sessions routées à travers la machine hébergeant le proxy. Dans
+ce mode, on ne précise pas l'adresse de répartition "dispatch",
+car celle-ci est tirée de l'adresse destination de la session
+détournée. Le système doit permettre de rediriger les paquets
+vers un processus local.
+
+Exemple :
+-------
+    listen http_proxy 0.0.0.0:65000
+       mode http
+       transparent
+       cookie SERVERID
        server server01 192.168.1.1:80
        server server02 192.168.1.2:80
-       redisp # renvoyer vers dispatch si serveur HS.
+
+    # iptables -t nat -A PREROUTING -i eth0 -p tcp -d 192.168.1.100 \
+      --dport 80 -j REDIRECT --to-ports 65000
 
 Journalisation des connexions
 =============================
@@ -307,48 +404,91 @@ Les cat
     local0, local1, local2, local3, local4, local5, local6, local7
 
 
-Remplacement d'entêtes par expressions régulières
-=================================================
+Modification des entêtes HTTP
+=============================
 
 En mode HTTP uniquement, il est possible de remplacer certains entêtes
-client et/ou serveur à partir d'expressions régulières. Deux
-limitations cependant :
-  - il n'est pas encore possible de supprimer un entête ni d'en
-    ajouter un ; On peut en général s'en sortir avec des
-    modifications.
-  - les entêtes fournis au milieu de connexions persistentes
-    (keep-alive) ne sont pas vus.
+dans la requête et/ou la réponse à partir d'expressions régulières. Une
+limitation cependant : les entêtes fournis au milieu de connexions
+persistentes (keep-alive) ne sont pas vus. Les données ne sont pas
+affectées, ceci ne s'applique qu'aux entêtes.
 
 La syntaxe est :
-   cliexp <search> <replace>   pour les entêtes client
-   srvexp <search> <replace>   pour les entêtes serveur
+   reqadd <string>             pour ajouter un entête dans la requête
+   reqrep <search> <replace>   pour modifier la requête
+   reqrep <search>             pour supprimer un entête dans la requête
+
+   rspadd <string>             pour ajouter un entête dans la réponse
+   rsprep <search> <replace>   pour modifier la réponse
+   rsprep <search>             pour supprimer un entête dans la réponse
+
 
 <search> 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.
+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
+  \xXX pour un caractère spécifique XX (comme en C)
+
+
+<replace> contient la chaîne remplaçant la portion vérifiée par l'expression.
+Elle peut inclure les caractères spéciaux ci-dessus, faire référence à un
+groupe délimité par des parenthèses dans l'expression régulière, par sa
+position numérale. Les positions vont de 1 à 9, et sont codées par un '\'
+suivi du chiffre désiré. Il est également possible d'insérer un caractère non
+imprimable (utile pour le saut de ligne) inscrivant '\x' suivi du code
+hexadécimal de ce caractère (comme en C).
+
+<string> représente une chaîne qui sera ajoutée systématiquement après la
+dernière ligne d'entête.
+
+Remarques :
+---------
+  - la première ligne de la requête et celle de la réponse sont traitées comme
+    des entêtes, ce qui permet de réécrire des URL et des codes d'erreur.
+  - 'reqrep' est l'équivalent de 'cliexp' en version 1.0, et 'rsprep' celui de
+    'srvexp'. Ces noms sont toujours supportés mais déconseillés.
+  - pour des raisons de performances, le nombre total de caractères ajoutés sur
+    une requête ou une réponse est limité à 256. Cette valeur est modifiable dans
+    le code. Pour un usage temporaire, on peut gagner de la place en supprimant
+    quelques entêtes inutiles avant les ajouts.
 
-<replace> contient la chaîne remplaçant la portion vérifiée par
-l'expression. Elle peut inclure des espaces et tabulations précédés
-par un '\', faire référence à un groupe délimité par des parenthèses
-dans l'expression régulière, par sa position numérale. Les positions
-vont de 1 à 9, et sont codées par un '\' suivi du chiffre désiré. Il
-est également possible d'insérer un caractère non imprimable (utile
-pour le saut de ligne) inscrivant '\x' suivi du code hexadécimal de ce
-caractère (comme en C).
+Exemples :
+--------
+       reqrep  ^(GET.*)(.free.fr)(.*) \1.online.fr\3
+       reqrep  ^(POST.*)(.free.fr)(.*) \1.online.fr\3
+       reqrep  ^Proxy-Connection:.*    Proxy-Connection:\ close
+       rsprep  ^Server:.* Server:\ Tux-2.0
+       rsprep  ^(Location:\ )([^:]*://[^/]*)(.*) \1\3
+       rspdel  ^Connection:.*
+       rspadd  Connection:\ close
 
-Remarque : la première ligne de la requête et celle de la réponse sont
-traitées comme des entêtes, ce qui permet de réécrire des URL et des
-codes d'erreur.
 
-Exemples :
-----------
-       cliexp  ^(GET.*)(.free.fr)(.*) \1.online.fr\3
-       cliexp  ^(POST.*)(.free.fr)(.*) \1.online.fr\3
-       cliexp  ^Proxy-Connection:.*    Proxy-Connection:\ close
-       srvexp  ^Proxy-Connection:.*    Proxy-Connection:\ close
-       srvexp  ^(Location:\ )([^:]*://[^/]*)(.*) \1\3
+Répartition avec persistence
+============================
+
+La combinaison de l'insertion de cookie avec la répartition de charge interne
+permet d'assurer une persistence dans les sessions HTTP d'une manière
+pratiquement transparente pour les applications. Le principe est simple :
+  - assigner un cookie à chaque serveur
+  - effectuer une répartition interne
+  - insérer un cookie dans les réponses issues d'une répartition
 
+Exemple :
+-------
+    listen application 0.0.0.0:80
+       mode http
+       cookie SERVERID insert indirect
+       balance roundrobin
+       server 192.168.1.1:80 cookie server01 check
+       server 192.168.1.2:80 cookie server02 check
 
 =====================
 |Paramétrage système|
@@ -360,13 +500,15 @@ Sous Linux 2.4
 echo 131072 > /proc/sys/fs/file-max
 echo 65536 > /proc/sys/net/ipv4/ip_conntrack_max
 echo 1024 60999 > /proc/sys/net/ipv4/ip_local_port_range
-echo 16384 > /proc/sys/net/ipv4/ip_queue_maxlen
+echo 32768 > /proc/sys/net/ipv4/ip_queue_maxlen
 echo 60 > /proc/sys/net/ipv4/tcp_fin_timeout
-echo 4096 > /proc/sys/net/ipv4/tcp_max_orphans
+echo 262144 > /proc/sys/net/ipv4/tcp_max_orphans
 echo 16384 > /proc/sys/net/ipv4/tcp_max_syn_backlog
 echo 262144 > /proc/sys/net/ipv4/tcp_max_tw_buckets
 echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle
 echo 0 > /proc/sys/net/ipv4/tcp_timestamps
-ulimit -n 65536
+echo 0 > /proc/sys/net/ipv4/tcp_sack
+echo 0 > /proc/sys/net/ipv4/tcp_ecn
+ulimit -n 131072
 
 -- fin --
index 053648f5b248eb5b8f24a3b5a7cdd64bb5d955ff..6aed525edbc27e1c8142aa7b831c83cb9460c35c 100644 (file)
@@ -1,20 +1,52 @@
-listen proxy1 0.0.0.0:3128
+listen proxy1 0.0.0.0:8000
        mode    http
-        cookie SERVERID
-       dispatch 192.168.12.1:80
-       server srv1 192.168.12.2:8080
-       server srv2 192.168.12.3:8080
+       #mode   tcp
+        cookie SERVERID insert indirect
+       balance roundrobin
+       #dispatch 127.0.0.1:3130
+       #dispatch 127.0.0.1:31300
+       #dispatch 127.0.0.1:80
+       #dispatch 127.0.0.1:22
+       server tuxlocal 127.0.0.1:80 cookie cookie1 check
+       server tuxceleron 10.101.0.1:80 cookie cookie2 check
+       #server telnet 127.0.0.1:23
+       #server ssh 127.0.0.1:22
+       server local 127.0.0.1:3130 cookie cookie3 check
+       #server local 127.0.0.1:3130
+       #server celeron 10.101.0.1:80 cookie srv1
+       #server celeron 10.101.0.1:31300
+       #server local 10.101.23.9:31300
        contimeout      3000
        clitimeout      150000
        srvtimeout      150000
        maxconn 60000
-       redisp
+       redispatch
+       retries 3
+       grace 3000
+       #rsprep ^Server.* Server:\ IIS
+       #rspdel ^Server.*
+       #rspadd Set-Cookie:\ mycookie=0;\ path=/
+       #rsprep ^(Date:\ )([^,]*)(,\ )(.*) LaDate\ est:\ \4\ (\2)
+       
+listen proxy1 0.0.0.0:3128
+       mode    http
+        cookie SERVERID indirect
+       dispatch 127.0.0.1:8080
+       server srv1 127.0.0.1:8080
+       #server srv2 192.168.12.3:8080
+       contimeout      3000
+       clitimeout      450000
+       srvtimeout      450000
+       maxconn 60000
+       redispatch
        retries 3
        grace 3000
+       
 
 listen proxy2 0.0.0.0:3129
        mode    http
-       dispatch 127.0.0.1:80
+       transparent
+#      dispatch 127.0.0.1:80
        contimeout      3000
        clitimeout      150000
        srvtimeout      150000
@@ -35,5 +67,13 @@ listen health 0.0.0.0:3130
        mode    health
        clitimeout      1500
        srvtimeout      1500
-       maxconn 4
+       maxconn 6000
+       grace 0
+
+
+listen health 0.0.0.0:31300
+       mode    health
+       clitimeout      1500
+       srvtimeout      1500
+       maxconn 6000
        grace 0
index dbbef4326284b840b96c3e9428fdd99bc139e9a6..f1e183bc907b2ca6371483920fa02faf2b0fb770 100644 (file)
--- a/haproxy.c
+++ b/haproxy.c
@@ -1,6 +1,6 @@
 /*
- * HA-Proxy : High Availability-enabled HTTP/TCP proxy - Willy Tarreau
- * willy AT meta-x DOT org.
+ * HA-Proxy : High Availability-enabled HTTP/TCP proxy
+ * 2000-2002 - 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
@@ -9,7 +9,29 @@
  *
  * ChangeLog :
  *
- * 2001/12/30 : release of version 1.0.2 : no fixed a bug in header processing
+ * 2002/03/10
+ *   - released 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.
  * 2000/11/28 : major rewrite
  * 2000/11/26 : first write
  *
- * TODO: handle properly intermediate incomplete server headers.
+ * TODO:
+ *   - handle properly intermediate incomplete server headers. Done ?
+ *   - log proxies start/stop
+ *   - handle hot-reconfiguration
  *
  */
 
 #include <time.h>
 #include <regex.h>
 #include <syslog.h>
+#if defined(TRANSPARENT) && defined(NETFILTER)
+#include <linux/netfilter_ipv4.h>
+#endif
 
-#define HAPROXY_VERSION "1.0.2"
-#define HAPROXY_DATE   "2001/12/30"
+#define HAPROXY_VERSION "1.1.0"
+#define HAPROXY_DATE   "2002/03/10"
 
 /* this is for libc5 for example */
 #ifndef TCP_NODELAY
 // reserved buffer space for header rewriting
 #define        MAXREWRITE      256
 
+// max # args on a configuration line
+#define MAX_LINE_ARGS  10
+
 // max # of regexps per proxy
 #define        MAX_REGEXP      10
 
 // max # of matches per regexp
 #define        MAX_MATCH       10
 
+/* FIXME: serverid_len and cookiename_len are no longer checked in configuration file */
 #define COOKIENAME_LEN 16
 #define SERVERID_LEN   16
 #define CONN_RETRIES   3
 
+/* FIXME: this should be user-configurable */
+#define        CHK_CONNTIME    2000
+#define        CHK_INTERVAL    2000
+#define FALLTIME       3
+#define RISETIME       2
+
 /* how many bits are needed to code the size of an int (eg: 32bits -> 5) */
 #define        INTBITS         5
 
 #define STATTIME       2000
 #endif
 
+/* this reduces the number of calls to select() by choosing appropriate
+ * sheduler precision in milliseconds. It should be near the minimum
+ * time that is needed by select() to collect all events. All timeouts
+ * are rounded up by adding this value prior to pass it to select().
+ */
+#define SCHEDULER_RESOLUTION   9
+
 #define MINTIME(old, new)      (((new)<0)?(old):(((old)<0||(new)<(old))?(new):(old)))
 #define SETNOW(a)              (*a=now)
 
@@ -157,39 +202,56 @@ int strlcpy(char *dst, const char *src, int size) {
 #define pool_free(type, ptr) (free(ptr));
 #endif /* MEM_OPTIM */
 
-#define sizeof_session sizeof(struct task)
+#define sizeof_task    sizeof(struct task)
+#define sizeof_session sizeof(struct session)
 #define sizeof_buffer  sizeof(struct buffer)
 #define sizeof_fdtab   sizeof(struct fdtab)
 #define sizeof_str256  256
 
 
-/*
- * different possible states for the sockets
- */
+/* different possible states for the sockets */
 #define FD_STCLOSE     0
 #define FD_STLISTEN    1
 #define FD_STCONN      2
 #define FD_STREADY     3
 #define FD_STERROR     4
 
+/* values for task->state */
 #define TASK_IDLE      0
 #define TASK_RUNNING   1
 
+/* values for proxy->state */
 #define PR_STNEW       0
 #define PR_STIDLE      1
 #define PR_STRUN       2
 #define PR_STDISABLED  3
 
+/* values for proxy->mode */
 #define PR_MODE_TCP    0
 #define PR_MODE_HTTP   1
 #define PR_MODE_HEALTH 2
 
+/* bits for proxy->options */
+#define PR_O_REDISP    1       /* allow reconnection to dispatch in case of errors */
+#define PR_O_TRANSP    2       /* transparent mode : use original DEST as dispatch */
+#define PR_O_COOK_RW   4       /* rewrite all direct cookies with the right serverid */
+#define PR_O_COOK_IND  8       /* keep only indirect cookies */
+#define PR_O_COOK_INS  16      /* insert cookies when not accessing a server directly */
+#define PR_O_COOK_ANY  (PR_O_COOK_RW | PR_O_COOK_IND | PR_O_COOK_INS)
+#define PR_O_BALANCE_RR        32      /* balance in round-robin mode */
+#define PR_O_BALANCE   (PR_O_BALANCE_RR)
+
+/* various task flags */
+#define TF_DIRECT      1       /* connection made on the server matching the client cookie */
+
+/* different possible states for the client side */
 #define CL_STHEADERS   0
 #define CL_STDATA      1
 #define CL_STSHUTR     2
 #define CL_STSHUTW     3
 #define CL_STCLOSE     4
 
+/* different possible states for the server side */
 #define SV_STIDLE      0
 #define SV_STCONN      1
 #define SV_STHEADERS   2
@@ -204,11 +266,15 @@ int strlcpy(char *dst, const char *src, int size) {
 #define        RES_NULL        2       /* result is 0 (read == 0), or connect without need for writing */
 #define RES_ERROR      3       /* result -1 or error on the socket (eg: connect()) */
 
-/* modes of operation */
+/* modes of operation (global variable "mode") */
 #define        MODE_DEBUG      1
 #define        MODE_STATS      2
 #define        MODE_LOG        4
 #define        MODE_DAEMON     8
+#define        MODE_QUIET      16
+
+/* server flags */
+#define SRV_RUNNING    1
 
 /*********************************************************************/
 
@@ -229,15 +295,30 @@ struct buffer {
 
 struct server {
     struct server *next;
-    char *id;                          /* the id found in the cookie */
+    int state;                         /* server state (SRV_*) */
+    int  cklen;                                /* the len of the cookie, to speed up checks */
+    char *cookie;                      /* the id set in the cookie */
+    char *id;                          /* just for identification */
     struct sockaddr_in addr;           /* the address to connect to */
+    int health;                                /* 0->rise-1 = bad; rise->rise+fall-1 = good */
+    int result;                                /* 0 = connect OK, -1 = connect KO */
+    int curfd;                         /* file desc used for current test, or -1 if not in test */
 };
 
+/* The base for all tasks */
 struct task {
     struct task *next, *prev;          /* chaining ... */
     struct task *rqnext;               /* chaining in run queue ... */
+    struct task *wq;                   /* the wait queue this task is in */
     int state;                         /* task state : IDLE or RUNNING */
     struct timeval expire;             /* next expiration time for this task, use only for fast sorting */
+    int (*process)(struct task *t);    /* the function which processes the task */
+    void *context;                     /* the task's context */
+};
+
+/* WARNING: if new fields are added, they must be initialized in event_accept() */
+struct session {
+    struct task *task;                 /* the task associated with this session */
     /* application specific below */
     struct timeval crexpire;           /* expiration date for a client read  */
     struct timeval cwexpire;           /* expiration date for a client write */
@@ -251,12 +332,12 @@ struct task {
     int cli_state;                     /* state of the client side */
     int srv_state;                     /* state of the server side */
     int conn_retries;                  /* number of connect retries left */
-    int conn_redisp;                   /* allow reconnection to dispatch in case of errors */
+    int flags;                         /* some flags describing the session */
     struct buffer *req;                        /* request buffer */
     struct buffer *rep;                        /* response buffer */
     struct sockaddr_in cli_addr;       /* the client address */
     struct sockaddr_in srv_addr;       /* the address to connect to */
-    char cookie_val[SERVERID_LEN+1];   /* the cookie value, if present */
+    struct server *srv;                        /* the server being used */
 };
 
 struct proxy {
@@ -264,7 +345,8 @@ struct proxy {
     int state;                         /* proxy state */
     struct sockaddr_in listen_addr;    /* the address we listen to */
     struct sockaddr_in dispatch_addr;  /* the default address to connect to */
-    struct server *srv;                        /* known servers */
+    struct server *srv, *cursrv;       /* known servers, current server */
+    int nbservers;                     /* # of servers */
     char *cookie_name;                 /* name of the cookie to look for */
     int clitimeout;                    /* client I/O timeout (in milliseconds) */
     int srvtimeout;                    /* server I/O timeout (in milliseconds) */
@@ -272,18 +354,17 @@ struct proxy {
     char *id;                          /* proxy id */
     int nbconn;                                /* # of active sessions */
     int maxconn;                       /* max # of active sessions */
-    int conn_retries;                  /* number of connect retries left */
-    int conn_redisp;                   /* allow to reconnect to dispatch in case of errors */
-    int mode;                          /* mode = PR_MODE_TCP or PR_MODE_HTTP */
-    struct task task;                  /* active sessions (bi-dir chaining) */
-    struct task *rq;                   /* sessions in the run queue (unidir chaining) */
+    int conn_retries;                  /* maximum number of connect retries */
+    int options;                       /* PR_O_REDISP, PR_O_TRANSP */
+    int mode;                          /* mode = PR_MODE_TCP, PR_MODE_HTTP or PR_MODE_HEALTH */
     struct proxy *next;
     struct sockaddr_in logsrv1, logsrv2; /* 2 syslog servers */
     char logfac1, logfac2;             /* log facility for both servers. -1 = disabled */
     struct timeval stop_time;          /* date to stop listening, when stopping != 0 */
-    int nb_cliexp, nb_srvexp;
-    struct hdr_exp cli_exp[MAX_REGEXP];        /* regular expressions for client headers */
-    struct hdr_exp srv_exp[MAX_REGEXP];        /* regular expressions for server headers */
+    int nb_reqexp, nb_rspexp, nb_reqadd, nb_rspadd;
+    struct hdr_exp req_exp[MAX_REGEXP];        /* regular expressions for request headers */
+    struct hdr_exp rsp_exp[MAX_REGEXP];        /* regular expressions for response headers */
+    char *req_add[MAX_REGEXP], *rsp_add[MAX_REGEXP]; /* headers to be added */
     int grace;                         /* grace time after stop request */
 };
 
@@ -313,10 +394,16 @@ fd_set    *ReadEvent,
 void **pool_session = NULL,
     **pool_buffer   = NULL,
     **pool_fdtab    = NULL,
-    **pool_str256   = NULL;
+    **pool_str256   = NULL,
+    **pool_task            = NULL;
 
 struct proxy *proxy  = NULL;   /* list of all existing proxies */
 struct fdtab *fdtab = NULL;    /* array of all the file descriptors */
+struct task *rq = NULL;                /* global run queue */
+struct task wait_queue = {     /* global wait queue */
+    prev:LIST_HEAD(wait_queue),
+    next:LIST_HEAD(wait_queue)
+};
 
 static int mode = 0;           /* MODE_DEBUG, ... */
 static int totalconn = 0;      /* total # of terminated sessions */
@@ -376,6 +463,7 @@ int event_cli_read(int fd);
 int event_cli_write(int fd);
 int event_srv_read(int fd);
 int event_srv_write(int fd);
+int process_session(struct task *t);
 
 /*********************************************************************/
 /*  general purpose functions  ***************************************/
@@ -383,7 +471,7 @@ int event_srv_write(int fd);
 
 void display_version() {
     printf("HA-Proxy version " HAPROXY_VERSION " " HAPROXY_DATE"\n");
-    printf("Copyright 2000-2001 Willy Tarreau <willy AT meta-x DOT org>\n\n");
+    printf("Copyright 2000-2002 Willy Tarreau <willy AT meta-x DOT org>\n\n");
 }
 
 /*
@@ -403,7 +491,8 @@ void usage(char *name) {
            "        -s enables statistics output\n"
            "        -l enables long statistics format\n"
 #endif
-           "        -D goes daemon\n"
+           "        -D goes daemon ; implies -q\n"
+           "        -q quiet mode : don't display messages\n"
            "        -n sets the maximum total # of connections (%d)\n"
            "        -N sets the default, per-proxy maximum # of connections (%d)\n\n",
            name, cfg_maxconn, cfg_maxpconn);
@@ -419,15 +508,17 @@ void Alert(char *fmt, ...) {
     struct timeval tv;
     struct tm *tm;
 
-    va_start(argp, fmt);
+    if (!(mode & MODE_QUIET)) {
+       va_start(argp, fmt);
 
-    gettimeofday(&tv, NULL);
-    tm=localtime(&tv.tv_sec);
-    fprintf(stderr, "[ALERT] %03d/%02d%02d%02d (%d) : ",
-           tm->tm_yday, tm->tm_hour, tm->tm_min, tm->tm_sec, getpid());
-    vfprintf(stderr, fmt, argp);
-    fflush(stderr);
-    va_end(argp);
+       gettimeofday(&tv, NULL);
+       tm=localtime(&tv.tv_sec);
+       fprintf(stderr, "[ALERT] %03d/%02d%02d%02d (%d) : ",
+               tm->tm_yday, tm->tm_hour, tm->tm_min, tm->tm_sec, getpid());
+       vfprintf(stderr, fmt, argp);
+       fflush(stderr);
+       va_end(argp);
+    }
 }
 
 
@@ -439,15 +530,31 @@ void Warning(char *fmt, ...) {
     struct timeval tv;
     struct tm *tm;
 
-    va_start(argp, fmt);
+    if (!(mode & MODE_QUIET)) {
+       va_start(argp, fmt);
 
-    gettimeofday(&tv, NULL);
-    tm=localtime(&tv.tv_sec);
-    fprintf(stderr, "[WARNING] %03d/%02d%02d%02d (%d) : ",
-           tm->tm_yday, tm->tm_hour, tm->tm_min, tm->tm_sec, getpid());
-    vfprintf(stderr, fmt, argp);
-    fflush(stderr);
-    va_end(argp);
+       gettimeofday(&tv, NULL);
+       tm=localtime(&tv.tv_sec);
+       fprintf(stderr, "[WARNING] %03d/%02d%02d%02d (%d) : ",
+               tm->tm_yday, tm->tm_hour, tm->tm_min, tm->tm_sec, getpid());
+       vfprintf(stderr, fmt, argp);
+       fflush(stderr);
+       va_end(argp);
+    }
+}
+
+/*
+ * Displays the message on <out> only if quiet mode is not set.
+ */
+void qfprintf(FILE *out, char *fmt, ...) {
+    va_list argp;
+
+    if (!(mode & MODE_QUIET)) {
+       va_start(argp, fmt);
+       vfprintf(out, fmt, argp);
+       fflush(out);
+       va_end(argp);
+    }
 }
 
 
@@ -743,11 +850,14 @@ static inline struct timeval *tv_min(struct timeval *tvmin,
 
 
 
-/* deletes an FD from the fdsets, and recomputes the maxfd limit */
+/* Deletes an FD from the fdsets, and recomputes the maxfd limit.
+ * The file descriptor is also closed.
+ */
 static inline void fd_delete(int fd) {
-    fdtab[fd].state = FD_STCLOSE;
     FD_CLR(fd, StaticReadEvent);
     FD_CLR(fd, StaticWriteEvent);
+    close(fd);
+    fdtab[fd].state = FD_STCLOSE;
 
     while ((maxfd-1 >= 0) && (fdtab[maxfd-1].state == FD_STCLOSE))
            maxfd--;
@@ -763,58 +873,52 @@ static inline void fd_insert(int fd) {
 /*   task management   ***************************************/
 /*************************************************************/
 
-/* puts the task <s> in <p>'s run queue, and returns <s> */
-static inline struct task *task_wakeup(struct proxy *p, struct task *s) {
-    //    fprintf(stderr,"task_wakeup: proxy %p, task %p\n", p, s);
-
-    if (s->state == TASK_RUNNING)
-       return s;
+/* puts the task <t> in run queue <q>, and returns <t> */
+static inline struct task *task_wakeup(struct task **q, struct task *t) {
+    if (t->state == TASK_RUNNING)
+       return t;
     else {
-       s->rqnext = p->rq;
-       s->state = TASK_RUNNING;
-       return p->rq = s;
+       t->rqnext = *q;
+       t->state = TASK_RUNNING;
+       return *q = t;
     }
 }
 
-/* removes the task <s> from <p>'s run queue.
- * <s> MUST be <p>'s first task in the queue.
+/* removes the task <t> from the queue <q>
+ * <s> MUST be <q>'s first task.
  * set the run queue to point to the next one, and return it
  */
-static inline struct task *task_sleep(struct proxy *p, struct task *s) {
-    if (s->state == TASK_RUNNING) {
-       p->rq = s->rqnext;
-       s->state = TASK_IDLE; /* tell that s has left the run queue */
+static inline struct task *task_sleep(struct task **q, struct task *t) {
+    if (t->state == TASK_RUNNING) {
+       *q = t->rqnext;
+       t->state = TASK_IDLE; /* tell that s has left the run queue */
     }
-    return p->rq; /* return next running task */
+    return *q; /* return next running task */
 }
 
 /*
- * removes the task <s> from its wait queue. It must have already been removed
+ * removes the task <t> from its wait queue. It must have already been removed
  * from the run queue. A pointer to the task itself is returned.
  */
-static inline struct task *task_delete(struct task *s) {
-    s->prev->next = s->next;
-    s->next->prev = s->prev;
-    return s;
+static inline struct task *task_delete(struct task *t) {
+    t->prev->next = t->next;
+    t->next->prev = t->prev;
+    return t;
 }
 
 /*
- * frees  the context associated to a task. It must have been removed first.
+ * frees a task. Its context must have been freed since it will be lost.
  */
 static inline void task_free(struct task *t) {
-    if (t->req)
-       pool_free(buffer, t->req);
-    if (t->rep)
-       pool_free(buffer, t->rep);
-    pool_free(session, t);
+    pool_free(task, t);
 }
 
-/* inserts <task> into the list <list>, where it may already be. In this case, it
+/* inserts <task> into its assigned wait queue, where it may already be. In this case, it
  * may be only moved or left where it was, depending on its timing requirements.
  * <task> is returned.
  */
-
-struct task *task_queue(struct task *list, struct task *task) {
+struct task *task_queue(struct task *task) {
+    struct task *list = task->wq;
     struct task *start_from;
 
     /* first, test if the task was already in a list */
@@ -884,49 +988,102 @@ struct task *task_queue(struct task *list, struct task *task) {
 /* some prototypes */
 static int maintain_proxies(void);
 
+/* this either returns the sockname or the original destination address. Code
+ * inspired from Patrick Schaaf's example of nf_getsockname() implementation.
+ */
+static int get_original_dst(int fd, struct sockaddr_in *sa, int *salen) {
+#if defined(TRANSPARENT) && defined(SO_ORIGINAL_DST)
+    return getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, (void *)sa, salen);
+#else
+#if defined(TRANSPARENT) && defined(USE_GETSOCKNAME)
+    return getsockname(fd, (struct sockaddr *)sa, salen);
+#else
+    return -1;
+#endif
+#endif
+}
+
+/*
+ * frees  the context associated to a session. It must have been removed first.
+ */
+static inline void session_free(struct session *s) {
+    if (s->req)
+       pool_free(buffer, s->req);
+    if (s->rep)
+       pool_free(buffer, s->rep);
+    pool_free(session, s);
+}
+
 
 /*
- * This function initiates a connection to the server whose name is in <s->proxy->src->id>,
- * or the dispatch server if <id> not found. It returns 0 if
+ * 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
  * it's OK, -1 if it's impossible.
  */
-int connect_server(struct task *s, int usecookie) {
-    struct server *srv = s->proxy->srv;
-    char *sn = s->cookie_val;
+int connect_server(struct session *s) {
     int one = 1;
     int fd;
 
     //    fprintf(stderr,"connect_server : s=%p\n",s);
 
-    if (usecookie) {
-       while (*sn && srv && strcmp(sn, srv->id)) {
-           srv = srv->next;
-       }
-       if (!srv || !*sn) { /* server not found, let's use the dispatcher */
-           s->srv_addr = s->proxy->dispatch_addr;
-       }
-       else {
-           s->srv_addr = srv->addr;
+    if (s->flags & TF_DIRECT) { /* srv cannot be null */
+       s->srv_addr = s->srv->addr;
+    }
+    else if (s->proxy->options & PR_O_BALANCE) {
+       if (s->proxy->options & PR_O_BALANCE_RR) {
+           int retry = s->proxy->nbservers;
+           do {
+               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;
+           } while (retry--);
+
+           if (retry == 0) /* no server left */
+               return -1;
+
+           s->srv = s->proxy->cursrv;
+           s->srv_addr = s->srv->addr;
+           s->proxy->cursrv = s->proxy->cursrv->next;
        }
+       else /* unknown balancing algorithm */
+           return -1;
     }
-    else
+    else if (*(int *)&s->proxy->dispatch_addr) {
+       /* connect to the defined dispatch addr */
        s->srv_addr = s->proxy->dispatch_addr;
+    }
+    else if (s->proxy->options & PR_O_TRANSP) {
+       /* in transparent mode, use the original dest addr if no dispatch specified */
+       int salen = sizeof(struct sockaddr_in);
+       if (get_original_dst(s->cli_fd, &s->srv_addr, &salen) == -1) {
+           qfprintf(stderr, "Cannot get original server address.\n");
+           return -1;
+       }
+    }
 
     if ((fd = s->srv_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
-       fprintf(stderr,"Cannot get a server socket.\n");
+       qfprintf(stderr, "Cannot get a server socket.\n");
        return -1;
     }
        
+    if (fd >= cfg_maxsock) {
+       Alert("socket(): not enough free sockets. Raise -n argument. Giving up.\n");
+       close(fd);
+       return -1;
+    }
+
     if ((fcntl(fd, F_SETFL, O_NONBLOCK)==-1) ||
        (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)) == -1)) {
-       fprintf(stderr,"Cannot set client socket to non blocking mode.\n");
+       qfprintf(stderr,"Cannot set client socket to non blocking mode.\n");
        close(fd);
        return -1;
     }
 
     if ((connect(fd, (struct sockaddr *)&s->srv_addr, sizeof(s->srv_addr)) == -1) && (errno != EINPROGRESS)) {
        if (errno == EAGAIN) { /* no free ports left, try again later */
-           fprintf(stderr,"Cannot connect, no free ports.\n");
+           qfprintf(stderr,"Cannot connect, no free ports.\n");
            close(fd);
            return -1;
        }
@@ -936,7 +1093,7 @@ int connect_server(struct task *s, int usecookie) {
        }
     }
 
-    fdtab[fd].owner = s;
+    fdtab[fd].owner = s->task;
     fdtab[fd].read  = &event_srv_read;
     fdtab[fd].write = &event_srv_write;
     fdtab[fd].state = FD_STCONN; /* connection in progress */
@@ -957,74 +1114,86 @@ int connect_server(struct task *s, int usecookie) {
  * It returns 0.
  */
 int event_cli_read(int fd) {
-    struct task *s = fdtab[fd].owner;
+    struct task *t = fdtab[fd].owner;
+    struct session *s = t->context;
     struct buffer *b = s->req;
     int ret, max;
 
     //    fprintf(stderr,"event_cli_read : fd=%d, s=%p\n", fd, s);
 
-    if (b->l == 0) { /* let's realign the buffer to optimize I/O */
-       b->r = b->w = b->h = b->lr  = b->data;
-       max = BUFSIZE - MAXREWRITE;
-    }
-    else if (b->r > b->w) {
-       max = b->data + BUFSIZE - MAXREWRITE - b->r;
-    }
-    else {
-       max = b->w - b->r;
-       if (max > BUFSIZE - MAXREWRITE)
-           max = BUFSIZE - MAXREWRITE;
-    }
-
-    if (max == 0) {
-       FD_CLR(fd, StaticReadEvent);
-       //fprintf(stderr, "cli_read(%d) : max=%d, d=%p, r=%p, w=%p, l=%d\n",
-       //fd, max, b->data, b->r, b->w, b->l);
-       return 0;
-    }
-
     if (fdtab[fd].state != FD_STERROR) {
+       while (1) {
+           if (b->l == 0) { /* let's realign the buffer to optimize I/O */
+               b->r = b->w = b->h = b->lr  = b->data;
+               max = BUFSIZE - MAXREWRITE;
+           }
+           else if (b->r > b->w) {
+               max = b->data + BUFSIZE - MAXREWRITE - b->r;
+           }
+           else {
+               max = b->w - b->r;
+               if (max > BUFSIZE - MAXREWRITE)
+                   max = BUFSIZE - MAXREWRITE;
+           }
+           
+           if (max == 0) {  /* not anymore room to store data */
+               FD_CLR(fd, StaticReadEvent);
+               break;;
+           }
+           
 #ifndef MSG_NOSIGNAL
-       int skerr, lskerr;
-       lskerr=sizeof(skerr);
-       getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
-       if (skerr)
-               ret = -1;
-       else
-               ret = recv(fd, b->r, max, 0);
+           {
+               int skerr, lskerr;
+               
+               lskerr = sizeof(skerr);
+               getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
+               if (skerr)
+                   ret = -1;
+               else
+                   ret = recv(fd, b->r, max, 0);
+           }
 #else
-       ret = recv(fd, b->r, max, MSG_NOSIGNAL);
+           ret = recv(fd, b->r, max, MSG_NOSIGNAL);
 #endif
-
-       if (ret > 0) {
-           b->r += ret;
-           b->l += ret;
-           s->res_cr = RES_DATA;
-           
-           if (b->r == b->data + BUFSIZE) {
-               b->r = b->data; /* wrap around the buffer */
+           if (ret > 0) {
+               b->r += ret;
+               b->l += ret;
+               s->res_cr = RES_DATA;
+               
+               if (b->r == b->data + BUFSIZE) {
+                   b->r = b->data; /* wrap around the buffer */
+               }
+               /* we hope to read more data or to get a close on next round */
+               continue;
            }
-       }
-       else if (ret == 0)
-           s->res_cr = RES_NULL;
-       else if (errno == EAGAIN) /* ignore EAGAIN */
-           return 0;
-       else {
-           s->res_cr = RES_ERROR;
-           fdtab[fd].state = FD_STERROR;
-       }
+           else if (ret == 0) {
+               s->res_cr = RES_NULL;
+               break;
+           }
+           else if (errno == EAGAIN) {/* ignore EAGAIN */
+               break;
+           }
+           else {
+               s->res_cr = RES_ERROR;
+               fdtab[fd].state = FD_STERROR;
+               break;
+           }
+       } /* while(1) */
     }
     else {
        s->res_cr = RES_ERROR;
        fdtab[fd].state = FD_STERROR;
     }
 
-    if (s->proxy->clitimeout)
-       tv_delayfrom(&s->crexpire, &now, s->proxy->clitimeout);
-    else
-       tv_eternity(&s->crexpire);
+    if (s->res_cr != RES_SILENT) {
+       if (s->proxy->clitimeout)
+           tv_delayfrom(&s->crexpire, &now, s->proxy->clitimeout);
+       else
+           tv_eternity(&s->crexpire);
+       
+       task_wakeup(&rq, t);
+    }
 
-    task_wakeup(s->proxy, s);
     return 0;
 }
 
@@ -1034,74 +1203,86 @@ int event_cli_read(int fd) {
  * It returns 0.
  */
 int event_srv_read(int fd) {
-    struct task *s = fdtab[fd].owner;
+    struct task *t = fdtab[fd].owner;
+    struct session *s = t->context;
     struct buffer *b = s->rep;
     int ret, max;
 
     //    fprintf(stderr,"event_srv_read : fd=%d, s=%p\n", fd, s);
 
-    if (b->l == 0) { /* let's realign the buffer to optimize I/O */
-       b->r = b->w = b->h = b->lr  = b->data;
-       max = BUFSIZE - MAXREWRITE;
-    }
-    else if (b->r > b->w) {
-       max = b->data + BUFSIZE - MAXREWRITE - b->r;
-    }
-    else {
-       max = b->w - b->r;
-       if (max > BUFSIZE - MAXREWRITE)
-           max = BUFSIZE - MAXREWRITE;
-    }
-
-    if (max == 0) {
-       FD_CLR(fd, StaticReadEvent);
-       //fprintf(stderr, "srv_read(%d) : max=%d, d=%p, r=%p, w=%p, l=%d\n",
-       //fd, max, b->data, b->r, b->w, b->l);
-       return 0;
-    }
-
     if (fdtab[fd].state != FD_STERROR) {
+       while (1) {
+           if (b->l == 0) { /* let's realign the buffer to optimize I/O */
+               b->r = b->w = b->h = b->lr  = b->data;
+               max = BUFSIZE - MAXREWRITE;
+           }
+           else if (b->r > b->w) {
+               max = b->data + BUFSIZE - MAXREWRITE - b->r;
+           }
+           else {
+               max = b->w - b->r;
+               if (max > BUFSIZE - MAXREWRITE)
+                   max = BUFSIZE - MAXREWRITE;
+           }
+           
+           if (max == 0) {  /* not anymore room to store data */
+               FD_CLR(fd, StaticReadEvent);
+               break;
+           }
+
 #ifndef MSG_NOSIGNAL
-       int skerr, lskerr;
-       lskerr=sizeof(skerr);
-       getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
-       if (skerr)
-               ret = -1;
-       else
-               ret = recv(fd, b->r, max, 0);
+           {
+               int skerr, lskerr;
+
+               lskerr = sizeof(skerr);
+               getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
+               if (skerr)
+                   ret = -1;
+               else
+                   ret = recv(fd, b->r, max, 0);
+           }
 #else
-       ret = recv(fd, b->r, max, MSG_NOSIGNAL);
+           ret = recv(fd, b->r, max, MSG_NOSIGNAL);
 #endif
-       if (ret > 0) {
-           b->r += ret;
-           b->l += ret;
-           s->res_sr = RES_DATA;
+           if (ret > 0) {
+               b->r += ret;
+               b->l += ret;
+               s->res_sr = RES_DATA;
            
-           if (b->r == b->data + BUFSIZE) {
-               b->r = b->data; /* wrap around the buffer */
+               if (b->r == b->data + BUFSIZE) {
+                   b->r = b->data; /* wrap around the buffer */
+               }
+               /* we hope to read more data or to get a close on next round */
+               continue;
            }
-       }
-       else if (ret == 0)
-           s->res_sr = RES_NULL;
-       else if (errno != EAGAIN) /* ignore EAGAIN */
-           return 0;
-       else {
-           s->res_sr = RES_ERROR;
-           fdtab[fd].state = FD_STERROR;
-       }
+           else if (ret == 0) {
+               s->res_sr = RES_NULL;
+               break;
+           }
+           else if (errno == EAGAIN) {/* ignore EAGAIN */
+               break;
+           }
+           else {
+               s->res_sr = RES_ERROR;
+               fdtab[fd].state = FD_STERROR;
+               break;
+           }
+       } /* while(1) */
     }
     else {
        s->res_sr = RES_ERROR;
        fdtab[fd].state = FD_STERROR;
     }
 
+    if (s->res_sr != RES_SILENT) {
+       if (s->proxy->srvtimeout)
+           tv_delayfrom(&s->srexpire, &now, s->proxy->srvtimeout);
+       else
+           tv_eternity(&s->srexpire);
+       
+       task_wakeup(&rq, t);
+    }
 
-    if (s->proxy->srvtimeout)
-       tv_delayfrom(&s->srexpire, &now, s->proxy->srvtimeout);
-    else
-       tv_eternity(&s->srexpire);
-
-    task_wakeup(s->proxy, s);
     return 0;
 }
 
@@ -1110,7 +1291,8 @@ int event_srv_read(int fd) {
  * It returns 0.
  */
 int event_cli_write(int fd) {
-    struct task *s = fdtab[fd].owner;
+    struct task *t = fdtab[fd].owner;
+    struct session *s = t->context;
     struct buffer *b = s->rep;
     int ret, max;
 
@@ -1128,10 +1310,11 @@ int event_cli_write(int fd) {
        max = b->data + BUFSIZE - b->w;
     
     if (max == 0) {
-       FD_CLR(fd, StaticWriteEvent);
+       // FD_CLR(fd, StaticWriteEvent); // useless
        //fprintf(stderr, "cli_write(%d) : max=%d, d=%p, r=%p, w=%p, l=%d\n",
        //fd, max, b->data, b->r, b->w, b->l);
        s->res_cw = RES_NULL;
+       task_wakeup(&rq, t);
        return 0;
     }
 
@@ -1141,7 +1324,7 @@ int event_cli_write(int fd) {
 #endif
        if (max == 0) { /* nothing to write, just make as if we were never called */
                s->res_cw = RES_NULL;
-               task_wakeup(s->proxy, s);
+               task_wakeup(&rq, t);
                return 0;
        }
 
@@ -1188,7 +1371,7 @@ int event_cli_write(int fd) {
     else
        tv_eternity(&s->cwexpire);
 
-    task_wakeup(s->proxy, s);
+    task_wakeup(&rq, t);
     return 0;
 }
 
@@ -1198,7 +1381,8 @@ int event_cli_write(int fd) {
  * It returns 0.
  */
 int event_srv_write(int fd) {
-    struct task *s = fdtab[fd].owner;
+    struct task *t = fdtab[fd].owner;
+    struct session *s = t->context;
     struct buffer *b = s->req;
     int ret, max;
 
@@ -1216,10 +1400,12 @@ int event_srv_write(int fd) {
        max = b->data + BUFSIZE - b->w;
     
     if (max == 0) {
-       FD_CLR(fd, StaticWriteEvent);
+       /* may be we have received a connection acknowledgement in TCP mode without data */
+       // FD_CLR(fd, StaticWriteEvent); // useless ?
        //fprintf(stderr, "srv_write(%d) : max=%d, d=%p, r=%p, w=%p, l=%d\n",
        //fd, max, b->data, b->r, b->w, b->l);
        s->res_sw = RES_NULL;
+       task_wakeup(&rq, t);
        return 0;
     }
 
@@ -1229,8 +1415,9 @@ int event_srv_write(int fd) {
 #endif
        fdtab[fd].state = FD_STREADY;
        if (max == 0) { /* nothing to write, just make as if we were never called, except to finish a connect() */
+           //FD_CLR(fd, StaticWriteEvent); // useless ?
            s->res_sw = RES_NULL;
-           task_wakeup(s->proxy, s);
+           task_wakeup(&rq, t);
            return 0;
        }
 
@@ -1276,142 +1463,195 @@ int event_srv_write(int fd) {
     else
        tv_eternity(&s->swexpire);
 
-    task_wakeup(s->proxy, s);
+    task_wakeup(&rq, t);
     return 0;
 }
 
 
 /*
  * this function is called on a read event from a listen socket, corresponding
- * to an accept. It returns 0.
+ * to an accept. It tries to accept as many connections as possible.
+ * It returns 0.
  */
 int event_accept(int fd) {
     struct proxy *p = (struct proxy *)fdtab[fd].owner;
-    struct task *s;
-    int laddr;
+    struct session *s;
+    struct task *t;
     int cfd;
     int one = 1;
 
-    if ((s = pool_alloc(session)) == NULL) { /* disable this proxy for a while */
-       Alert("out of memory in event_accept().\n");
-       FD_CLR(fd, StaticReadEvent);
-       p->state = PR_STIDLE;
-       return 0;
-    }
 
-    laddr = sizeof(s->cli_addr);
-    if ((cfd = accept(fd, (struct sockaddr *)&s->cli_addr, &laddr)) == -1) {
-       pool_free(session, s);
-       return 0;
-    }
+    while (p->nbconn < p->maxconn) {
+       struct sockaddr_in addr;
+       int laddr = sizeof(addr);
+       if ((cfd = accept(fd, (struct sockaddr *)&addr, &laddr)) == -1)
+           return 0;       /* nothing more to accept */
 
-    if ((fcntl(cfd, F_SETFL, O_NONBLOCK) == -1) ||
-       (setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY,
-                   (char *) &one, sizeof(one)) == -1)) {
-       Alert("accept(): cannot set the socket in non blocking mode. Giving up\n");
-       close(cfd);
-       pool_free(session, s);
-       return 0;
-    }
+       if ((s = pool_alloc(session)) == NULL) { /* disable this proxy for a while */
+           Alert("out of memory in event_accept().\n");
+           FD_CLR(fd, StaticReadEvent);
+           p->state = PR_STIDLE;
+           close(cfd);
+           return 0;
+       }
 
-    if ((p->mode == PR_MODE_TCP || p->mode == PR_MODE_HTTP)
-       && (p->logfac1 >= 0 || p->logfac2 >= 0)) {
-       struct sockaddr_in peername, sockname;
-       unsigned char *pn, *sn;
-       int namelen;
-       char message[256];
-
-       namelen = sizeof(peername);
-       getpeername(cfd, (struct sockaddr *)&peername, &namelen);
-       pn = (unsigned char *)&peername.sin_addr;
-
-       namelen = sizeof(sockname);
-       getsockname(cfd, (struct sockaddr *)&sockname, &namelen);
-       sn = (unsigned char *)&sockname.sin_addr;
-
-       sprintf(message, "Connect from %d.%d.%d.%d:%d to %d.%d.%d.%d:%d (%s/%s)\n",
-               pn[0], pn[1], pn[2], pn[3], ntohs(peername.sin_port),
-               sn[0], sn[1], sn[2], sn[3], ntohs(sockname.sin_port),
-               p->id, (p->mode == PR_MODE_HTTP) ? "HTTP" : "TCP");
-
-       if (p->logfac1 >= 0)
-           send_syslog(&p->logsrv1, p->logfac1, LOG_INFO, message);
-       if (p->logfac2 >= 0)
-           send_syslog(&p->logsrv2, p->logfac2, LOG_INFO, message);
-    }
-
-    s->proxy = p;
-    s->state = TASK_IDLE;
-    s->cli_state = (p->mode == PR_MODE_HTTP) ?  CL_STHEADERS : CL_STDATA; /* no HTTP headers for non-HTTP proxies */
-    s->srv_state = SV_STIDLE;
-    s->req = s->rep = NULL; /* will be allocated later */
-    s->cookie_val[0] = 0;
-    s->res_cr = s->res_cw = s->res_sr = s->res_sw = RES_SILENT;
-    s->rqnext = NULL; /* task not in run queue */
-    s->next = s->prev = NULL;
-    s->cli_fd = cfd;
-    s->conn_retries = p->conn_retries;
-    s->conn_redisp  = p->conn_redisp;
-
-    if ((s->req = pool_alloc(buffer)) == NULL) { /* no memory */
-       close(cfd); /* nothing can be done for this fd without memory */
-       pool_free(session, s);
-       return 0;
-    }
-    s->req->l = 0;
-    s->req->h = s->req->r = s->req->lr = s->req->w = s->req->data;             /* r and w will be reset further */
+       if ((t = pool_alloc(task)) == NULL) { /* disable this proxy for a while */
+           Alert("out of memory in event_accept().\n");
+           FD_CLR(fd, StaticReadEvent);
+           p->state = PR_STIDLE;
+           close(cfd);
+           pool_free(session, s);
+           return 0;
+       }
 
-    if ((s->rep = pool_alloc(buffer)) == NULL) { /* no memory */
-       pool_free(buffer, s->req);
-       close(cfd); /* nothing can be done for this fd without memory */
-       pool_free(session, s);
-       return 0;
-    }
-    s->rep->l = 0;
-    s->rep->h = s->rep->r = s->rep->lr = s->rep->w = s->rep->data;
+       s->cli_addr = addr;
+       if (cfd >= cfg_maxsock) {
+           Alert("accept(): not enough free sockets. Raise -n argument. Giving up.\n");
+           close(cfd);
+           pool_free(task, t);
+           pool_free(session, s);
+           return 0;
+       }
 
-    fdtab[cfd].read  = &event_cli_read;
-    fdtab[cfd].write = &event_cli_write;
-    fdtab[cfd].owner = s;
-    fdtab[cfd].state = FD_STREADY;
+       if ((fcntl(cfd, F_SETFL, O_NONBLOCK) == -1) ||
+           (setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY,
+                       (char *) &one, sizeof(one)) == -1)) {
+           Alert("accept(): cannot set the socket in non blocking mode. Giving up\n");
+           close(cfd);
+           pool_free(task, t);
+           pool_free(session, s);
+           return 0;
+       }
 
-    if (p->mode == PR_MODE_HEALTH) {  /* health check mode, no client reading */
-       FD_CLR(cfd, StaticReadEvent);
-       tv_eternity(&s->crexpire);
-       shutdown(s->cli_fd, SHUT_RD);
-       s->cli_state = CL_STSHUTR;
+       if ((p->mode == PR_MODE_TCP || p->mode == PR_MODE_HTTP)
+           && (p->logfac1 >= 0 || p->logfac2 >= 0)) {
+           struct sockaddr_in peername, sockname;
+           unsigned char *pn, *sn;
+           int namelen;
+           char message[256];
+
+           //namelen = sizeof(peername);
+           //getpeername(cfd, (struct sockaddr *)&peername, &namelen);
+           //pn = (unsigned char *)&peername.sin_addr;
+           pn = (unsigned char *)&s->cli_addr;
+
+           namelen = sizeof(sockname);
+           if (get_original_dst(cfd, (struct sockaddr_in *)&sockname, &namelen) == -1)
+               getsockname(cfd, (struct sockaddr *)&sockname, &namelen);
+           sn = (unsigned char *)&sockname.sin_addr;
+
+           sprintf(message, "Connect from %d.%d.%d.%d:%d to %d.%d.%d.%d:%d (%s/%s)\n",
+                   pn[0], pn[1], pn[2], pn[3], ntohs(peername.sin_port),
+                   sn[0], sn[1], sn[2], sn[3], ntohs(sockname.sin_port),
+                   p->id, (p->mode == PR_MODE_HTTP) ? "HTTP" : "TCP");
+
+           if (p->logfac1 >= 0)
+               send_syslog(&p->logsrv1, p->logfac1, LOG_INFO, message);
+           if (p->logfac2 >= 0)
+               send_syslog(&p->logsrv2, p->logfac2, LOG_INFO, message);
+       }
+
+
+       t->next = t->prev = t->rqnext = NULL; /* task not in run queue yet */
+       t->wq = LIST_HEAD(wait_queue); /* but already has a wait queue assigned */
+       t->state = TASK_IDLE;
+       t->process = process_session;
+       t->context = s;
+
+       s->task = t;
+       s->proxy = p;
+       s->cli_state = (p->mode == PR_MODE_HTTP) ?  CL_STHEADERS : CL_STDATA; /* no HTTP headers for non-HTTP proxies */
+       s->srv_state = SV_STIDLE;
+       s->req = s->rep = NULL; /* will be allocated later */
+       s->flags = 0;
+       s->res_cr = s->res_cw = s->res_sr = s->res_sw = RES_SILENT;
+       s->cli_fd = cfd;
+       s->srv_fd = -1;
+       s->conn_retries = p->conn_retries;
+
+       if ((s->req = pool_alloc(buffer)) == NULL) { /* no memory */
+           close(cfd); /* nothing can be done for this fd without memory */
+           pool_free(task, t);
+           pool_free(session, s);
+           return 0;
+       }
+       s->req->l = 0;
+       s->req->h = s->req->r = s->req->lr = s->req->w = s->req->data;          /* r and w will be reset further */
 
-       strcpy(s->rep->data, "OK\n"); /* forge an "OK" response */
-       s->rep->l = 3;
-       s->rep->r += 3;
-    }
-    else {
-       FD_SET(cfd, StaticReadEvent);
-    }
+       if ((s->rep = pool_alloc(buffer)) == NULL) { /* no memory */
+           pool_free(buffer, s->req);
+           close(cfd); /* nothing can be done for this fd without memory */
+           pool_free(task, t);
+           pool_free(session, s);
+           return 0;
+       }
+       s->rep->l = 0;
+       s->rep->h = s->rep->r = s->rep->lr = s->rep->w = s->rep->data;
 
-    fd_insert(cfd);
+       fdtab[cfd].read  = &event_cli_read;
+       fdtab[cfd].write = &event_cli_write;
+       fdtab[cfd].owner = t;
+       fdtab[cfd].state = FD_STREADY;
 
-    tv_eternity(&s->cnexpire);
-    tv_eternity(&s->srexpire);
-    tv_eternity(&s->swexpire);
-    tv_eternity(&s->cwexpire);
+       if (p->mode == PR_MODE_HEALTH) {  /* health check mode, no client reading */
+           FD_CLR(cfd, StaticReadEvent);
+           tv_eternity(&s->crexpire);
+           shutdown(s->cli_fd, SHUT_RD);
+           s->cli_state = CL_STSHUTR;
 
-    if (s->proxy->clitimeout)
-       tv_delayfrom(&s->crexpire, &now, s->proxy->clitimeout);
-    else
-       tv_eternity(&s->crexpire);
+           strcpy(s->rep->data, "OK\n"); /* forge an "OK" response */
+           s->rep->l = 3;
+           s->rep->r += 3;
+       }
+       else {
+           FD_SET(cfd, StaticReadEvent);
+       }
 
-    s->expire = s->crexpire;
+       fd_insert(cfd);
 
-    task_queue(LIST_HEAD(p->task), s);
-    task_wakeup(p, s);
+       tv_eternity(&s->cnexpire);
+       tv_eternity(&s->srexpire);
+       tv_eternity(&s->swexpire);
+       tv_eternity(&s->cwexpire);
+
+       if (s->proxy->clitimeout)
+           tv_delayfrom(&s->crexpire, &now, s->proxy->clitimeout);
+       else
+           tv_eternity(&s->crexpire);
+
+       t->expire = s->crexpire;
+
+       task_queue(t);
+       task_wakeup(&rq, t);
 
-    p->nbconn++;
-    actconn++;
-    totalconn++;
+       p->nbconn++;
+       actconn++;
+       totalconn++;
 
-    // fprintf(stderr, "accepting from %p => %d conn, %d total\n", p, actconn, totalconn);
+       // fprintf(stderr, "accepting from %p => %d conn, %d total\n", p, actconn, totalconn);
+    } /* end of while (p->nbconn < p->maxconn) */
+    return 0;
+}
+
+
+/*
+ * This function is used only for server health-checks. It handles
+ * the connection acknowledgement and returns 1 if the socket is OK,
+ * or -1 if an error occured.
+ */
+int event_srv_hck(int fd) {
+    struct task *t = fdtab[fd].owner;
+    struct server *s = t->context;
+
+    int skerr, lskerr;
+    lskerr=sizeof(skerr);
+    getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
+    if (skerr)
+       s->result = -1;
+    else
+       s->result = 1;
 
+    task_wakeup(&rq, t);
     return 0;
 }
 
@@ -1424,7 +1664,7 @@ int event_accept(int fd) {
  * If there's no space left, the move is not done.
  *
  */
-int buffer_replace(struct buffer *b, char *pos, char *str, char *end) {
+int buffer_replace(struct buffer *b, char *pos, char *end, char *str) {
     int delta;
     int len;
 
@@ -1440,17 +1680,18 @@ int buffer_replace(struct buffer *b, char *pos, char *str, char *end) {
     /* now, copy str over pos */
     memcpy(pos, str,len);
 
-    if (b->r >= end) b->r += delta;
-    if (b->w >= end) b->w += delta;
-    if (b->h >= end) b->h += delta;
-    if (b->lr >= end) b->lr += delta;
+    /* we only move data after the displaced zone */
+    if (b->r  > pos) b->r  += delta;
+    if (b->w  > pos) b->w  += delta;
+    if (b->h  > pos) b->h  += delta;
+    if (b->lr > pos) b->lr += delta;
     b->l += delta;
 
     return delta;
 }
 
 /* same except that the string len is given */
-int buffer_replace2(struct buffer *b, char *pos, char *str, int len, char *end) {
+int buffer_replace2(struct buffer *b, char *pos, char *end, char *str, int len) {
     int delta;
 
     delta = len - (end - pos);
@@ -1464,10 +1705,11 @@ int buffer_replace2(struct buffer *b, char *pos, char *str, int len, char *end)
     /* now, copy str over pos */
     memcpy(pos, str,len);
 
-    if (b->r >= end) b->r += delta;
-    if (b->w >= end) b->w += delta;
-    if (b->h >= end) b->h += delta;
-    if (b->lr >= end) b->lr += delta;
+    /* we only move data after the displaced zone */
+    if (b->r  > pos) b->r  += delta;
+    if (b->w  > pos) b->w  += delta;
+    if (b->h  > pos) b->h  += delta;
+    if (b->lr > pos) b->lr += delta;
     b->l += delta;
 
     return delta;
@@ -1518,7 +1760,7 @@ int exp_replace(char *dst, char *src, char *str, regmatch_t *matches) {
  * cookie. It returns 1 if a state has changed (and a resync may be needed),
  * 0 else.
  */
-int process_cli(struct task *t) {
+int process_cli(struct session *t) {
     int s = t->srv_state;
     int c = t->cli_state;
     struct buffer *req = t->req;
@@ -1529,177 +1771,225 @@ int process_cli(struct task *t) {
     //FD_ISSET(t->srv_fd, StaticReadEvent), FD_ISSET(t->srv_fd, StaticWriteEvent)
     //);
     if (c == CL_STHEADERS) {
-       char *ptr;
+       /* now parse the partial (or complete) headers */
+       while (req->lr < req->r) { /* this loop only sees one header at each iteration */
+           char *ptr;
+           int delete_header;
        
-       /* read timeout, read error, or last read : give up */
-       if (t->res_cr == RES_ERROR || t->res_cr == RES_NULL ||
-           tv_cmp2_ms(&t->crexpire, &now) <= 0) {
-           FD_CLR(t->cli_fd, StaticReadEvent);
-           FD_CLR(t->cli_fd, StaticWriteEvent);
-           fd_delete(t->cli_fd);
-           close(t->cli_fd);
-           tv_eternity(&t->crexpire);
-           t->cli_state = CL_STCLOSE;
-           return 1;
-       }
-       else if (t->res_cr == RES_SILENT) {
-           return 0;
-       }
-       /* now we know that there are headers to process */
-
-       if (req->l >= BUFSIZE - MAXREWRITE) {
-           /* buffer full : stop reading till we free some space */
-           FD_CLR(t->cli_fd, StaticReadEvent);
-           tv_eternity(&t->crexpire);
-       }
+           ptr = req->lr;
 
-       ptr = req->lr;
-       req->lr = req->r; /* tell that bytes up to <lr> have been read and processes */
-       while (ptr < req->r) {
            /* look for the end of the current header */
            while (ptr < req->r && *ptr != '\n' && *ptr != '\r')
                ptr++;
            
-           if (ptr < req->r) {
-               /* now we have one complete client header between req->h and ptr */
-               if (ptr == req->h) { /* empty line, end of headers */
-                   t->cli_state = CL_STDATA;
-                   //req->lr = ptr; /* tell that bytes up to <lr> have been read and processes */
-                   return 1;
+           if (ptr == req->h) { /* empty line, end of headers */
+               char newhdr[MAXREWRITE + 1];
+               int line, len;
+               /* we can only get here after an end of headers */
+               /* we'll have something else to do here : add new headers ... */
+
+               for (line = 0; line < t->proxy->nb_reqadd; line++) {
+                   len = sprintf(newhdr, "%s\r\n", t->proxy->req_add[line]);
+                   buffer_replace2(req, req->h, req->h, newhdr, len);
                }
-               else {
-                   /* we have one standard header */
-                   if (mode & MODE_DEBUG) {
-                       int len, max;
-                       len = sprintf(trash, "clihdr[%04x:%04x]: ", (unsigned  short)t->cli_fd, (unsigned short)t->srv_fd);
-                       max = ptr - req->h;
-                       UBOUND(max, sizeof(trash) - len - 1);
-                       len += strlcpy(trash + len, req->h, max + 1);
-                       trash[len++] = '\n';
-                       write(1, trash, len);
-                   }
-
-                   if ((req->r >= req->h + 8) && (t->proxy->cookie_name != NULL)
-                       && (strncmp(req->h, "Cookie: ", 8) == 0)) {
-                       char *p1, *p2, *p3, *p4;
-
-                       p1 = req->h + 8; /* first char after 'Cookie: ' */
-
-                       while (p1 < ptr) {
-                           while (p1 < ptr && (isspace(*p1) || *p1 == ';'))
-                               p1++;
-
-                           if (p1 == ptr)
-                               break;
-                           else if (*p1 == ';') { /* next cookie */
-                               ++p1;
-                               continue;
-                           }
 
-                           /* p1 is at the beginning of the cookie name */
-                           p2 = p1;
-
-                           while (p2 < ptr && *p2 != '=' && *p2 != ';')
-                               p2++;
-
-                           if (p2 == ptr)
-                               break;
-                           else if (*p2 == ';') { /* next cookie */
-                               p1=++p2;
-                               continue;
-                           }
-
-                           p3 = p2 + 1; /* skips the '=' sign */
-                           if (p3 == ptr)
-                               break;
+               t->cli_state = CL_STDATA;
 
-                           p4=p3;
-                           while (p4 < ptr && !isspace(*p4) && *p4 != ';')
-                               p4++;
+               /* FIXME: we'll set the client in a wait state while we try to
+                * connect to the server. Is this really needed ? wouldn't it be
+                * better to release the maximum of system buffers instead ? */
+               FD_CLR(t->cli_fd, StaticReadEvent);
+               tv_eternity(&t->crexpire);
+               break;
+           }
 
-                           /* here, we have the cookie name between p1 and p2,
-                            * and its value between p3 and p4.
-                            * we can process it.
-                            */
+           /* to get a complete header line, we need the ending \r\n, \n\r, \r or \n too */
+           if (ptr > req->r - 2) {
+               /* this is a partial header, let's wait for more to come */
+               req->lr = ptr;
+               break;
+           }
 
-                           if ((p2-p1 == strlen(t->proxy->cookie_name)) &&
-                               (strncmp(p1, t->proxy->cookie_name, p2-p1) == 0)) {
-                               /* Cool... it's the right one */
-                               int l;
-                               l = (p4 - p3) < SERVERID_LEN ?
-                                   (p4 - p3) : SERVERID_LEN;
-                               strlcpy(t->cookie_val, p3, l + 1);
-                               break;
-                           }
-                           else {
-//                             fprintf(stderr,"Ignoring unknown cookie : ");
-//                             write(2, p1, p2-p1);
-//                             fprintf(stderr," = ");
-//                             write(2, p3, p4-p3);
-//                             fprintf(stderr,"\n");
-                           }
-                           /* we'll have to look for another cookie ... */
-                           p1 = p4;
-                       }
-                       /* FIXME */
-//                     fprintf(stderr,"Cookie is now: <%s>\n", s->cookie_val);
-                   }
-                   else if (t->proxy->nb_cliexp) { /* try headers regexps */
-                       struct proxy *p = t->proxy;
-                       int exp;
-                       char term;
-
-                       term = *ptr;
-                       *ptr = '\0';
-                       for (exp=0; exp < p->nb_cliexp; exp++) {
-                           if (regexec(p->cli_exp[exp].preg, req->h, MAX_MATCH, pmatch, 0) == 0) {
-                               int len = exp_replace(trash, req->h, p->cli_exp[exp].replace, pmatch);
-                               ptr += buffer_replace2(req, req->h, trash, len, ptr);
-                               break;
-                           }
-                       }
-                       *ptr = term; /* restore the string terminator */
-                   }
-                   
-                   /* look for the beginning of the next header */
-                   if (ptr < req->r) {
-                       if (*ptr == '\n') {
-                           if ((++ptr < req->r) && (*ptr == '\r'))
-                               ptr++;
+           /* now we know that *ptr is either \r or \n,
+            * and that there are at least 1 char after it.
+            */
+           if ((ptr[0] == ptr[1]) || (ptr[1] != '\r' && ptr[1] != '\n'))
+               req->lr = ptr + 1; /* \r\r, \n\n, \r[^\n], \n[^\r] */
+           else
+               req->lr = ptr + 2; /* \r\n or \n\r */
+
+           /*
+            * now we know that we have a full header ; we can do whatever
+            * we want with these pointers :
+            *   req->h  = beginning of header
+            *   ptr     = end of header (first \r or \n)
+            *   req->lr = beginning of next line (next rep->h)
+            *   req->r  = end of data (not used at this stage)
+            */
+
+           delete_header = 0;
+
+           if ((mode & MODE_DEBUG) && !(mode & MODE_QUIET)) {
+               int len, max;
+               len = sprintf(trash, "clihdr[%04x:%04x]: ", (unsigned  short)t->cli_fd, (unsigned short)t->srv_fd);
+               max = ptr - req->h;
+               UBOUND(max, sizeof(trash) - len - 1);
+               len += strlcpy(trash + len, req->h, max + 1);
+               trash[len++] = '\n';
+               write(1, trash, len);
+           }
+
+           /* try headers regexps */
+           if (t->proxy->nb_reqexp) {
+               struct proxy *p = t->proxy;
+               int exp;
+               char term;
+               
+               term = *ptr;
+               *ptr = '\0';
+               for (exp=0; exp < p->nb_reqexp; exp++) {
+                   if (regexec(p->req_exp[exp].preg, req->h, MAX_MATCH, pmatch, 0) == 0) {
+                       if (p->req_exp[exp].replace != NULL) {
+                           int len = exp_replace(trash, req->h, p->req_exp[exp].replace, pmatch);
+                           ptr += buffer_replace2(req, req->h, ptr, trash, len);
                        }
-                       else if (*ptr == '\r') {
-                           if ((++ptr < req->r) && (*ptr == '\n'))
-                               ptr++;
+                       else {
+                           delete_header = 1;
                        }
-                       req->h = ptr;
+                       break;
                    }
                }
+               *ptr = term; /* restore the string terminator */
            }
-           else if (ptr >= req->data + BUFSIZE - MAXREWRITE) { /* no more headers */
-               t->cli_state = CL_STDATA;
-               FD_CLR(t->cli_fd, StaticReadEvent);
-               tv_eternity(&t->crexpire);
-               //req->lr = ptr; /* tell that bytes up to <lr> have been read and processes */
-               return 1;
-           }
-       }
-       //req->lr = ptr; /* tell that bytes up to <lr> have been read and processes */
-    }
-    else if (c == CL_STDATA) {
+           
+           /* now look for cookies */
+           if (!delete_header && (req->r >= req->h + 8) && (t->proxy->cookie_name != NULL)
+               && (strncmp(req->h, "Cookie: ", 8) == 0)) {
+               char *p1, *p2, *p3, *p4;
+               
+               p1 = req->h + 8; /* first char after 'Cookie: ' */
+               
+               while (p1 < ptr) {
+                   while (p1 < ptr && (isspace(*p1) || *p1 == ';'))
+                       p1++;
+                   
+                   if (p1 == ptr)
+                       break;
+                   else if (*p1 == ';') { /* next cookie */
+                       ++p1;
+                       continue;
+                   }
+                   
+                   /* p1 is at the beginning of the cookie name */
+                   p2 = p1;
+                   
+                   while (p2 < ptr && *p2 != '=' && *p2 != ';')
+                       p2++;
+                   
+                   if (p2 == ptr)
+                       break;
+                   else if (*p2 == ';') { /* next cookie */
+                       p1=++p2;
+                       continue;
+                   }
+
+                   p3 = p2 + 1; /* skips the '=' sign */
+                   if (p3 == ptr)
+                       break;
+                   
+                   p4=p3;
+                   while (p4 < ptr && !isspace(*p4) && *p4 != ';')
+                       p4++;
+                   
+                   /* here, we have the cookie name between p1 and p2,
+                    * and its value between p3 and p4.
+                    * we can process it.
+                    */
+                   
+                   if ((p2 - p1 == strlen(t->proxy->cookie_name)) &&
+                       (strncmp(p1, t->proxy->cookie_name, p2 - p1) == 0)) {
+                       /* Cool... it's the right one */
+                       struct server *srv = t->proxy->srv;
+
+                       while (srv &&
+                              ((srv->cklen != p4 - p3) || memcmp(p3, srv->cookie, p4 - p3))) {
+                           srv = srv->next;
+                       }
+
+                       if (srv) { /* we found the server */
+                           t->flags |= TF_DIRECT;
+                           t->srv = srv;
+                       }
+
+                       break;
+                   }
+                   else {
+                       // fprintf(stderr,"Ignoring unknown cookie : ");
+                       // write(2, p1, p2-p1);
+                       // fprintf(stderr," = ");
+                       // write(2, p3, p4-p3);
+                       // fprintf(stderr,"\n");
+                   }
+                   /* we'll have to look for another cookie ... */
+                   p1 = p4;
+               } /* while (p1 < ptr) */
+           } /* end of cookie processing */
+
+           /* let's look if we have to delete this header */
+           if (delete_header) {
+               buffer_replace2(req, req->h, req->lr, "", 0);
+           }
+           req->h = req->lr;
+       } /* while (req->lr < req->r) */
+
+       /* end of header processing (even if incomplete) */
+
+       if ((req->l < BUFSIZE - MAXREWRITE) && ! FD_ISSET(t->cli_fd, StaticReadEvent)) {
+           FD_SET(t->cli_fd, StaticReadEvent);
+           if (t->proxy->clitimeout)
+               tv_delayfrom(&t->crexpire, &now, t->proxy->clitimeout);
+           else
+               tv_eternity(&t->crexpire);
+       }
+
+       /* read timeout, read error, or last read : give up */
+       if (t->res_cr == RES_ERROR || t->res_cr == RES_NULL ||
+           tv_cmp2_ms(&t->crexpire, &now) <= 0) {
+           //FD_CLR(t->cli_fd, StaticReadEvent);
+           //FD_CLR(t->cli_fd, StaticWriteEvent);
+           tv_eternity(&t->crexpire);
+           fd_delete(t->cli_fd);
+           //close(t->cli_fd);
+           t->cli_state = CL_STCLOSE;
+           return 1;
+       }
+//       else if (t->res_cr == RES_SILENT) {
+//           return 0;
+//       }
+
+       if (req->l >= BUFSIZE - MAXREWRITE) {
+           /* buffer full : stop reading till we free some space */
+           FD_CLR(t->cli_fd, StaticReadEvent);
+           tv_eternity(&t->crexpire);
+       }
+
+       return t->cli_state != CL_STHEADERS;
+    }
+    else if (c == CL_STDATA) {
        /* read or write error */
        if (t->res_cw == RES_ERROR || t->res_cr == RES_ERROR) {
-           FD_CLR(t->cli_fd, StaticReadEvent);
-           FD_CLR(t->cli_fd, StaticWriteEvent);
            tv_eternity(&t->crexpire);
            tv_eternity(&t->cwexpire);
-           close(t->cli_fd);
+           fd_delete(t->cli_fd);
+           //FD_CLR(t->cli_fd, StaticReadEvent);
+           //FD_CLR(t->cli_fd, StaticWriteEvent);
+           //close(t->cli_fd);
            t->cli_state = CL_STCLOSE;
            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) {
-
            FD_CLR(t->cli_fd, StaticReadEvent);
            //      if (req->l == 0) /* nothing to write on the server side */
            //          FD_CLR(t->srv_fd, StaticWriteEvent);
@@ -1736,7 +2026,7 @@ int process_cli(struct task *t) {
        }
 
        if ((rep->l == 0) ||
-           ((s == SV_STHEADERS) && (rep->w == rep->h))) {
+           ((s == SV_STHEADERS) /* FIXME: this may be optimized && (rep->w == rep->h)*/)) {
            if (FD_ISSET(t->cli_fd, StaticWriteEvent)) {
                FD_CLR(t->cli_fd, StaticWriteEvent); /* stop writing */
                tv_eternity(&t->cwexpire);
@@ -1757,16 +2047,15 @@ int process_cli(struct task *t) {
        if ((t->res_cw == RES_ERROR) ||
            ((s == SV_STSHUTR || s == SV_STCLOSE) && (rep->l == 0))
            || (tv_cmp2_ms(&t->crexpire, &now) <= 0)) {
-           
-           FD_CLR(t->cli_fd, StaticWriteEvent);
+           //FD_CLR(t->cli_fd, StaticWriteEvent);
            tv_eternity(&t->cwexpire);
            fd_delete(t->cli_fd);
-           close(t->cli_fd);
+           //close(t->cli_fd);
            t->cli_state = CL_STCLOSE;
            return 1;
        }
        else if ((rep->l == 0) ||
-           ((s == SV_STHEADERS) && (rep->w == rep->h))) {
+                ((s == SV_STHEADERS) /* FIXME: this may be optimized && (rep->w == rep->h)*/)) {
            if (FD_ISSET(t->cli_fd, StaticWriteEvent)) {
                FD_CLR(t->cli_fd, StaticWriteEvent); /* stop writing */
                tv_eternity(&t->cwexpire);
@@ -1786,10 +2075,10 @@ int process_cli(struct task *t) {
     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->cwexpire, &now) <= 0) {
-           FD_CLR(t->cli_fd, StaticReadEvent);
+           //FD_CLR(t->cli_fd, StaticReadEvent);
            tv_eternity(&t->crexpire);
            fd_delete(t->cli_fd);
-           close(t->cli_fd);
+           //close(t->cli_fd);
            t->cli_state = CL_STCLOSE;
            return 1;
        }
@@ -1811,9 +2100,9 @@ int process_cli(struct task *t) {
        return 0;
     }
     else { /* CL_STCLOSE: nothing to do */
-       if (mode & MODE_DEBUG) {
+       if ((mode & MODE_DEBUG) && !(mode & MODE_QUIET)) {
            int len;
-           len = sprintf(trash, "clicls[%04x:%04x]\n", t->cli_fd, t->srv_fd);
+           len = sprintf(trash, "clicls[%04x:%04x]\n", (unsigned short)t->cli_fd, (unsigned short)t->srv_fd);
            write(1, trash, len);
        }
        return 0;
@@ -1826,16 +2115,17 @@ int process_cli(struct task *t) {
  * manages the server FSM and its socket. It returns 1 if a state has changed
  * (and a resync may be needed), 0 else.
  */
-int process_srv(struct task *t) {
+int process_srv(struct session *t) {
     int s = t->srv_state;
     int c = t->cli_state;
     struct buffer *req = t->req;
     struct buffer *rep = t->rep;
 
-           //fprintf(stderr,"process_srv: c=%d, s=%d, cr=%d, cw=%d, sr=%d, sw=%d\n", c, s,
-           //FD_ISSET(t->cli_fd, StaticReadEvent), FD_ISSET(t->cli_fd, StaticWriteEvent),
-           // FD_ISSET(t->srv_fd, StaticReadEvent), FD_ISSET(t->srv_fd, StaticWriteEvent)
-           //);
+    //fprintf(stderr,"process_srv: c=%d, s=%d\n", c, s);
+    //fprintf(stderr,"process_srv: c=%d, s=%d, cr=%d, cw=%d, sr=%d, sw=%d\n", c, s,
+    //FD_ISSET(t->cli_fd, StaticReadEvent), FD_ISSET(t->cli_fd, StaticWriteEvent),
+    //FD_ISSET(t->srv_fd, StaticReadEvent), FD_ISSET(t->srv_fd, StaticWriteEvent)
+    //);
     if (s == SV_STIDLE) {
        if (c == CL_STHEADERS)
            return 0;   /* stay in idle, waiting for data to reach the client side */
@@ -1847,13 +2137,18 @@ int process_srv(struct task *t) {
            return 1;
        }
        else { /* go to SV_STCONN */
-           if (connect_server(t, 1) == 0) { /* initiate a connection to the server */
+           if (connect_server(t) == 0) { /* initiate a connection to the server */
                //fprintf(stderr,"0: c=%d, s=%d\n", c, s);
                t->srv_state = SV_STCONN;
            }
            else { /* try again */
                while (t->conn_retries-- > 0) {
-                   if (connect_server(t, !t->conn_redisp || (t->conn_retries > 0)) == 0) {
+                   if ((t->proxy->options & PR_O_REDISP) && (t->conn_retries == 0)) {
+                       t->flags &= ~TF_DIRECT; /* ignore cookie and force to use the dispatcher */
+                       t->srv = NULL; /* it's left to the dispatcher to choose a server */
+                   }
+
+                   if (connect_server(t) == 0) {
                        t->srv_state = SV_STCONN;
                        break;
                    }
@@ -1875,13 +2170,17 @@ int process_srv(struct task *t) {
        else if (t->res_sw == RES_SILENT || t->res_sw == RES_ERROR) {
            //fprintf(stderr,"2: c=%d, s=%d\n", c, s);
            /* timeout,  connect error or first write error */
-           FD_CLR(t->srv_fd, StaticWriteEvent);
+           //FD_CLR(t->srv_fd, StaticWriteEvent);
            fd_delete(t->srv_fd);
-           close(t->srv_fd);
+           //close(t->srv_fd);
            t->conn_retries--;
-           if (t->conn_retries >= 0 &&
-               connect_server(t, !t->conn_redisp || (t->conn_retries > 0)) == 0) {
-               return 0; /* no state changed */
+           if (t->conn_retries >= 0) {
+                   if ((t->proxy->options & PR_O_REDISP) && (t->conn_retries == 0)) {
+                       t->flags &= ~TF_DIRECT; /* ignore cookie and force to use the dispatcher */
+                       t->srv = NULL; /* it's left to the dispatcher to choose a server */
+                   }
+                   if (connect_server(t) == 0)
+                       return 0; /* no state changed */
            }
            /* if conn_retries < 0 or other error, let's abort */
            tv_eternity(&t->cnexpire);
@@ -1906,20 +2205,204 @@ int process_srv(struct task *t) {
            }
            else
                t->srv_state = SV_STHEADERS;
+           tv_eternity(&t->cnexpire);
            return 1;
        }
     }
     else if (s == SV_STHEADERS) { /* receiving server headers */
-       char *ptr;
-       int header_processed = 0;
+
+       /* now parse the partial (or complete) headers */
+       while (rep->lr < rep->r) { /* this loop only sees one header at each iteration */
+           char *ptr;
+           int delete_header;
+
+           ptr = rep->lr;
+
+           /* look for the end of the current header */
+           while (ptr < rep->r && *ptr != '\n' && *ptr != '\r')
+               ptr++;
+           
+           if (ptr == rep->h) {
+               char newhdr[MAXREWRITE + 1];
+               int line, len;
+
+               /* we can only get here after an end of headers */
+               /* we'll have something else to do here : add new headers ... */
+
+               if ((t->srv) && !(t->flags & TF_DIRECT) && (t->proxy->options & PR_O_COOK_INS)) {
+                   /* the server is known, it's not the one the client requested, we have to
+                    * insert a set-cookie here.
+                    */
+                   len = sprintf(newhdr, "Set-Cookie: %s=%s; path=/\r\n",
+                                 t->proxy->cookie_name, t->srv->cookie);
+                   buffer_replace2(rep, rep->h, rep->h, newhdr, len);
+               }
+
+               /* headers to be added */
+               for (line = 0; line < t->proxy->nb_rspadd; line++) {
+                   len = sprintf(newhdr, "%s\r\n", t->proxy->rsp_add[line]);
+                   buffer_replace2(rep, rep->h, rep->h, newhdr, len);
+               }
+
+               t->srv_state = SV_STDATA;
+               break;
+           }
+
+           /* to get a complete header line, we need the ending \r\n, \n\r, \r or \n too */
+           if (ptr > rep->r - 2) {
+               /* this is a partial header, let's wait for more to come */
+               rep->lr = ptr;
+               break;
+           }
+
+           //      fprintf(stderr,"h=%p, ptr=%p, lr=%p, r=%p, *h=", rep->h, ptr, rep->lr, rep->r);
+           //      write(2, rep->h, ptr - rep->h);   fprintf(stderr,"\n");
+
+           /* now we know that *ptr is either \r or \n,
+            * and that there are at least 1 char after it.
+            */
+           if ((ptr[0] == ptr[1]) || (ptr[1] != '\r' && ptr[1] != '\n'))
+               rep->lr = ptr + 1; /* \r\r, \n\n, \r[^\n], \n[^\r] */
+           else
+               rep->lr = ptr + 2; /* \r\n or \n\r */
+
+           /*
+            * now we know that we have a full header ; we can do whatever
+            * we want with these pointers :
+            *   rep->h  = beginning of header
+            *   ptr     = end of header (first \r or \n)
+            *   rep->lr = beginning of next line (next rep->h)
+            *   rep->r  = end of data (not used at this stage)
+            */
+
+           delete_header = 0;
+
+           if ((mode & MODE_DEBUG) && !(mode & MODE_QUIET)) {
+               int len, max;
+               len = sprintf(trash, "srvhdr[%04x:%04x]: ", (unsigned  short)t->cli_fd, (unsigned short)t->srv_fd);
+               max = ptr - rep->h;
+               UBOUND(max, sizeof(trash) - len - 1);
+               len += strlcpy(trash + len, rep->h, max + 1);
+               trash[len++] = '\n';
+               write(1, trash, len);
+           }
+
+           /* try headers regexps */
+           if (t->proxy->nb_rspexp) {
+               struct proxy *p = t->proxy;
+               int exp;
+               char term;
+               
+               term = *ptr;
+               *ptr = '\0';
+               for (exp=0; exp < p->nb_rspexp; exp++) {
+                   if (regexec(p->rsp_exp[exp].preg, rep->h, MAX_MATCH, pmatch, 0) == 0) {
+                       if (p->rsp_exp[exp].replace != NULL) {
+                           int len = exp_replace(trash, rep->h, p->rsp_exp[exp].replace, pmatch);
+                           ptr += buffer_replace2(rep, rep->h, ptr, trash, len);
+                       }
+                       else {
+                           delete_header = 1;
+                       }
+                       break;
+                   }
+               }
+               *ptr = term; /* restore the string terminator */
+           }
+           
+           /* check for server cookies */
+           if (!delete_header && (t->proxy->options & PR_O_COOK_ANY) && (rep->r >= rep->h + 12) &&
+               (t->proxy->cookie_name != NULL) && (strncmp(rep->h, "Set-Cookie: ", 12) == 0)) {
+               char *p1, *p2, *p3, *p4;
+               
+               p1 = rep->h + 12; /* first char after 'Set-Cookie: ' */
+               
+               while (p1 < ptr) { /* in fact, we'll break after the first cookie */
+                   while (p1 < ptr && (isspace(*p1)))
+                       p1++;
+                   
+                   if (p1 == ptr || *p1 == ';') /* end of cookie */
+                       break;
+                   
+                   /* p1 is at the beginning of the cookie name */
+                   p2 = p1;
+                   
+                   while (p2 < ptr && *p2 != '=' && *p2 != ';')
+                       p2++;
+                   
+                   if (p2 == ptr || *p2 == ';') /* next cookie */
+                       break;
+                   
+                   p3 = p2 + 1; /* skips the '=' sign */
+                   if (p3 == ptr)
+                       break;
+                   
+                   p4 = p3;
+                   while (p4 < ptr && !isspace(*p4) && *p4 != ';')
+                       p4++;
+                   
+                   /* here, we have the cookie name between p1 and p2,
+                    * and its value between p3 and p4.
+                    * we can process it.
+                    */
+                   
+                   if ((p2 - p1 == strlen(t->proxy->cookie_name)) &&
+                       (strncmp(p1, t->proxy->cookie_name, p2 - p1) == 0)) {
+                       /* Cool... it's the right one */
+                       
+                       /* If the cookie is in insert mode on a known server, we'll delete
+                        * this occurrence because we'll insert another one later.
+                        * We'll delete it too if the "indirect" option is set and we're in
+                        * a direct access. */
+                       if (((t->srv) && (t->proxy->options & PR_O_COOK_INS)) ||
+                           ((t->flags & TF_DIRECT) && (t->proxy->options & PR_O_COOK_IND))) {
+                           /* this header must be deleted */
+                           delete_header = 1;
+                       }
+                       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);
+                       }
+                       break;
+                   }
+                   else {
+                       //      fprintf(stderr,"Ignoring unknown cookie : ");
+                       //      write(2, p1, p2-p1);
+                       //      fprintf(stderr," = ");
+                       //      write(2, p3, p4-p3);
+                       //      fprintf(stderr,"\n");
+                   }
+                   break; /* we don't want to loop again since there cannot be another cookie on the same line */
+               } /* we're now at the end of the cookie value */
+           } /* end of cookie processing */
+
+           /* let's look if we have to delete this header */
+           if (delete_header) {
+               buffer_replace2(rep, rep->h, rep->lr, "", 0);
+           }
+           rep->h = rep->lr;
+       } /* while (rep->lr < rep->r) */
+
+       /* end of header processing (even if incomplete) */
+
+       if ((rep->l < BUFSIZE - MAXREWRITE) && ! FD_ISSET(t->srv_fd, StaticReadEvent)) {
+           FD_SET(t->srv_fd, StaticReadEvent);
+           if (t->proxy->srvtimeout)
+               tv_delayfrom(&t->srexpire, &now, t->proxy->srvtimeout);
+           else
+               tv_eternity(&t->srexpire);
+       }
 
        /* read or write error */
        if (t->res_sw == RES_ERROR || t->res_sr == RES_ERROR) {
-           FD_CLR(t->srv_fd, StaticReadEvent);
-           FD_CLR(t->srv_fd, StaticWriteEvent);
            tv_eternity(&t->srexpire);
            tv_eternity(&t->swexpire);
-           close(t->srv_fd);
+           //FD_CLR(t->srv_fd, StaticReadEvent);
+           //FD_CLR(t->srv_fd, StaticWriteEvent);
+           //close(t->srv_fd);
+           fd_delete(t->srv_fd);
            t->srv_state = SV_STCLOSE;
            return 1;
        }
@@ -1966,95 +2449,23 @@ int process_srv(struct task *t) {
            }
        }
 
-       /* now parse the partial (or complete) headers */
-
-       //fprintf(stderr,"rep->data=%p, rep->lr=%p, rep->r=%p, rep->l=%d\n", rep->data, rep->lr, rep->r, rep->l);
-       ptr = rep->lr;
-       rep->lr = rep->r;
-
-       //write(1,"rep=",4); write(1, ptr, 4); write(1,"\n",1);
-       //write(1,"hdr=",4); write(1, rep->h, 4); write(1,"\n",1);
-       while (ptr < rep->r) {
-           /* look for the end of the current header */
-           while (ptr < rep->r && *ptr != '\n' && *ptr != '\r')
-               ptr++;
-           
-           if (ptr < rep->r) {
-               //write(1,"ptr=",4); write(1, ptr, 4); write(1,"\n",1);
-               /* now we have one complete header between rep->h and ptr */
-               header_processed = 1;
-               if (ptr == rep->h) { /* empty line, end of headers */
-                   t->srv_state = SV_STDATA;
-                   //rep->lr = ptr; /* tell that bytes up to <lr> have been read and processes */
-                   return 1;
-               }
-               else {
-                   /* we have one standard header */
-                   if (mode & MODE_DEBUG) {
-                       int len, max;
-                       len = sprintf(trash, "srvhdr[%04x:%04x]: ", (unsigned  short)t->cli_fd, (unsigned short)t->srv_fd);
-                       max = ptr - rep->h;
-                       UBOUND(max, sizeof(trash) - len - 1);
-                       len += strlcpy(trash + len, rep->h, max + 1);
-                       trash[len++] = '\n';
-                       write(1, trash, len);
-                   }
-
-                   if (t->proxy->nb_srvexp) { /* try headers regexps */
-                       struct proxy *p = t->proxy;
-                       int exp;
-                       char term;
-
-                       term = *ptr;
-                       *ptr = '\0';
-                       for (exp=0; exp < p->nb_srvexp; exp++) {
-                           if (regexec(p->srv_exp[exp].preg, rep->h, MAX_MATCH, pmatch, 0) == 0) {
-                               int len = exp_replace(trash, rep->h, p->srv_exp[exp].replace, pmatch);
-                               ptr += buffer_replace2(rep, rep->h, trash, len, ptr);
-                               break;
-                           }
-                       }
-                       *ptr = term; /* restore the string terminator */
-                   }
-
-                   /* look for the beginning of the next header */
-                   if (ptr < rep->r) {
-                       if (*ptr == '\n') {
-                           if ((++ptr < rep->r) && (*ptr == '\r'))
-                               ptr++;
-                       }
-                       else if (*ptr == '\r') {
-                           if ((++ptr < rep->r) && (*ptr == '\n'))
-                               ptr++;
-                       }
-                       rep->h = ptr;
-                   }
-               }
-               //// rep->lr = ptr;
-               //rep->lr = rep->h;
-           }
-       }
-
-       if ((rep->l < BUFSIZE - MAXREWRITE) && ! FD_ISSET(t->srv_fd, StaticReadEvent)) {
-           FD_SET(t->srv_fd, StaticReadEvent);
-           if (t->proxy->srvtimeout)
-               tv_delayfrom(&t->srexpire, &now, t->proxy->srvtimeout);
-           else
-               tv_eternity(&t->srexpire);
-       }
-
-       /* be nice with the client side which would like to send a complete header */
-       return header_processed;
-       //return 0;
+       /* be nice with the client side which would like to send a complete header
+        * FIXME: COMPLETELY BUGGY !!! not all headers may be processed because the client
+        * would read all remaining data at once ! The client should not write past rep->lr
+        * when the server is in header state.
+        */
+       //return header_processed;
+       return t->srv_state != SV_STHEADERS;
     }
     else if (s == SV_STDATA) {
        /* read or write error */
        if (t->res_sw == RES_ERROR || t->res_sr == RES_ERROR) {
-           FD_CLR(t->srv_fd, StaticReadEvent);
-           FD_CLR(t->srv_fd, StaticWriteEvent);
            tv_eternity(&t->srexpire);
            tv_eternity(&t->swexpire);
-           close(t->srv_fd);
+           //FD_CLR(t->srv_fd, StaticReadEvent);
+           //FD_CLR(t->srv_fd, StaticWriteEvent);
+           //close(t->srv_fd);
+           fd_delete(t->srv_fd);
            t->srv_state = SV_STCLOSE;
            return 1;
        }
@@ -2116,11 +2527,10 @@ int process_srv(struct task *t) {
        if ((t->res_sw == RES_ERROR) ||
            ((c == CL_STSHUTR || c == CL_STCLOSE) && (req->l == 0)) ||
            (tv_cmp2_ms(&t->swexpire, &now) <= 0)) {
-
-           FD_CLR(t->srv_fd, StaticWriteEvent);
+           //FD_CLR(t->srv_fd, StaticWriteEvent);
            tv_eternity(&t->swexpire);
            fd_delete(t->srv_fd);
-           close(t->srv_fd);
+           //close(t->srv_fd);
            t->srv_state = SV_STCLOSE;
            return 1;
        }
@@ -2145,11 +2555,10 @@ int process_srv(struct task *t) {
        if (t->res_sr == RES_ERROR || t->res_sr == RES_NULL ||
            c == CL_STSHUTW || c == CL_STCLOSE ||
            tv_cmp2_ms(&t->srexpire, &now) <= 0) {
-
-           FD_CLR(t->srv_fd, StaticReadEvent);
+           //FD_CLR(t->srv_fd, StaticReadEvent);
            tv_eternity(&t->srexpire);
            fd_delete(t->srv_fd);
-           close(t->srv_fd);
+           //close(t->srv_fd);
            t->srv_state = SV_STCLOSE;
            return 1;
        }
@@ -2171,9 +2580,9 @@ int process_srv(struct task *t) {
        return 0;
     }
     else { /* SV_STCLOSE : nothing to do */
-       if (mode & MODE_DEBUG) {
+       if ((mode & MODE_DEBUG) && !(mode & MODE_QUIET)) {
            int len;
-           len = sprintf(trash, "srvcls[%04x:%04x]\n", t->cli_fd, t->srv_fd);
+           len = sprintf(trash, "srvcls[%04x:%04x]\n", (unsigned short)t->cli_fd, (unsigned short)t->srv_fd);
            write(1, trash, len);
        }
        return 0;
@@ -2182,42 +2591,164 @@ int process_srv(struct task *t) {
 }
 
 
-/*
- * puts a task back to the wait queue in a clean state, or
- * cleans up its resources if it must be deleted.
+/* Processes the client and server jobs of a session task, then
+ * puts it back to the wait queue in a clean state, or
+ * cleans up its resources if it must be deleted. Returns
+ * the time the task accepts to wait, or -1 for infinity
  */
-void process_task(struct task *t) {
-
-    if (t->cli_state != CL_STCLOSE || t->srv_state != SV_STCLOSE) {
+int process_session(struct task *t) {
+    struct session *s = t->context;
+    int fsm_resync = 0;
+
+    do {
+       fsm_resync = 0;
+       //fprintf(stderr,"before_cli:cli=%d, srv=%d\n", t->cli_state, t->srv_state);
+       fsm_resync |= process_cli(s);
+       //fprintf(stderr,"cli/srv:cli=%d, srv=%d\n", t->cli_state, t->srv_state);
+       fsm_resync |= process_srv(s);
+       //fprintf(stderr,"after_srv:cli=%d, srv=%d\n", t->cli_state, t->srv_state);
+    } while (fsm_resync);
+
+    if (s->cli_state != CL_STCLOSE || s->srv_state != SV_STCLOSE) {
        struct timeval min1, min2;
-       t->res_cw = t->res_cr = t->res_sw = t->res_sr = RES_SILENT;
+       s->res_cw = s->res_cr = s->res_sw = s->res_sr = RES_SILENT;
 
-       tv_min(&min1, &t->crexpire, &t->cwexpire);
-       tv_min(&min2, &t->srexpire, &t->swexpire);
-       tv_min(&min1, &min1, &t->cnexpire);
+       tv_min(&min1, &s->crexpire, &s->cwexpire);
+       tv_min(&min2, &s->srexpire, &s->swexpire);
+       tv_min(&min1, &min1, &s->cnexpire);
        tv_min(&t->expire, &min1, &min2);
 
        /* restore t to its place in the task list */
-       task_queue(LIST_HEAD(t->proxy->task), t);
+       task_queue(t);
 
-       return; /* nothing more to do */
+       return tv_remain(&now, &t->expire); /* nothing more to do */
     }
 
-    t->proxy->nbconn--;
+    s->proxy->nbconn--;
     actconn--;
     
-    if (mode & MODE_DEBUG) {
+    if ((mode & MODE_DEBUG) && !(mode & MODE_QUIET)) {
        int len;
-       len = sprintf(trash, "closed[%04x:%04x]\n", t->cli_fd, t->srv_fd);
+       len = sprintf(trash, "closed[%04x:%04x]\n", (unsigned short)s->cli_fd, (unsigned short)s->srv_fd);
        write(1, trash, len);
     }
 
     /* the task MUST not be in the run queue anymore */
     task_delete(t);
+    session_free(s);
     task_free(t);
+    return -1; /* rest in peace for eternity */
+}
+
+
+
+/*
+ * manages a server health-check. Returns
+ * the time the task accepts to wait, or -1 for infinity.
+ */
+int process_chk(struct task *t) {
+    struct server *s = t->context;
+    int fd = s->curfd;
+    int one = 1;
+
+    //fprintf(stderr, "process_chk: 1\n");
+
+    if (fd < 0) {   /* no check currently running */
+       //fprintf(stderr, "process_chk: 2\n");
+       if (tv_cmp2_ms(&t->expire, &now) > 0) { /* not good time yet */
+           task_queue(t);      /* restore t to its place in the task list */
+           return tv_remain(&now, &t->expire);
+       }
+       
+       /* we'll initiate a new check */
+       s->result = 0; /* no result yet */
+       if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) != -1) {
+           if ((fd < cfg_maxsock) &&
+               (fcntl(fd, F_SETFL, O_NONBLOCK) != -1) &&
+               (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)) {
+                   /* OK, connection in progress or established */
+
+                   //fprintf(stderr, "process_chk: 4\n");
+
+                   s->curfd = fd; /* that's how we know a test is in progress ;-) */
+                   fdtab[fd].owner = t;
+                   fdtab[fd].read  = NULL;
+                   fdtab[fd].write = &event_srv_hck;
+                   fdtab[fd].state = FD_STCONN; /* connection in progress */
+                   FD_SET(fd, StaticWriteEvent);  /* for connect status */
+                   fd_insert(fd);
+                   tv_delayfrom(&t->expire, &now, CHK_CONNTIME);
+                   task_queue(t);      /* restore t to its place in the task list */
+                   return tv_remain(&now, &t->expire);
+               }
+               else if (errno != EALREADY && errno != EISCONN && errno != EAGAIN) {
+                   s->result = -1;    /* a real error */
+               }
+           }
+           //fprintf(stderr, "process_chk: 5\n");
+           close(fd);
+       }
+
+       if (!s->result) { /* nothing done */
+           //fprintf(stderr, "process_chk: 6\n");
+           tv_delayfrom(&t->expire, &now, CHK_INTERVAL);
+           task_queue(t);      /* restore t to its place in the task list */
+           return tv_remain(&now, &t->expire);
+       }
+
+       /* here, we have seen a failure */
+       if (s->health > FALLTIME)
+           s->health--; /* still good */
+       else {
+           s->health = 0; /* failure */
+           s->state &= ~SRV_RUNNING;
+       }
+
+       //fprintf(stderr, "process_chk: 7\n");
+       tv_delayfrom(&t->expire, &now, CHK_CONNTIME);
+    }
+    else {
+       //fprintf(stderr, "process_chk: 8\n");
+       /* there was a test running */
+       if (s->result > 0) { /* good server detected */
+           //fprintf(stderr, "process_chk: 9\n");
+           s->health++; /* was bad, stays for a while */
+           if (s->health >= FALLTIME) {
+               s->health = FALLTIME + RISETIME -1; /* OK now */
+               s->state |= SRV_RUNNING;
+           }
+           s->curfd = -1;
+           FD_CLR(fd, StaticWriteEvent);
+           fd_delete(fd);
+           tv_delayfrom(&t->expire, &now, CHK_INTERVAL);
+       }
+       else if (s->result < 0 || tv_cmp2_ms(&t->expire, &now) <= 0) {
+           //fprintf(stderr, "process_chk: 10\n");
+           /* failure or timeout detected */
+           if (s->health > FALLTIME)
+               s->health--; /* still good */
+           else {
+               s->health = 0; /* failure */
+               s->state &= ~SRV_RUNNING;
+           }
+           s->curfd = -1;
+           FD_CLR(fd, StaticWriteEvent);
+           fd_delete(fd);
+           tv_delayfrom(&t->expire, &now, CHK_INTERVAL);
+       }
+       /* if result is 0 and there's no timeout, we have to wait again */
+    }
+    //fprintf(stderr, "process_chk: 11\n");
+    s->result = 0;
+    task_queue(t);     /* restore t to its place in the task list */
+    return tv_remain(&now, &t->expire);
 }
 
 
+
 #if STATTIME > 0
 int stats(void);
 #endif
@@ -2228,21 +2759,58 @@ int stats(void);
 
 void select_loop() {
   int next_time;
-#if STATTIME > 0
   int time2;
-#endif
   int status;
   int fd,i;
   struct timeval delta;
   int readnotnull, writenotnull;
-  struct proxy *p;
+  struct task *t, *tnext;
 
-  /* stop when there's no connection left and we don't allow them anymore */
-  while (actconn || listeners > 0) {
-      next_time = -1;
-      tv_now(&now);
+  tv_now(&now);
+
+  while (1) {
+      next_time = -1; /* set the timer to wait eternally first */
+
+      /* look for expired tasks and add them to the run queue.
+       */
+      tnext = ((struct task *)LIST_HEAD(wait_queue))->next;
+      while ((t = tnext) != LIST_HEAD(wait_queue)) { /* we haven't looped ? */
+         tnext = t->next;
+
+         /* wakeup expired entries. It doesn't matter if they are
+          * already running because of a previous event
+          */
+         if (tv_cmp2_ms(&t->expire, &now) <= 0) {
+             task_wakeup(&rq, t);
+         }
+         else {
+             break;
+         }
+      }
+
+      /* process each task in the run queue now. Each task may be deleted
+       * since we only use tnext.
+       */
+      tnext = rq;
+      while ((t = tnext) != NULL) {
+         int temp_time;
+         
+         tnext = t->rqnext;
+         task_sleep(&rq, t);
+         
+         temp_time = t->process(t);
+         next_time = MINTIME(temp_time, next_time);
+      }
+
+
+      /* maintain all proxies in a consistent state. This should quickly become a task */
+      time2 = maintain_proxies();
+      next_time = MINTIME(time2, next_time);
+
+      /* stop when there's no connection left and we don't allow them anymore */
+      if (!actconn && listeners == 0)
+         break;
 
-      maintain_proxies();
          
 #if STATTIME > 0
       time2 = stats();
@@ -2250,17 +2818,22 @@ void select_loop() {
       next_time = MINTIME(time2, next_time);
 #endif
 
-      if (next_time >= 0) {
+      if (next_time > 0) {  /* FIXME */
          /* Convert to timeval */
-         delta.tv_sec=next_time/1000; 
-         delta.tv_usec=(next_time%1000)*1000;
+         /* to avoid eventual select loops due to timer precision */
+         next_time += SCHEDULER_RESOLUTION;
+         delta.tv_sec  = next_time / 1000; 
+         delta.tv_usec = (next_time % 1000) * 1000;
+      }
+      else if (next_time == 0) { /* allow select to return immediately when needed */
+         delta.tv_sec = delta.tv_usec = 0;
       }
 
 
       /* let's restore fdset state */
 
       readnotnull = 0; writenotnull = 0;
-      for (i = 0; i < (cfg_maxsock + 3 + FD_SETSIZE - 1)/(8*sizeof(int)); i++) {
+      for (i = 0; i < (cfg_maxsock + FD_SETSIZE - 1)/(8*sizeof(int)); i++) {
          readnotnull |= (*(((int*)ReadEvent)+i) = *(((int*)StaticReadEvent)+i)) != 0;
          writenotnull |= (*(((int*)WriteEvent)+i) = *(((int*)StaticWriteEvent)+i)) != 0;
       }
@@ -2280,7 +2853,12 @@ void select_loop() {
                    NULL,
                    (next_time >= 0) ? &delta : NULL);
       
+      /* this is an experiment on the separation of the select work */
+      // status  = (readnotnull  ? select(maxfd, ReadEvent, NULL, NULL, (next_time >= 0) ? &delta : NULL) : 0);
+      // status |= (writenotnull ? select(maxfd, NULL, WriteEvent, NULL, (next_time >= 0) ? &delta : NULL) : 0);
+      
       tv_now(&now);
+
       if (status > 0) { /* must proceed with events */
 
          int fds;
@@ -2290,63 +2868,25 @@ void select_loop() {
              if ((((int *)(ReadEvent))[fds] | ((int *)(WriteEvent))[fds]) != 0)
                  for (count = 1<<INTBITS, fd = fds << INTBITS; count && fd < maxfd; count--, fd++) {
                      
+                     /* if we specify read first, the accepts and zero reads will be
+                      * seen first. Moreover, system buffers will be flushed faster.
+                      */
                      if (fdtab[fd].state == FD_STCLOSE)
                          continue;
-
-                     if (FD_ISSET(fd, WriteEvent))
-                         fdtab[fd].write(fd);
                      
                      if (FD_ISSET(fd, ReadEvent))
                          fdtab[fd].read(fd);
+
+                     if (fdtab[fd].state == FD_STCLOSE)
+                         continue;
+
+                     if (FD_ISSET(fd, WriteEvent))
+                         fdtab[fd].write(fd);
                  }
       }
       else {
          //      fprintf(stderr,"select returned %d, maxfd=%d\n", status, maxfd);
       }
-
-      for (p = proxy; p; p = p->next) {
-         struct task *t, *tnext;
-         tnext = ((struct task *)LIST_HEAD(p->task))->next;
-         while ((t = tnext) != LIST_HEAD(p->task)) { /* we haven't looped ? */
-             tnext = t->next;
-
-             /* wakeup expired entries. It doesn't matter if they are
-              * already running because of a previous event
-              */
-             if (tv_cmp2_ms(&t->expire, &now) <= 0) {
-                 //              fprintf(stderr,"WQ: expiring task %p : rq=%p\n", t, p->rq);
-                 task_wakeup(p, t);
-             }
-             else {
-                 //              fprintf(stderr,"WQ: ignoring task %p : rq=%p\n", t, p->rq);
-                 break;
-             }
-         }
-
-         /* process each task in the run queue now. Each task may be deleted
-          * since we only use tnext.
-          */
-         tnext = p->rq;
-         while ((t = tnext) != NULL) {
-             int fsm_resync = 0;
-
-             tnext = t->rqnext;
-             task_sleep(p, t);
-
-             do {
-                 fsm_resync = 0;
-                 //fprintf(stderr,"before_cli:cli=%d, srv=%d\n", t->cli_state, t->srv_state);
-                 fsm_resync |= process_cli(t);
-                 //fprintf(stderr,"cli/srv:cli=%d, srv=%d\n", t->cli_state, t->srv_state);
-                 fsm_resync |= process_srv(t);
-                 //fprintf(stderr,"after_srv:cli=%d, srv=%d\n", t->cli_state, t->srv_state);
-             } while (fsm_resync);
-
-             // task_queue(LIST_HEAD(p->task), t);  /* restore t to its place in the task list */
-             // it has been moved to process_task which was more logical.
-             process_task(t);
-         }
-      }
   }
 }
 
@@ -2370,10 +2910,10 @@ int stats(void) {
        
        if (mode & MODE_STATS) {        
                if ((lines++ % 16 == 0) && !(mode & MODE_LOG))
-                   fprintf(stderr,
+                   qfprintf(stderr,
                            "\n active   total  tsknew tskgood tskleft tskrght tsknsch tsklsch tskrsch\n");
                if (lines>1) {
-                       fprintf(stderr,"%07d %07d %07d %07d %07d %07d %07d %07d %07d\n",
+                       qfprintf(stderr,"%07d %07d %07d %07d %07d %07d %07d %07d %07d\n",
                                actconn, totalconn,
                                stats_tsk_new, stats_tsk_good,
                                stats_tsk_left, stats_tsk_right,
@@ -2394,27 +2934,15 @@ int stats(void) {
 /*
  * this function enables proxies when there are enough free sessions,
  * or stops them when the table is full. It is designed to be called from the
- * select_loop().
+ * select_loop(). It returns the time left before next expiration event
+ * during stop time, -1 otherwise.
  */
 static int maintain_proxies(void) {
     struct proxy *p;
+    int tleft; /* time left */
 
     p = proxy;
-
-    if (stopping) {
-       while (p) {
-           if (p->state != PR_STDISABLED) {
-               if (stopping && (tv_remain(&now, &p->stop_time) == 0)) {
-                   FD_CLR(p->listen_fd, StaticReadEvent);
-                   close(p->listen_fd);
-                   p->state = PR_STDISABLED;
-                   listeners--;
-               }
-           }
-           p = p->next;
-       }
-       return -1;
-    }
+    tleft = -1; /* infinite time */
 
     /* if there are enough free sessions, we'll activate proxies */
     if (actconn < cfg_maxconn) {
@@ -2444,7 +2972,27 @@ static int maintain_proxies(void) {
        }
     }
 
-    return -1;
+    if (stopping) {
+       p = proxy;
+       while (p) {
+           if (p->state != PR_STDISABLED) {
+               int t;
+               t = tv_remain(&now, &p->stop_time);
+               if (t == 0) {
+                   //FD_CLR(p->listen_fd, StaticReadEvent);
+                   //close(p->listen_fd);
+                   fd_delete(p->listen_fd);
+                   p->state = PR_STDISABLED;
+                   listeners--;
+               }
+               else {
+                   tleft = MINTIME(t, tleft);
+               }
+           }
+           p = p->next;
+       }
+    }
+    return tleft;
 }
 
 /*
@@ -2456,6 +3004,7 @@ static void soft_stop(void) {
 
     stopping = 1;
     p = proxy;
+    tv_now(&now); /* else, the old time before select will be used */
     while (p) {
        if (p->state != PR_STDISABLED)
            tv_delayfrom(&p->stop_time, &now, p->grace);
@@ -2473,26 +3022,25 @@ void sig_soft_stop(int sig) {
 
 
 void dump(int sig) {
-    struct proxy *p;
-
-    for (p = proxy; p; p = p->next) {
-       struct task *t, *tnext;
-       tnext = ((struct task *)LIST_HEAD(p->task))->next;
-       while ((t = tnext) != LIST_HEAD(p->task)) { /* we haven't looped ? */
-           tnext = t->next;
-           fprintf(stderr,"[dump] wq: task %p, still %ld ms, "
-                   "cli=%d, srv=%d, cr=%d, cw=%d, sr=%d, sw=%d, "
-                   "req=%d, rep=%d, clifd=%d\n",
-                   t, tv_remain(&now, &t->expire),
-                   t->cli_state,
-                   t->srv_state,
-                   FD_ISSET(t->cli_fd, StaticReadEvent),
-                   FD_ISSET(t->cli_fd, StaticWriteEvent),
-                   FD_ISSET(t->srv_fd, StaticReadEvent),
-                   FD_ISSET(t->srv_fd, StaticWriteEvent),
-                   t->req->l, t->rep?t->rep->l:0, t->cli_fd
-                   );
-       }
+    struct task *t, *tnext;
+    struct session *s;
+
+    tnext = ((struct task *)LIST_HEAD(wait_queue))->next;
+    while ((t = tnext) != LIST_HEAD(wait_queue)) { /* we haven't looped ? */
+       tnext = t->next;
+       s = t->context;
+       qfprintf(stderr,"[dump] wq: task %p, still %ld ms, "
+                "cli=%d, srv=%d, cr=%d, cw=%d, sr=%d, sw=%d, "
+                "req=%d, rep=%d, clifd=%d\n",
+                s, tv_remain(&now, &t->expire),
+                s->cli_state,
+                s->srv_state,
+                FD_ISSET(s->cli_fd, StaticReadEvent),
+                FD_ISSET(s->cli_fd, StaticWriteEvent),
+                FD_ISSET(s->srv_fd, StaticReadEvent),
+                FD_ISSET(s->srv_fd, StaticWriteEvent),
+                s->req->l, s->rep?s->rep->l:0, s->cli_fd
+                );
     }
 }
 
@@ -2505,8 +3053,8 @@ int readcfgfile(char *file) {
     char *line;
     FILE *f;
     int linenum = 0;
-    char *cmd;
-    char *args[10];
+    char *end;
+    char *args[MAX_LINE_ARGS];
     int arg;
     int cfgerr = 0;
 
@@ -2518,44 +3066,77 @@ int readcfgfile(char *file) {
 
     while (fgets(line = thisline, sizeof(thisline), f) != NULL) {
        linenum++;
-       /* skips leading spaces */
-       while (isspace(*line))
-           line++;
 
-       /* cleans up line contents */
-       cmd = line;
-       while (*cmd) {
-           if (*cmd == '#' || *cmd == ';' || *cmd == '\n' || *cmd == '\r')
-               *cmd = 0; /* end of string, end of loop */
-           else
-               cmd++;
-       }
+       end = line + strlen(line);
 
-       if (*line == 0)
-           continue;
+       /* skip leading spaces */
+       while (isspace(*line))
+           line++;
        
-       /* fills args[0..9] with the line contents */
-       for (arg=0; arg<9; arg++) {
-           int escaped = 0;
-
-           args[arg] = line;
-           while (*line && (escaped || !isspace(*line))) {
-               if (!escaped) {
-                   if (*line == '\\')
-                       escaped = 1; 
+       arg = 0;
+       args[arg] = line;
+
+       while (*line && arg < MAX_LINE_ARGS) {
+           /* first, we'll replace \\, \<space>, \#, \r, \n, \t, \xXX with their
+            * C equivalent value. Other combinations left unchanged (eg: \1).
+            */
+           if (*line == '\\') {
+               int skip = 0;
+               if (line[1] == ' ' || line[1] == '\\' || line[1] == '#') {
+                   *line = line[1];
+                   skip = 1;
+               }
+               else if (line[1] == 'r') {
+                   *line = '\r';
+                   skip = 1;
+               } 
+               else if (line[1] == 'n') {
+                   *line = '\n';
+                   skip = 1;
+               }
+               else if (line[1] == 't') {
+                   *line = '\t';
+                   skip = 1;
+               }
+               else if (line[1] == 'x' && (line + 3 < end )) {
+                   unsigned char hex1, hex2;
+                   hex1 = toupper(line[2]) - '0'; hex2 = toupper(line[3]) - '0';
+                   if (hex1 > 9) hex1 -= 'A' - '9' - 1;
+                   if (hex2 > 9) hex2 -= 'A' - '9' - 1;
+                   *line = (hex1<<4) + hex2;
+                   skip = 3;
+               } 
+               if (skip) {
+                   memmove(line + 1, line + 1 + skip, end - (line + skip + 1));
+                   end -= skip;
                }
-               else
-                   escaped = 0;
                line++;
            }
-
-           if (*line) {
-               *(line++) = 0;
-               while (isspace(*line))
+           else {
+               if (*line == '#' || *line == '\n' || *line == '\r')
+                   *line = 0; /* end of string, end of loop */
+               else
                    line++;
+               
+               /* a non-escaped space is an argument separator */
+               if (isspace(*line)) {
+                   *line++ = 0;
+                   while (isspace(*line))
+                       line++;
+                   args[++arg] = line;
+               }
            }
        }
 
+       /* empty line */
+       if (!**args)
+           continue;
+
+       /* zero out remaining args */
+       while (++arg < MAX_LINE_ARGS) {
+           args[arg] = line;
+       }
+
        if (!strcmp(args[0], "listen")) {  /* new proxy */
            if (strchr(args[2], ':') == NULL) {
                Alert("parsing [%s:%d] : <listen> expects <id> and <addr:port> as arguments.\n",
@@ -2573,12 +3154,10 @@ int readcfgfile(char *file) {
            curproxy->id = strdup(args[1]);
            curproxy->listen_addr = *str2sa(args[2]);
            curproxy->state = PR_STNEW;
-           curproxy->task.prev = curproxy->task.next = LIST_HEAD(curproxy->task);
-           curproxy->rq = NULL;
            /* set default values */
            curproxy->maxconn = cfg_maxpconn;
            curproxy->conn_retries = CONN_RETRIES;
-           curproxy->conn_redisp = 0;
+           curproxy->options = 0;
            curproxy->clitimeout = curproxy->contimeout = curproxy->srvtimeout = 0;
            curproxy->mode = PR_MODE_TCP;
            curproxy->logfac1 = curproxy->logfac2 = -1; /* log disabled */
@@ -2603,6 +3182,7 @@ int readcfgfile(char *file) {
            curproxy->state = PR_STDISABLED;
        }
        else if (!strcmp(args[0], "cookie")) {  /* cookie name */
+           int cur_arg;
            if (curproxy->cookie_name != NULL) {
                Alert("parsing [%s:%d] : cookie name already specified. Continuing.\n",
                      file, linenum);
@@ -2615,6 +3195,30 @@ int readcfgfile(char *file) {
                return -1;
            }
            curproxy->cookie_name = strdup(args[1]);
+
+           cur_arg = 2;
+           while (*(args[cur_arg])) {
+               if (!strcmp(args[cur_arg], "rewrite")) {
+                   curproxy->options |= PR_O_COOK_RW;
+               }
+               else if (!strcmp(args[cur_arg], "indirect")) {
+                   curproxy->options |= PR_O_COOK_IND;
+               }
+               else if (!strcmp(args[cur_arg], "insert")) {
+                   curproxy->options |= PR_O_COOK_INS;
+               }
+               else {
+                   Alert("parsing [%s:%d] : <cookie> supports 'rewrite', 'insert' and 'indirect' options.\n",
+                         file, linenum);
+                   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] : <cookie> 'rewrite' and 'indirect' mode are incompatibles.\n",
+                     file, linenum);
+               return -1;
+           }
        }
        else if (!strcmp(args[0], "contimeout")) {  /* connect timeout */
            if (curproxy->contimeout != 0) {
@@ -2663,9 +3267,16 @@ int readcfgfile(char *file) {
            }
            curproxy->conn_retries = atol(args[1]);
        }
-       else if (!strcmp(args[0], "redisp")) {  /* enable reconnections to dispatch */
-           curproxy->conn_redisp = 1;
+       else if (!strcmp(args[0], "redispatch") || !strcmp(args[0], "redisp")) {
+           /* enable reconnections to dispatch */
+           curproxy->options |= PR_O_REDISP;
+       }
+#ifdef TRANSPARENT
+       else if (!strcmp(args[0], "transparent")) {
+           /* enable transparent proxy connections */
+           curproxy->options |= PR_O_TRANSP;
        }
+#endif
        else if (!strcmp(args[0], "maxconn")) {  /* maxconn */
            if (*(args[1]) == 0) {
                Alert("parsing [%s:%d] : <maxconn> expects an integer argument.\n",
@@ -2690,21 +3301,73 @@ int readcfgfile(char *file) {
            }
            curproxy->dispatch_addr = *str2sa(args[1]);
        }
+       else if (!strcmp(args[0], "balance")) {  /* set balancing with optionnal algorithm */
+           if (*(args[1])) {
+               if (!strcmp(args[1], "roundrobin")) {
+                   curproxy->options |= PR_O_BALANCE_RR;
+               }
+               else {
+                   Alert("parsing [%s:%d] : <balance> supports 'roundrobin' options.\n",
+                         file, linenum);
+                   return -1;
+               }
+           }
+           else /* if no option is set, use round-robin by default */
+               curproxy->options |= PR_O_BALANCE_RR;
+       }
        else if (!strcmp(args[0], "server")) {  /* server address */
+           int cur_arg;
+
            if (strchr(args[2], ':') == NULL) {
                Alert("parsing [%s:%d] : <server> expects <name> and <addr:port> as arguments.\n",
                      file, linenum);
                return -1;
            }
-           if ((newsrv = (struct server *)calloc(1, sizeof(struct server)))
-               == NULL) {
-               Alert("parsing [%s:%d] : out of memory\n", file, linenum);
+           if ((newsrv = (struct server *)calloc(1, sizeof(struct server))) == NULL) {
+               Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
                exit(1);
            }
            newsrv->next = curproxy->srv;
            curproxy->srv = newsrv;
            newsrv->id = strdup(args[1]);
            newsrv->addr = *str2sa(args[2]);
+           newsrv->state = SRV_RUNNING; /* early server setup */
+           newsrv->health = FALLTIME; /* up, but will fall down at first failure */
+           newsrv->curfd = -1; /* no health-check in progress */
+           cur_arg = 3;
+           while (*args[cur_arg]) {
+               if (!strcmp(args[cur_arg], "cookie")) {
+                   newsrv->cookie = strdup(args[cur_arg + 1]);
+                   newsrv->cklen = strlen(args[cur_arg + 1]);
+                   cur_arg += 2;
+               }
+               else if (!strcmp(args[cur_arg], "check")) {
+                   struct task *t;
+
+                   if ((t = pool_alloc(task)) == NULL) { /* disable this proxy for a while */
+                       Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
+                       return -1;
+                   }
+
+                   t->next = t->prev = t->rqnext = NULL; /* task not in run queue yet */
+                   t->wq = LIST_HEAD(wait_queue); /* but already has a wait queue assigned */
+                   t->state = TASK_IDLE;
+                   t->process = process_chk;
+                   t->context = newsrv;
+
+                   tv_delayfrom(&t->expire, &now, CHK_INTERVAL); /* check this every ms */
+                   task_queue(t);
+                   task_wakeup(&rq, t);
+
+                   cur_arg += 1;
+               }
+               else {
+                   Alert("parsing [%s:%d] : server %s only supports options 'cookie' and 'check'.\n",
+                         file, linenum, newsrv->id);
+                   return -1;
+               }
+           }
+           curproxy->nbservers++;
        }
        else if (!strcmp(args[0], "log")) {  /* syslog server address */
            struct sockaddr_in *sa;
@@ -2743,16 +3406,16 @@ int readcfgfile(char *file) {
            }
 
        }
-       else if (!strcmp(args[0], "cliexp")) {  /* client regex */
+       else if (!strcmp(args[0], "cliexp") || !strcmp(args[0], "reqrep")) {  /* replace request header from a regex */
            regex_t *preg;
-           if (curproxy->nb_cliexp >= MAX_REGEXP) {
-               Alert("parsing [%s:%d] : too many client expressions. Continuing.\n",
+           if (curproxy->nb_reqexp >= MAX_REGEXP) {
+               Alert("parsing [%s:%d] : too many request expressions. Continuing.\n",
                      file, linenum);
                continue;
            }
 
            if (*(args[1]) == 0 || *(args[2]) == 0) {
-               Alert("parsing [%s:%d] : <cliexp> expects <search> and <replace> as arguments.\n",
+               Alert("parsing [%s:%d] : <reqrep> expects <search> and <replace> as arguments.\n",
                      file, linenum);
                return -1;
            }
@@ -2762,20 +3425,82 @@ int readcfgfile(char *file) {
                Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
                return -1;
            }
-           curproxy->cli_exp[curproxy->nb_cliexp].preg = preg;
-           curproxy->cli_exp[curproxy->nb_cliexp].replace = strdup(args[2]);
-           curproxy->nb_cliexp++;
+           curproxy->req_exp[curproxy->nb_reqexp].preg = preg;
+           curproxy->req_exp[curproxy->nb_reqexp].replace = strdup(args[2]);
+           curproxy->nb_reqexp++;
        }
-       else if (!strcmp(args[0], "srvexp")) {  /* server regex */
+       else if (!strcmp(args[0], "reqdel")) {  /* delete request header from a regex */
            regex_t *preg;
-           if (curproxy->nb_srvexp >= MAX_REGEXP) {
+           if (curproxy->nb_reqexp >= MAX_REGEXP) {
+               Alert("parsing [%s:%d] : too many request expressions. Continuing.\n",
+                     file, linenum);
+               continue;
+           }
+
+           if (*(args[1]) == 0) {
+               Alert("parsing [%s:%d] : <reqdel> expects <search> as an argument.\n",
+                     file, linenum);
+               return -1;
+           }
+
+           preg = calloc(1, sizeof(regex_t));
+           if (regcomp(preg, args[1], REG_EXTENDED) != 0) {
+               Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
+               return -1;
+           }
+           curproxy->req_exp[curproxy->nb_reqexp].preg = preg;
+           curproxy->req_exp[curproxy->nb_reqexp].replace = NULL; /* means it must be deleted */
+           curproxy->nb_reqexp++;
+       }
+       else if (!strcmp(args[0], "reqadd")) {  /* add request header */
+           if (curproxy->nb_reqadd >= MAX_REGEXP) {
+               Alert("parsing [%s:%d] : too many client expressions. Continuing.\n",
+                     file, linenum);
+               continue;
+           }
+
+           if (*(args[1]) == 0) {
+               Alert("parsing [%s:%d] : <reqadd> expects <header> as an argument.\n",
+                     file, linenum);
+               return -1;
+           }
+
+           curproxy->req_add[curproxy->nb_reqadd++] = strdup(args[1]);
+       }
+       else if (!strcmp(args[0], "srvexp") || !strcmp(args[0], "rsprep")) {  /* replace response header from a regex */
+           regex_t *preg;
+           if (curproxy->nb_rspexp >= MAX_REGEXP) {
                Alert("parsing [%s:%d] : too many server expressions. Continuing.\n",
                      file, linenum);
                continue;
            }
 
            if (*(args[1]) == 0 || *(args[2]) == 0) {
-               Alert("parsing [%s:%d] : <srvexp> expects <search> and <replace> as arguments.\n",
+               Alert("parsing [%s:%d] : <rsprep> expects <search> and <replace> as arguments.\n",
+                     file, linenum);
+               return -1;
+           }
+
+           preg = calloc(1, sizeof(regex_t));
+           if (regcomp(preg, args[1], REG_EXTENDED) != 0) {
+               Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
+               return -1;
+           }
+           //      fprintf(stderr,"before=<%s> after=<%s>\n", args[1], args[2]);
+           curproxy->rsp_exp[curproxy->nb_rspexp].preg = preg;
+           curproxy->rsp_exp[curproxy->nb_rspexp].replace = strdup(args[2]);
+           curproxy->nb_rspexp++;
+       }
+       else if (!strcmp(args[0], "rspdel")) {  /* delete response header from a regex */
+           regex_t *preg;
+           if (curproxy->nb_rspexp >= MAX_REGEXP) {
+               Alert("parsing [%s:%d] : too many server expressions. Continuing.\n",
+                     file, linenum);
+               continue;
+           }
+
+           if (*(args[1]) == 0) {
+               Alert("parsing [%s:%d] : <rspdel> expects <search> as an argument.\n",
                      file, linenum);
                return -1;
            }
@@ -2786,9 +3511,24 @@ int readcfgfile(char *file) {
                return -1;
            }
            //      fprintf(stderr,"before=<%s> after=<%s>\n", args[1], args[2]);
-           curproxy->srv_exp[curproxy->nb_srvexp].preg = preg;
-           curproxy->srv_exp[curproxy->nb_srvexp].replace = strdup(args[2]);
-           curproxy->nb_srvexp++;
+           curproxy->rsp_exp[curproxy->nb_rspexp].preg = preg;
+           curproxy->rsp_exp[curproxy->nb_rspexp].replace = NULL; /* means it must be deleted */
+           curproxy->nb_rspexp++;
+       }
+       else if (!strcmp(args[0], "rspadd")) {  /* add response header */
+           if (curproxy->nb_rspadd >= MAX_REGEXP) {
+               Alert("parsing [%s:%d] : too many server expressions. Continuing.\n",
+                     file, linenum);
+               continue;
+           }
+
+           if (*(args[1]) == 0) {
+               Alert("parsing [%s:%d] : <rspadd> expects <header> as an argument.\n",
+                     file, linenum);
+               return -1;
+           }
+
+           curproxy->rsp_add[curproxy->nb_rspadd++] = strdup(args[1]);
        }
        else {
            Alert("parsing [%s:%d] : unknown keyword <%s>\n", file, linenum, args[0]);
@@ -2808,7 +3548,30 @@ int readcfgfile(char *file) {
     }
 
     while (curproxy != NULL) {
-       if (curproxy->mode == PR_MODE_TCP || curproxy->mode == PR_MODE_HEALTH) { /* TCP PROXY or HEALTH CHECK */
+       if ((curproxy->mode != PR_MODE_HEALTH) &&
+           !(curproxy->options & (PR_O_TRANSP | PR_O_BALANCE)) &&
+           (*(int *)&curproxy->dispatch_addr == 0)) {
+           Alert("parsing %s : listener %s has no dispatch address and is not in transparent or balance mode.\n",
+                   file, curproxy->id);
+           cfgerr++;
+       }
+       else if ((curproxy->mode != PR_MODE_HEALTH) && (curproxy->options & PR_O_BALANCE)) {
+           if (curproxy->options & PR_O_TRANSP) {
+               Alert("parsing %s : listener %s cannot use both transparent and balance mode.\n",
+                     file, curproxy->id);
+               cfgerr++;
+           }
+           else if (curproxy->srv == NULL) {
+               Alert("parsing %s : listener %s needs at least 1 server in balance mode.\n",
+                     file, curproxy->id);
+               cfgerr++;
+           }
+           else if (*(int *)&curproxy->dispatch_addr != 0) {
+               Warning("parsing %s : dispatch address of listener %s will be ignored in balance mode.\n",
+                       file, curproxy->id);
+           }
+       }
+       else if (curproxy->mode == PR_MODE_TCP || curproxy->mode == PR_MODE_HEALTH) { /* TCP PROXY or HEALTH CHECK */
            if (curproxy->cookie_name != NULL) {
                Warning("parsing %s : cookie will be ignored for listener %s.\n",
                        file, curproxy->id);
@@ -2817,11 +3580,11 @@ int readcfgfile(char *file) {
                Warning("parsing %s : servers will be ignored for listener %s.\n",
                        file, curproxy->id);
            }
-           if (curproxy->nb_srvexp) {
+           if (curproxy->nb_rspexp) {
                Warning("parsing %s : server regular expressions will be ignored for listener %s.\n",
                        file, curproxy->id);
            }
-           if (curproxy->nb_cliexp) {
+           if (curproxy->nb_reqexp) {
                Warning("parsing %s : client regular expressions will be ignored for listener %s.\n",
                        file, curproxy->id);
            }
@@ -2860,7 +3623,7 @@ void init(int argc, char **argv) {
     char *tmp;
 
     if (1<<INTBITS != sizeof(int)*8) {
-       fprintf(stderr,
+       qfprintf(stderr,
                "Error: wrong architecture. Recompile so that sizeof(int)=%d\n",
                sizeof(int)*8);
        exit(1);
@@ -2886,7 +3649,9 @@ void init(int argc, char **argv) {
            else if (*flag == 'd')
                mode |= MODE_DEBUG;
            else if (*flag == 'D')
-               mode |= MODE_DAEMON;
+               mode |= MODE_DAEMON | MODE_QUIET;
+           else if (*flag == 'q')
+               mode |= MODE_QUIET;
 #if STATTIME > 0
            else if (*flag == 's')
                mode |= MODE_STATS;
@@ -2925,20 +3690,20 @@ void init(int argc, char **argv) {
 
     ReadEvent = (fd_set *)calloc(1,
                sizeof(fd_set) *
-               (cfg_maxsock + 3 + FD_SETSIZE - 1) / FD_SETSIZE);
+               (cfg_maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
     WriteEvent = (fd_set *)calloc(1,
                sizeof(fd_set) *
-               (cfg_maxsock + 3 + FD_SETSIZE - 1) / FD_SETSIZE);
+               (cfg_maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
     StaticReadEvent = (fd_set *)calloc(1,
                sizeof(fd_set) *
-               (cfg_maxsock + 3 + FD_SETSIZE - 1) / FD_SETSIZE);
+               (cfg_maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
     StaticWriteEvent = (fd_set *)calloc(1,
                sizeof(fd_set) *
-               (cfg_maxsock + 3 + FD_SETSIZE - 1) / FD_SETSIZE);
+               (cfg_maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
 
     fdtab = (struct fdtab *)calloc(1,
-               sizeof(struct fdtab) * (cfg_maxsock + 3));
-    for (i = 0; i < cfg_maxsock + 3; i++) {
+               sizeof(struct fdtab) * (cfg_maxsock));
+    for (i = 0; i < cfg_maxsock; i++) {
        fdtab[i].state = FD_STCLOSE;
     }
 }
@@ -2963,6 +3728,13 @@ int start_proxies() {
            return -1;
        }
        
+       if (fd >= cfg_maxsock) {
+           Alert("socket(): not enough free sockets for proxy %s. Raise -n argument. Aborting.\n",
+                 curproxy->id);
+           close(fd);
+           return -1;
+       }
+
        if ((fcntl(fd, F_SETFL, O_NONBLOCK) == -1) ||
            (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
                        (char *) &one, sizeof(one)) == -1)) {
@@ -2996,7 +3768,7 @@ int start_proxies() {
        /* the function for the accept() event */
        fdtab[fd].read  = &event_accept;
        fdtab[fd].write = NULL; /* never called */
-       fdtab[fd].owner = (struct task *)curproxy; /* reference the proxy */
+       fdtab[fd].owner = (struct task *)curproxy; /* reference the proxy instead of a task */
        curproxy->state = PR_STRUN;
        fdtab[fd].state = FD_STLISTEN;
        FD_SET(fd, StaticReadEvent);
@@ -3022,10 +3794,13 @@ int main(int argc, char **argv) {
            Alert("[%s.main()] Cannot fork\n", argv[0]);
            exit(1); /* there has been an error */
        }
+       setpgid(1, 0);
+    }
 
+    if (mode & MODE_QUIET) {
        /* detach from the tty */
+       fclose(stdin); fclose(stdout); fclose(stderr);
        close(0); close(1); close(2);
-       setpgid(1, 0);
     }
 
     signal(SIGQUIT, dump);
diff --git a/init.d/haproxy b/init.d/haproxy
new file mode 100644 (file)
index 0000000..80fe4a6
--- /dev/null
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+bin=/usr/sbin/haproxy
+cmdline='$bin -D -f /etc/haproxy/haproxy.cfg'
+
+. $ROOT/sbin/init.d/default 
+
+# arret en douceur
+function dostop {
+   pids=`pidof -o $$ -- $PNAME`
+   if [ ! -z "$pids" ]; then
+      echo "Asking $PNAME to terminate asap..."
+      kill -USR1 $pids
+   fi
+}
+
+main $*
+