]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
* released 1.1.8 v1.1.8
authorwilly tarreau <willy@wtap.(none)>
Sat, 17 Dec 2005 12:08:06 +0000 (13:08 +0100)
committerwilly tarreau <willy@wtap.(none)>
Sat, 17 Dec 2005 12:08:06 +0000 (13:08 +0100)
* option "dontlognull"
* fixed "double space" bug in config parser
* fixed an uninitialized server field in case of dispatch
  with no existing server which could cause a segfault during
  logging.
* the pid logged was always the father's, which was wrong for daemons.
* fixed wrong level "LOG_INFO" for message "proxy started".
* http logging is now complete :
  - ip:port, date, proxy, server
  - req_time, conn_time, hdr_time, tot_time
  - status, size, request
* source address binding

Makefile
NOTES
TODO
doc/haproxy.txt
examples/cfg
haproxy.c

index 602b0570620a2857721e69ebd4a1ee98825229b4..81d1a39b093e1bb2aad7bfac91916189e170bd78 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -32,8 +32,8 @@ LIBS=$(LIBS.$(TARGET))
 
 # - 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
+# - use -DTPROXY to compile with transparent proxy support.
+CFLAGS = -Wall $(COPTS) $(DEBUG) -DSTATTIME=0 -DTPROXY
 LDFLAGS = -g
 
 all: haproxy
diff --git a/NOTES b/NOTES
index 382018c52617cdbd853b09408313f5d5e00ddc96..ce994f7ff1257f6751333e3a488a89d0e55317bb 100644 (file)
--- a/NOTES
+++ b/NOTES
@@ -9,7 +9,19 @@
   * add x-forwarded-for
   * log http requests on demand, and destination server name
 1.1.7 -> 1.1.8
-  - log destination server IP
+  * full HTTP log with destination server ID, req and resp time.
+  * source address of outgoing connections
+1.1.8 -> 1.1.9
   - handle parametrable HTTP health-checks replies
   - differentiate http headers and http uris
+  - support environment variables in config file
   - support keep-alive
+
+--- Notes about cookie usage ---
+
+Cookie insertion is done at the end of server response.
+Cookie matching is done after header replacement, but before
+header deletion. This means that it's perfectly possible to
+delete an inserted cookie once it has been matched, so that
+the server never knows about it.
+
diff --git a/TODO b/TODO
index 41827a20861bc9362eef3fb61628d28519d995ad..d690e70b9a05fa2afaa52e04ef4bc4bcf93da528 100644 (file)
--- a/TODO
+++ b/TODO
@@ -2,20 +2,21 @@
 * implémenter l'option "log global" au niveau proxy pour utiliser les logs
   globaux.
 * matching case-insensitive
+* implémenter outgoing addr
+* loguer t_cnx, t_data, t_total
 + factoriser la fonction de log (send_log = send_syslog+warning+alert)
 + désactivation du keep-alive (suppression des ^Connection: et ajout des Connection: close)
   -> 4 lignes (2 del, 2 add) suffisent.
-
-- loguer t_cnx, t_data, t_total
++ ne pas loguer certaines adresses IP sources
+  -> pour les health-checks uniquement -> pas de log pour les requêtes
+     vides (option dontlognull)
 - mesurer le tps consommé entre deux select, et fournir la conso CPU :
   %cpu = 100 * (tpreselect(n+1)-tpostselect(n)) / (tpreselect(n+1)-tpreselect(n))
 - implémenter limitation fd dans la conf : setrlimit(RLIMIT_NOFILE, ...)
 - implémenter core/no-core dans la conf : setrlimit(RLIMIT_CORE, ...)
-- implémenter outgoing addr
 - optimiser les regex pour accélérer les matches :
   - compter les matches
   - si match(n) & ([n].cpt > [n-1].cpt) & ([n].action == [n-1].action), swap(n,n-1)
   - régulièrement, diviser tous les compteurs (lors d'un dépassement par exemple)
 - filtrage sur l'adresse IP source
-- ne pas loguer certaines adresses IP sources
 - gestion keep-alive
index 71b0cb72664aecfa3697a73c60c6d77347eeac90..dec7e1e0eb496cb2774716eb05d4a7ec816f2cea 100644 (file)
@@ -1,9 +1,9 @@
 
                             H A - P r o x y
                             ---------------
-                             version 1.1.5
+                             version 1.1.8
                              willy tarreau
-                              2002/04/03
+                              2002/04/16
 
 ================
 | 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 ;
+  - 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.
+  - interdire des requêtes qui vérifient certaines conditions.
+
+Il requiert peu de ressources, et son architecture événementielle mono-processus
+lui permet facilement de gérer plusieurs milliers de connexions simultanées sur
+plusieurs relais sans effondrer le système.
 
-Il requiert peu de ressources, et son architecture événementielle
-mono-processus lui permet facilement de gérer plusieurs milliers de
-connexions simultanées sur plusieurs relais sans effondrer le système.
 
 ===========================
 | Paramètres de lancement |
@@ -36,25 +38,97 @@ Les options de lancement sont peu nombreuses :
     -s affiche les statistiques (si option compilée)
     -l ajoute des informations aux statistiques
 
-Le nombre maximal de connexion simultanées par proxy est le paramètre
-par défaut pour les proxies pour lesquels ce paramètre n'est pas
-précisé dans le fichier de configuration.
+Le nombre maximal de connexion simultanées par proxy est le paramètre par défaut
+pour les proxies pour lesquels ce paramètre n'est pas précisé dans le fichier de
+configuration. Il s'agit du paramètre 'maxconn' dans les sections 'listen'.
+
+Le nombre maximal total de connexions simultanées limite le nombre de connexions
+TCP utilisables à un instant par le processus, tous proxies confondus. Ce
+paramètre remplace le paramètre 'maxconn' de la section 'global'.
 
-Le nombre maximal total de connexions simultanées limite le nombre de
-connexions TCP utilisables à un instant par le processus, tous proxies
-confondus.
 
 ============================
 | Fichier de configuration |
 ============================
 
-
 Commentaires
 ============
 
-L'analyseur du fichier de configuration ignore des lignes vides, les
-espaces, les tabulations, et tout ce qui est compris entre le symbole
-'#' (s'il n'est pas précédé d'un '\'), et la fin de la ligne.
+L'analyseur du fichier de configuration ignore des lignes vides, les espaces,
+les tabulations, et tout ce qui est compris entre le symbole '#' (s'il n'est pas
+précédé d'un '\'), et la fin de la ligne. 
+
+Le fichier de configuration est découpé en sections répérées par des mots clés
+tels que :
+
+  - 'global'
+  - 'listen'
+
+Tous les paramètres font référence à la section définie par le dernier mot clé
+reconnu.
+
+
+1) Paramètres généraux
+======================
+
+Il s'agit des paramètres agissant sur le processus, ou bien sur l'ensemble des
+proxies. Ils sont tous spécifiés dans la section 'global'. Les paramètres
+supportés sont :
+
+  - log <adresse> <catégorie>
+  - maxconn <nombre>
+  - uid <identifiant>
+  - gid <identifiant>
+  - chroot <répertoire>
+  - nbproc <nombre>
+  - daemon
+  - debug
+  - quiet
+
+1.1) Journalisation des événements
+----------------------------------
+La plupart des événements sont journalisés : démarrages, arrêts, disparition et
+apparition de serveurs, connexions, erreurs. Tous les messages sont envoyés en
+syslog vers un ou deux serveurs. La syntaxe est la suivante :
+
+    log <adresse_ip> <facility>
+
+Les connexions sont envoyées en niveau "info". Les démarrages de service et de
+serveurs seront envoyés en "notice", les signaux d'arrêts en "warning" et les
+arrêts définitifs de services et de serveurs en "alert". Ceci est valable aussi
+bien pour les proxies que pour les serveurs testés au sein des proxies.
+
+Les catégories possibles sont :
+    kern, user, mail, daemon, auth, syslog, lpr, news,
+    uucp, cron, auth2, ftp, ntp, audit, alert, cron2,
+    local0, local1, local2, local3, local4, local5, local6, local7
+
+Exemple :
+---------
+    global
+       log 192.168.2.200 local3
+       log 192.168.2.201 local4
+
+1.2) limitation du nombre de connexions
+---------------------------------------
+Il est possible et conseillé de limiter le nombre global de connexions par
+processus. Les connexions sont comprises au sens 'acceptation de connexion',
+donc il faut s'attendre en règle général à avoir un peu plus du double de
+sessions TCP que le maximum de connexions fixé. C'est important pour fixer le
+paramètre 'ulimit -n' avant de lancer le proxy. Pour comptabiliser le nombre
+de sockets nécessaires, il faut prendre en compte ces paramètres :
+  - 1 socket par connexion entrante
+  - 1 socket par connexion sortante
+  - 1 socket par proxy
+  - 1 socket pour chaque serveur en cours de health-check
+  - 1 socket pour les logs
+
+Positionner la limite du nombre de descripteurs de fichiers (ulimit -n) à
+2 * maxconn + nbproxy + nbserveurs + 1.
+
+1.3) Changement d'uid et de gid
+-------------------------------
+
 
 
 Serveur
index ab63dbae2081994c4a485defefcbee250905ba87..96d5ba5520c534c4967042ca80bbd721ff26ddec 100644 (file)
@@ -1,20 +1,21 @@
 global
-       log     127.0.0.1 local0
+#      log     127.0.0.1 local0
 #      log     127.0.0.1 local1
        maxconn 4000
        uid     0
        gid     0
 #      chroot  /tmp
-#      nbproc  4
+#      nbproc  2
 #      daemon
 #      debug
 #      quiet
 
 listen proxy1 0.0.0.0:8000
        mode    http
+#      source  127.0.0.2:0
 #      log     127.0.0.1 local0
 #      log     127.0.0.1 local1
-       log global
+#      log global
        #mode   tcp
         cookie SERVERID insert indirect
        balance roundrobin
@@ -22,8 +23,13 @@ listen proxy1 0.0.0.0:8000
        #dispatch 127.0.0.1:31300
        #dispatch 127.0.0.1:80
        #dispatch 127.0.0.1:22
-       #server nc 127.0.0.1:8080 cookie cookie1 check
-       server tuxlocal 127.0.0.1:80 cookie cookie1 check
+       server nc 127.0.0.1:8080 cookie cookie1 check
+#      server tuxlocal0 10.101.23.9:80 cookie cookie1 check
+#      server tuxlocal1 127.0.0.1:80 cookie cookie1 check
+#      server tuxlocal2 127.0.0.1:80 cookie cookie2 check
+#      server tuxlocal3 127.0.0.1:80 cookie cookie3 check
+#      server tuxlocal4 127.0.0.1:80 cookie cookie4 check
+#      server vax 10.101.14.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
@@ -47,13 +53,19 @@ listen proxy1 0.0.0.0:8000
        #rsprep ^(Date:\ )([^,]*)(,\ )(.*) LaDate\ est:\ \4\ (\2)
        # force connection:close
        #reqidel ^Connection:
-       #rspidel        ^Connection:
+       #rspidel ^Connection:
        #reqadd Connection:\ close
        #rspadd Connection:\ close
        # processing options
        #option keepalive
        option  forwardfor
        option httplog
+       option dontlognull
+#      reqirep ^(Test:\ ) \0_toto_\1_toto
+#      reqidel ^Cookie:\ SERVERID=
+#      reqirep ^(GET|POST)\ .* \0
+#      reqirep ^(Host:|Connection:|User-agent:|Cookie:)\ .* \0
+#      reqideny ^
        
 listen proxy1 0.0.0.0:8001
        mode    http
index 0d3d9e84d359877dff7afb5705d2b450efdacfec..290432be3236e0c3ddc26db68ae6d4360f3a0753 100644 (file)
--- a/haproxy.c
+++ b/haproxy.c
@@ -8,11 +8,28 @@
  * 2 of the License, or (at your option) any later version.
  *
  * Pending bugs :
+ *   - solaris only : sometimes, an HTTP proxy with only a dispatch address causes
+ *     the proxy to terminate (no core) if the client breaks the connection during
+ *     the response. Seen on 1.1.8pre4, but never reproduced.
  *   - cookie in insert+indirect mode sometimes segfaults !
  *   - a proxy with an invalid config will prevent the startup even if disabled.
  *
  * ChangeLog :
  *
+ * 2002/04/18 : 1.1.8
+ *   - option "dontlognull"
+ *   - fixed "double space" bug in config parser
+ *   - fixed an uninitialized server field in case of dispatch
+ *     with no existing server which could cause a segfault during
+ *     logging.
+ *   - the pid logged was always the father's, which was wrong for daemons.
+ *   - fixed wrong level "LOG_INFO" for message "proxy started".
+ * 2002/04/13 :
+ *   - http logging is now complete :
+ *     - ip:port, date, proxy, server
+ *     - req_time, conn_time, hdr_time, tot_time
+ *     - status, size, request
+ *   - source address
  * 2002/04/12 : 1.1.7
  *   - added option forwardfor
  *   - added reqirep, reqidel, reqiallow, reqideny, rspirep, rspidel
 #include <time.h>
 #include <regex.h>
 #include <syslog.h>
-#if defined(TRANSPARENT) && defined(NETFILTER)
+#if defined(TPROXY) && defined(NETFILTER)
 #include <linux/netfilter_ipv4.h>
 #endif
 
-#define HAPROXY_VERSION "1.1.7"
-#define HAPROXY_DATE   "2002/04/12"
+#define HAPROXY_VERSION "1.1.8"
+#define HAPROXY_DATE   "2002/04/18"
 
 /* this is for libc5 for example */
 #ifndef TCP_NODELAY
@@ -283,7 +300,8 @@ int strlcpy(char *dst, const char *src, int size) {
 #define PR_O_BALANCE   (PR_O_BALANCE_RR)
 #define        PR_O_KEEPALIVE  64      /* follow keep-alive sessions */
 #define        PR_O_FWDFOR     128     /* insert x-forwarded-for with client address */
-#define PR_O_LOGHTTP   256     /* generate a full HTTP log */
+#define        PR_O_BIND_SRC   256     /* bind to a specific source address when connect()ing */
+#define PR_O_NULLNOLOG 512     /* a connect without request will not be logged */
 
 
 /* various session flags */
@@ -336,7 +354,7 @@ int strlcpy(char *dst, const char *src, int size) {
 #define CFG_GLOBAL     1
 #define CFG_LISTEN     2
 
-/* fields that need to be logged. They appear as flags in session->logwait */
+/* fields that need to be logged. They appear as flags in session->logs.logwait */
 #define LW_DATE                1       /* date */
 #define LW_CLIP                2       /* CLient IP */
 #define LW_SVIP                4       /* SerVer IP */
@@ -345,6 +363,7 @@ int strlcpy(char *dst, const char *src, int size) {
 #define LW_RESP                32      /* http RESPonse */
 #define LW_PXIP                64      /* proxy IP */
 #define LW_PXID                128     /* proxy ID */
+#define LW_BYTES       256     /* bytes read from server */
 
 /*********************************************************************/
 
@@ -363,6 +382,7 @@ struct buffer {
     unsigned int l;                    /* data length */
     char *r, *w, *h, *lr;              /* read ptr, write ptr, last header ptr, last read */
     char *rlim;                                /* read limit, used for header rewriting */
+    unsigned long long total;          /* total data read */
     char data[BUFSIZE];
 };
 
@@ -409,13 +429,22 @@ struct session {
     int srv_state;                     /* state of the server side */
     int conn_retries;                  /* number of connect retries left */
     int flags;                         /* some flags describing the session */
-    int logwait;                       /* log things waiting to be collected : LW_* */
     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 */
     struct server *srv;                        /* the server being used */
-    char *requri;                      /* first line if log needed, NULL otherwise */
+    struct {
+       int logwait;                    /* log fields waiting to be collected : LW_* */
+       struct timeval tv_accept;       /* date of the accept() (beginning of the session) */
+       long  t_request;                /* delay before the end of the request arrives, -1 if never occurs */
+       long  t_connect;                /* delay before the connect() to the server succeeds, -1 if never occurs */
+       long  t_data;                   /* delay before the first data byte from the server ... */
+       unsigned long  t_close;         /* total session duration */
+       char *uri;                      /* first line if log needed, NULL otherwise */
+       int status;                     /* HTTP status from the server, negative if from proxy */
+       long long bytes;                /* number of bytes transferred from the server */
+    } logs;
 };
 
 struct proxy {
@@ -435,6 +464,7 @@ struct proxy {
     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 sockaddr_in source_addr;    /* the address to which we want to bind for connect() */
     struct proxy *next;
     struct sockaddr_in logsrv1, logsrv2; /* 2 syslog servers */
     char logfac1, logfac2;             /* log facility for both servers. -1 = disabled */
@@ -539,19 +569,19 @@ const char *monthname[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
 #define MAX_HOSTNAME_LEN       32
 static char hostname[MAX_HOSTNAME_LEN] = "";
 
-const char *HTTP_403 =
-       "HTTP/1.0 403 Forbidden\r\n"
+const char *HTTP_400 =
+       "HTTP/1.0 400 Bad request\r\n"
        "Cache-Control: no-cache\r\n"
        "Connection: close\r\n"
        "\r\n"
-       "403 Forbidden : Request forbidden by administrative rules.\r\n";
+       "400 Bad request : Your browser sent an invalid request.\r\n";
 
-const char *HTTP_400 =
-       "HTTP/1.0 400 Bad request\r\n"
+const char *HTTP_403 =
+       "HTTP/1.0 403 Forbidden\r\n"
        "Cache-Control: no-cache\r\n"
        "Connection: close\r\n"
        "\r\n"
-       "400 Bad request : Your browser sent an invalid request.\r\n";
+       "403 Forbidden : Request forbidden by administrative rules.\r\n";
 
 const char *HTTP_502 =
        "HTTP/1.0 502 Proxy Error\r\n"
@@ -629,7 +659,7 @@ void Alert(char *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());
+               tm->tm_yday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)getpid());
        vfprintf(stderr, fmt, argp);
        fflush(stderr);
        va_end(argp);
@@ -651,7 +681,7 @@ void Warning(char *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());
+               tm->tm_yday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)getpid());
        vfprintf(stderr, fmt, argp);
        fflush(stderr);
        va_end(argp);
@@ -683,7 +713,7 @@ struct sockaddr_in *str2sa(char *str) {
     char *c;
     int port;
 
-    bzero(&sa, sizeof(sa));
+    memset(&sa, 0, sizeof(sa));
     str=strdup(str);
 
     if ((c=strrchr(str,':')) != NULL) {
@@ -1226,10 +1256,10 @@ static int maintain_proxies(void);
  * 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)
+#if defined(TPROXY) && defined(SO_ORIGINAL_DST)
     return getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, (void *)sa, salen);
 #else
-#if defined(TRANSPARENT) && defined(USE_GETSOCKNAME)
+#if defined(TPROXY) && defined(USE_GETSOCKNAME)
     return getsockname(fd, (struct sockaddr *)sa, salen);
 #else
     return -1;
@@ -1245,8 +1275,8 @@ static inline void session_free(struct session *s) {
        pool_free(buffer, s->req);
     if (s->rep)
        pool_free(buffer, s->rep);
-    if (s->requri)
-       pool_free(requri, s->requri);
+    if (s->logs.uri)
+       pool_free(requri, s->logs.uri);
 
     pool_free(session, s);
 }
@@ -1288,7 +1318,7 @@ int connect_server(struct session *s) {
        else /* unknown balancing algorithm */
            return -1;
     }
-    else if (*(int *)&s->proxy->dispatch_addr) {
+    else if (*(int *)&s->proxy->dispatch_addr.sin_addr) {
        /* connect to the defined dispatch addr */
        s->srv_addr = s->proxy->dispatch_addr;
     }
@@ -1319,6 +1349,14 @@ int connect_server(struct session *s) {
        return -1;
     }
 
+    /* allow specific binding */
+    if (s->proxy->options & PR_O_BIND_SRC &&
+       bind(fd, (struct sockaddr *)&s->proxy->source_addr, sizeof(s->proxy->source_addr)) == -1) {
+       Alert("Cannot bind to source address before connect() for proxy %s. Aborting.\n", s->proxy->id);
+       close(fd);
+       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 */
            qfprintf(stderr,"Cannot connect, no free ports.\n");
@@ -1405,6 +1443,8 @@ int event_cli_read(int fd) {
                if (b->r == b->data + BUFSIZE) {
                    b->r = b->data; /* wrap around the buffer */
                }
+
+               b->total += ret;
                /* we hope to read more data or to get a close on next round */
                continue;
            }
@@ -1498,6 +1538,8 @@ int event_srv_read(int fd) {
                if (b->r == b->data + BUFSIZE) {
                    b->r = b->data; /* wrap around the buffer */
                }
+
+               b->total += ret;
                /* we hope to read more data or to get a close on next round */
                continue;
            }
@@ -1746,21 +1788,45 @@ void sess_log(struct session *s) {
      * computed.
      */
 
-    log = p->to_log & ~s->logwait;
+    log = p->to_log & ~s->logs.logwait;
 
     pn = (log & LW_CLIP) ?
         (unsigned char *)&s->cli_addr.sin_addr :
         (unsigned char *)"\0\0\0\0";
 
-    uri = (log & LW_REQ) ? s->requri : "<requri>";
+    uri = (log & LW_REQ) ? s->logs.uri : "<BADREQ>";
     pxid = p->id;
     //srv = (log & LW_SVID) ? s->srv->id : "<svid>";
-    srv = ((p->to_log & LW_SVID) && s->srv != NULL) ? s->srv->id : "<svid>";
-       
-    send_log(p, LOG_INFO, "%d.%d.%d.%d:%d %s %s \"%s\"\n",
-            pn[0], pn[1], pn[2], pn[3], ntohs(s->cli_addr.sin_port),
-            pxid, srv, uri);
-    s->logwait = 0;
+    srv = ((p->to_log & LW_SVID) && s->srv != NULL) ? s->srv->id : "<NOSRV>";
+
+    if (p->to_log & LW_DATE) {
+       struct tm *tm = localtime(&s->logs.tv_accept.tv_sec);
+
+       send_log(p, LOG_INFO, "%d.%d.%d.%d:%d [%02d/%s/%04d:%02d:%02d:%02d] %s %s %d/%d/%d/%d %d %lld \"%s\"\n",
+                pn[0], pn[1], pn[2], pn[3], ntohs(s->cli_addr.sin_port),
+                tm->tm_mday, monthname[tm->tm_mon], tm->tm_year+1900,
+                tm->tm_hour, tm->tm_min, tm->tm_sec,
+                pxid, srv,
+                s->logs.t_request,
+                (s->logs.t_connect >= 0) ? s->logs.t_connect - s->logs.t_request : -1,
+                (s->logs.t_data >= 0) ? s->logs.t_data - s->logs.t_connect : -1,
+                s->logs.t_close,
+                s->logs.status, s->logs.bytes,
+                uri);
+    }
+    else {
+       send_log(p, LOG_INFO, "%d.%d.%d.%d:%d %s %s %d/%d/%d/%d %d %lld \"%s\"\n",
+                pn[0], pn[1], pn[2], pn[3], ntohs(s->cli_addr.sin_port),
+                pxid, srv,
+                s->logs.t_request,
+                (s->logs.t_connect >= 0) ? s->logs.t_connect - s->logs.t_request : -1,
+                (s->logs.t_data >= 0) ? s->logs.t_data - s->logs.t_connect : -1,
+                s->logs.t_close,
+                s->logs.status, s->logs.bytes,
+                uri);
+    }
+
+    s->logs.logwait = 0;
 }
 
 
@@ -1833,9 +1899,18 @@ int event_accept(int fd) {
        s->res_cr = s->res_cw = s->res_sr = s->res_sw = RES_SILENT;
        s->cli_fd = cfd;
        s->srv_fd = -1;
+       s->srv = NULL;
        s->conn_retries = p->conn_retries;
-       s->requri = NULL;
-       s->logwait = p->to_log;
+
+       s->logs.logwait = p->to_log;
+       s->logs.tv_accept = now;
+       s->logs.t_request = -1;
+       s->logs.t_connect = -1;
+       s->logs.t_data = -1;
+       s->logs.t_close = 0;
+       s->logs.uri = NULL;
+       s->logs.status = -1;
+       s->logs.bytes = 0;
 
        if ((p->mode == PR_MODE_TCP || p->mode == PR_MODE_HTTP)
            && (p->logfac1 >= 0 || p->logfac2 >= 0)) {
@@ -1851,8 +1926,8 @@ int event_accept(int fd) {
 
            if (p->to_log) {
                /* we have the client ip */
-               if (s->logwait & LW_CLIP)
-                   if (!(s->logwait &= ~LW_CLIP))
+               if (s->logs.logwait & LW_CLIP)
+                   if (!(s->logs.logwait &= ~LW_CLIP))
                        sess_log(s);
            }
            else
@@ -1875,6 +1950,7 @@ int event_accept(int fd) {
            return 0;
        }
        s->req->l = 0;
+       s->req->total = 0;
        s->req->h = s->req->r = s->req->lr = s->req->w = s->req->data;  /* r and w will be reset further */
        s->req->rlim = s->req->data + BUFSIZE;
        if (s->cli_state == CL_STHEADERS) /* reserver some space for header rewriting */
@@ -1888,6 +1964,7 @@ int event_accept(int fd) {
            return 0;
        }
        s->rep->l = 0;
+       s->rep->total = 0;
        s->rep->h = s->rep->r = s->rep->lr = s->rep->w = s->rep->rlim = s->rep->data;
 
        fdtab[cfd].read  = &event_cli_read;
@@ -2088,6 +2165,7 @@ int process_cli(struct session *t) {
 
                if (t->flags & SN_CLDENY) {
                    /* no need to go further */
+                   t->logs.status = 403;
                    client_retnclose(t, strlen(HTTP_403), HTTP_403);
                    return 1;
                }
@@ -2109,6 +2187,7 @@ int process_cli(struct session *t) {
                t->cli_state = CL_STDATA;
                req->rlim = req->data + BUFSIZE; /* no more rewrite needed */
 
+               t->logs.t_request = tv_delta(&t->logs.tv_accept, &now);
                /* 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 ? */
@@ -2141,25 +2220,25 @@ int process_cli(struct session *t) {
             *   req->r  = end of data (not used at this stage)
             */
 
-           if (t->logwait & LW_REQ &&
-               t->proxy->mode & PR_MODE_HTTP &&
-               t->proxy->options & PR_O_LOGHTTP) {
+           if (t->logs.logwait & LW_REQ &&
+               t->proxy->mode & PR_MODE_HTTP) {
                /* we have a complete HTTP request that we must log */
                int urilen;
 
-               if ((t->requri = pool_alloc(requri)) == NULL) {
+               if ((t->logs.uri = pool_alloc(requri)) == NULL) {
                    Alert("HTTP logging : out of memory.\n");
-                   client_retnclose(t, strlen(HTTP_403), HTTP_403);
+                   t->logs.status = 502;
+                   client_retnclose(t, strlen(HTTP_502), HTTP_502);
                    return 1;
                }
                
                urilen = ptr - req->h;
                if (urilen >= REQURI_LEN)
                    urilen = REQURI_LEN - 1;
-               memcpy(t->requri, req->h, urilen);
-               t->requri[urilen] = 0;
+               memcpy(t->logs.uri, req->h, urilen);
+               t->logs.uri[urilen] = 0;
 
-               if (!(t->logwait &= ~LW_REQ))
+               if (!(t->logs.logwait &= ~LW_REQ))
                    sess_log(t);
            }
 
@@ -2309,6 +2388,7 @@ int process_cli(struct session *t) {
         * won't be able to free more later, so the session will never terminate.
         */
        if (req->l >= req->rlim - req->data) {
+           t->logs.status = 400;
            client_retnclose(t, strlen(HTTP_400), HTTP_400);
            return 1;
        }
@@ -2507,6 +2587,7 @@ int process_srv(struct session *t) {
                    /* if conn_retries < 0 or other error, let's abort */
                    tv_eternity(&t->cnexpire);
                    t->srv_state = SV_STCLOSE;
+                   t->logs.status = 502;
                    client_return(t, strlen(HTTP_502), HTTP_502);
                }
            }
@@ -2539,6 +2620,8 @@ int process_srv(struct session *t) {
            return 1;
        }
        else { /* no error or write 0 */
+           t->logs.t_connect = tv_delta(&t->logs.tv_accept, &now);
+
            //fprintf(stderr,"3: c=%d, s=%d\n", c, s);
            if (req->l == 0) /* nothing to write */
                FD_CLR(t->srv_fd, StaticWriteEvent);
@@ -2600,6 +2683,7 @@ int process_srv(struct session *t) {
 
                t->srv_state = SV_STDATA;
                rep->rlim = rep->data + BUFSIZE; /* no more rewrite needed */
+               t->logs.t_data = tv_delta(&t->logs.tv_accept, &now);
                break;
            }
 
@@ -2630,6 +2714,12 @@ int process_srv(struct session *t) {
             *   rep->r  = end of data (not used at this stage)
             */
 
+
+           if (t->logs.logwait & LW_RESP) {
+               t->logs.logwait &= ~LW_RESP;
+               t->logs.status = atoi(rep->h + 9);
+           }
+
            delete_header = 0;
 
            if ((global.mode & MODE_DEBUG) && !(global.mode & MODE_QUIET)) {
@@ -2994,8 +3084,12 @@ int process_session(struct task *t) {
        write(1, trash, len);
     }
 
+    s->logs.t_close = tv_delta(&s->logs.tv_accept, &now);
+    if (s->rep != NULL)
+       s->logs.bytes = s->rep->total;
+
     /* let's do a final log if we need it */
-    if (s->logwait)
+    if (s->logs.logwait && (!(s->proxy->options & PR_O_NULLNOLOG) || s->req->total))
        sess_log(s);
 
     /* the task MUST not be in the run queue anymore */
@@ -3752,7 +3846,7 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
        if (!strcmp(args[1], "redispatch"))
            /* enable reconnections to dispatch */
            curproxy->options |= PR_O_REDISP;
-#ifdef TRANSPARENT
+#ifdef TPROXY
        else if (!strcmp(args[1], "transparent"))
            /* enable transparent proxy connections */
            curproxy->options |= PR_O_TRANSP;
@@ -3765,8 +3859,11 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
            curproxy->options |= PR_O_FWDFOR;
        else if (!strcmp(args[1], "httplog")) {
            /* generate a complete HTTP log */
-           curproxy->options |= PR_O_LOGHTTP;
-           curproxy->to_log |= LW_DATE | LW_CLIP | LW_SVID | LW_REQ | LW_PXID;
+           curproxy->to_log |= LW_DATE | LW_CLIP | LW_SVID | LW_REQ | LW_PXID | LW_RESP;
+       }
+       else if (!strcmp(args[1], "dontlognull")) {
+           /* don't log empty requests */
+           curproxy->options |= PR_O_NULLNOLOG;
        }
        else {
            Alert("parsing [%s:%d] : unknown option <%s>.\n", file, linenum, args[1]);
@@ -3778,7 +3875,7 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
        /* enable reconnections to dispatch */
        curproxy->options |= PR_O_REDISP;
     }
-#ifdef TRANSPARENT
+#ifdef TPROXY
     else if (!strcmp(args[0], "transparent")) {
        /* enable transparent proxy connections */
        curproxy->options |= PR_O_TRANSP;
@@ -3934,6 +4031,16 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
            return -1;
        }
     }
+    else if (!strcmp(args[0], "source")) {  /* address to which we bind when connecting */
+       if (strchr(args[1], ':') == NULL) {
+           Alert("parsing [%s:%d] : <source> expects <addr:port> as argument.\n",
+                 file, linenum);
+           return -1;
+       }
+       
+       curproxy->source_addr = *str2sa(args[1]);
+       curproxy->options |= PR_O_BIND_SRC;
+    }
     else if (!strcmp(args[0], "cliexp") || !strcmp(args[0], "reqrep")) {  /* replace request header from a regex */
        regex_t *preg;
        
@@ -4233,19 +4340,20 @@ int readcfgfile(char *file) {
                }
                line++;
            }
-           else {
-               if (*line == '#' || *line == '\n' || *line == '\r')
-                   *line = 0; /* end of string, end of loop */
-               else
-                   line++;
-               
+           else if (*line == '#' || *line == '\n' || *line == '\r') {
+               /* end of string, end of loop */
+               *line = 0;
+               break;
+           }
+           else if (isspace(*line)) {
                /* a non-escaped space is an argument separator */
-               if (isspace(*line)) {
-                   *line++ = 0;
-                   while (isspace(*line))
-                       line++;
-                   args[++arg] = line;
-               }
+               *line++ = 0;
+               while (isspace(*line))
+                   line++;
+               args[++arg] = line;
+           }
+           else {
+               line++;
            }
        }
 
@@ -4299,7 +4407,7 @@ int readcfgfile(char *file) {
        }
        if ((curproxy->mode != PR_MODE_HEALTH) &&
            !(curproxy->options & (PR_O_TRANSP | PR_O_BALANCE)) &&
-           (*(int *)&curproxy->dispatch_addr == 0)) {
+           (*(int *)&curproxy->dispatch_addr.sin_addr == 0)) {
            Alert("parsing %s : listener %s has no dispatch address and is not in transparent or balance mode.\n",
                    file, curproxy->id);
            cfgerr++;
@@ -4315,7 +4423,7 @@ int readcfgfile(char *file) {
                      file, curproxy->id);
                cfgerr++;
            }
-           else if (*(int *)&curproxy->dispatch_addr != 0) {
+           else if (*(int *)&curproxy->dispatch_addr.sin_addr != 0) {
                Warning("parsing %s : dispatch address of listener %s will be ignored in balance mode.\n",
                        file, curproxy->id);
            }
@@ -4559,7 +4667,7 @@ int start_proxies() {
 //       if (curproxy->logfac2 >= 0)
 //           send_syslog(&curproxy->logsrv2, curproxy->logfac2, LOG_INFO, trash);
 
-       send_log(curproxy, LOG_INFO, "Proxy %s started.\n", curproxy->id);
+       send_log(curproxy, LOG_NOTICE, "Proxy %s started.\n", curproxy->id);
        
     }
     return 0;
@@ -4627,6 +4735,7 @@ int main(int argc, char **argv) {
        if (proc == global.nbproc)
            exit(0); /* parent must leave */
 
+       pid = getpid(); /* update child's pid */
        setpgid(1, 0);
     }
 
@@ -4634,3 +4743,4 @@ int main(int argc, char **argv) {
 
     exit(0);
 }
+