]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
* released 1.2.7rc (1.1.33rc) v1.2.7rc
authorwilly tarreau <willy@wtap.(none)>
Sun, 18 Dec 2005 00:31:20 +0000 (01:31 +0100)
committerwilly tarreau <willy@wtap.(none)>
Sun, 18 Dec 2005 00:31:20 +0000 (01:31 +0100)
* second batch of socklen_t changes.
* clean-ups from Cameron Simpson.
* because tv_remain() does not know about eternity, using no timeout can
  make select() spin around a null time-out. Bug reported by Cameron Simpson.
* client read timeout was not properly set to eternity initialized after an
  accept() if it was not set in the config. It remained undetected so long
  because eternity is 0 and newly allocated pages are zeroed by the system.
* do not call get_original_dst() when not in transparent mode.
* implemented a workaround for a bug in certain epoll() implementations on
  linux-2.4 kernels (epoll-lt <= 0.21).
* implemented TCP keepalive with new options : tcpka, clitcpka, srvtcpka.

CHANGELOG
Makefile
doc/haproxy-en.txt
doc/haproxy-fr.txt
haproxy.c

index a9fc400b10e3aff6ed4dff943a9f9dd18a5420d2..edff112bbbac56a38ecfa1cdcdc06bbaf8fa505a 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,19 @@
 ChangeLog :
 ===========
 
+2005/10/09 : 1.2.7rc (1.1.33rc)
+  - second batch of socklen_t changes.
+  - clean-ups from Cameron Simpson.
+  - because tv_remain() does not know about eternity, using no timeout can
+    make select() spin around a null time-out. Bug reported by Cameron Simpson.
+  - client read timeout was not properly set to eternity initialized after an
+    accept() if it was not set in the config. It remained undetected so long
+    because eternity is 0 and newly allocated pages are zeroed by the system.
+  - do not call get_original_dst() when not in transparent mode.
+  - implemented a workaround for a bug in certain epoll() implementations on
+    linux-2.4 kernels (epoll-lt <= 0.21).
+  - implemented TCP keepalive with new options : tcpka, clitcpka, srvtcpka.
+
 2005/08/07 : 1.2.6
   - clean-up patch from Alexander Lazic fixes build on Debian 3.1 (socklen_t).
 
index c99933af76f090d30b52b9fbfa4b880b4edbca0c..c7139f7a630d9e870bb021e4506163e660a8122c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -33,7 +33,10 @@ PCREDIR      := $(shell pcre-config --prefix 2>/dev/null || :)
 COPTS.linux26 = -DNETFILTER -DENABLE_POLL -DENABLE_EPOLL
 LIBS.linux26 =
 
-# This is for enhanced Linux 2.4 with netfilter and epoll() patch
+# This is for enhanced Linux 2.4 with netfilter and epoll() patch.
+# Warning! If kernel is 2.4 with epoll-lt <= 0.21, then you must add
+# -DEPOLL_CTL_MOD_WORKAROUND to workaround a very rare bug.
+#COPTS.linux24e = -DNETFILTER -DENABLE_POLL -DENABLE_EPOLL -DUSE_MY_EPOLL -DEPOLL_CTL_MOD_WORKAROUND
 COPTS.linux24e = -DNETFILTER -DENABLE_POLL -DENABLE_EPOLL -DUSE_MY_EPOLL
 LIBS.linux24e =
 
index 4c5fd422204dc688d42529d5135e972f88161dad..5e932eb0bbc3004a8aa576619c4e5039c9839a49 100644 (file)
@@ -2,9 +2,9 @@
                              H A - P r o x y
                             Reference  Manual
                            -------------------
-                              version 1.2.6
+                              version 1.2.7
                               willy tarreau
-                                2005/08/07
+                                2005/10/09
 
 ============
 | Abstract |
@@ -1093,6 +1093,20 @@ Example :
         server srv1 192.168.1.1:80 source 192.168.2.1:20
         server srv2 192.168.1.2:80 source 192.168.2.1:20
 
+4.1.3) TCP keep-alive
+---------------------
+With version 1.2.7, it becomes possible to enable TCP keep-alives on both the
+client and server sides. This makes it possible to prevent long sessions from
+expiring on external layer 4 components such as firewalls and load-balancers.
+It also allows the system to terminate dead sessions when no timeout has been
+set (not recommanded). The proxy cannot set the keep-alive probes intervals nor
+maximal count, consult your operating system manual for this. There are 3
+options to enable TCP keep-alive :
+
+       option tcpka    # enables keep-alive both on client and server side
+       option clitcpka # enables keep-alive only on client side
+       option srvtcpka # enables keep-alive only on server side
+
 
 4.2) Event logging
 ------------------
index bd75d775e7a16a56316647b894d36b2fe2645708..eb0cd8566ff0a1816ba85074e69cfe44d4220e47 100644 (file)
@@ -2,9 +2,9 @@
                              H A - P r o x y
                            Manuel de référence
                            -------------------
-                               version 1.2.6
+                               version 1.2.7
                               willy tarreau
-                                2005/08/07
+                                2005/10/09
 
 ================
 | Introduction |
@@ -1102,6 +1102,22 @@ Exemple :
        server srv1 192.168.1.1:80 source 192.168.2.1:20
        server srv2 192.168.1.2:80 source 192.168.2.1:20
 
+4.1.3) Maintien de session TCP (keep-alive)
+-------------------------------------------
+Avec la version 1.2.7, il devient possible d'activer le maintien de session
+TCP (TCP keep-alive) à la fois côté client et côté serveur. Cela permet
+d'empêcher des sessions longues d'expirer sur des équipements de niveau 4
+externes tels que des firewalls ou des répartiteurs de charge. Cela permet
+aussi au système de détecter et terminer des sessions figées lorsqu'aucun
+time-out n'a été positionné (fortement déconseillé). Le proxy ne peut pas
+positionner l'intervalle entre les annonces ni le nombre maximal, veuillez
+vous référer au manuel du système d'exploitation pour cela. Il existe 3 options
+pour activer le maintien de session TCP :
+
+       option tcpka    # active le keep-alive côté client et côté serveur
+       option clitcpka # active le keep-alive côté client
+       option srvtcpka # active le keep-alive côté serveur
+
 
 4.2) Journalisation des connexions
 ----------------------------------
index 34d9b408ac9029af35643fbbfdbb729a7d94a822..e362df5bb8cb719e1113981e26b27a34443d0f8d 100644 (file)
--- a/haproxy.c
+++ b/haproxy.c
@@ -76,8 +76,8 @@
 
 #include "include/appsession.h"
 
-#define HAPROXY_VERSION "1.2.6"
-#define HAPROXY_DATE   "2005/08/07"
+#define HAPROXY_VERSION "1.2.7rc"
+#define HAPROXY_DATE   "2005/10/09"
 
 /* this is for libc5 for example */
 #ifndef TCP_NODELAY
  */
 #define SCHEDULER_RESOLUTION   9
 
+#define TIME_ETERNITY          -1
+/* returns the lowest delay amongst <old> and <new>, and respects TIME_ETERNITY */
 #define MINTIME(old, new)      (((new)<0)?(old):(((old)<0||(new)<(old))?(new):(old)))
 #define SETNOW(a)              (*a=now)
 
@@ -313,6 +315,8 @@ int strlcpy2(char *dst, const char *src, int size) {
 #define PR_O_LOGASAP   0x00008000      /* log as soon as possible, without waiting for the session to complete */
 #define PR_O_HTTP_CLOSE        0x00010000      /* force 'connection: close' in both directions */
 #define PR_O_CHK_CACHE 0x00020000      /* require examination of cacheability of the 'set-cookie' field */
+#define PR_O_TCP_CLI_KA        0x00040000      /* enable TCP keep-alive on client-side sessions */
+#define PR_O_TCP_SRV_KA        0x00080000      /* enable TCP keep-alive on server-side sessions */
 
 /* various session flags */
 #define SN_DIRECT      0x00000001      /* connection made on the server matching the client cookie */
@@ -553,7 +557,7 @@ struct proxy {
     int nbconn;                                /* # of active sessions */
     int maxconn;                       /* max # of active sessions */
     int conn_retries;                  /* maximum number of connect retries */
-    int options;                       /* PR_O_REDISP, PR_O_TRANSP */
+    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;
@@ -601,7 +605,7 @@ struct fdtab {
 
 /*********************************************************************/
 
-int cfg_maxpconn = 2000;       /* # of simultaneous connections per proxy (-N) */
+int cfg_maxpconn = DEFAULT_MAXCONN;    /* # of simultaneous connections per proxy (-N) */
 char *cfg_cfgfile = NULL;      /* configuration file */
 char *progname = NULL;         /* program name */
 int  pid;                      /* current process id */
@@ -1280,6 +1284,7 @@ static inline struct timeval *tv_delayfrom(struct timeval *tv, struct timeval *f
 
 /*
  * compares <tv1> and <tv2> : returns 0 if equal, -1 if tv1 < tv2, 1 if tv1 > tv2
+ * Must not be used when either argument is eternity. Use tv_cmp2() for that.
  */
 static inline int tv_cmp(struct timeval *tv1, struct timeval *tv2) {
     if (tv1->tv_sec < tv2->tv_sec)
@@ -1296,6 +1301,7 @@ static inline int tv_cmp(struct timeval *tv1, struct timeval *tv2) {
 
 /*
  * returns the absolute difference, in ms, between tv1 and tv2
+ * Must not be used when either argument is eternity.
  */
 unsigned long tv_delta(struct timeval *tv1, struct timeval *tv2) {
     int cmp;
@@ -1320,6 +1326,7 @@ unsigned long tv_delta(struct timeval *tv1, struct timeval *tv2) {
 
 /*
  * returns the difference, in ms, between tv1 and tv2
+ * Must not be used when either argument is eternity.
  */
 static inline unsigned long tv_diff(struct timeval *tv1, struct timeval *tv2) {
     unsigned long ret;
@@ -1334,6 +1341,7 @@ static inline unsigned long tv_diff(struct timeval *tv1, struct timeval *tv2) {
 
 /*
  * compares <tv1> and <tv2> modulo 1ms: returns 0 if equal, -1 if tv1 < tv2, 1 if tv1 > tv2
+ * Must not be used when either argument is eternity. Use tv_cmp2_ms() for that.
  */
 static inline int tv_cmp_ms(struct timeval *tv1, struct timeval *tv2) {
     if (tv1->tv_sec == tv2->tv_sec) {
@@ -1357,6 +1365,7 @@ static inline int tv_cmp_ms(struct timeval *tv1, struct timeval *tv2) {
 /*
  * returns the remaining time between tv1=now and event=tv2
  * if tv2 is passed, 0 is returned.
+ * Must not be used when either argument is eternity.
  */
 static inline unsigned long tv_remain(struct timeval *tv1, struct timeval *tv2) {
     unsigned long ret;
@@ -1448,6 +1457,28 @@ static inline int tv_cmp2_ms(struct timeval *tv1, struct timeval *tv2) {
        return 0;
 }
 
+/*
+ * returns the remaining time between tv1=now and event=tv2
+ * if tv2 is passed, 0 is returned.
+ * Returns TIME_ETERNITY if tv2 is eternity.
+ */
+static inline unsigned long tv_remain2(struct timeval *tv1, struct timeval *tv2) {
+    unsigned long ret;
+
+    if (tv_iseternity(tv2))
+       return TIME_ETERNITY;
+
+    if (tv_cmp_ms(tv1, tv2) >= 0)
+       return 0; /* event elapsed */
+
+    ret = (tv2->tv_sec - tv1->tv_sec) * 1000;
+    if (tv2->tv_usec > tv1->tv_usec)
+       ret += (tv2->tv_usec - tv1->tv_usec) / 1000;
+    else
+       ret -= (tv1->tv_usec - tv2->tv_usec) / 1000;
+    return (unsigned long) ret;
+}
+
 /*
  * returns the first event between tv1 and tv2 into tvmin.
  * a zero tv is ignored. tvmin is returned.
@@ -1648,7 +1679,7 @@ struct task *task_queue(struct task *task) {
 /* some prototypes */
 static int maintain_proxies(void);
 
-/* this either returns the sockname or the original destination address. Code
+/* 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, socklen_t *salen) {
@@ -1767,7 +1798,8 @@ int connect_server(struct session *s) {
     }
     else if (s->proxy->options & PR_O_TRANSP) {
        /* in transparent mode, use the original dest addr if no dispatch specified */
-       socklen_t salen = sizeof(struct sockaddr_in);
+       socklen_t salen = sizeof(s->srv_addr);
+
        if (get_original_dst(s->cli_fd, &s->srv_addr, &salen) == -1) {
            qfprintf(stderr, "Cannot get original server address.\n");
            return SN_ERR_INTERNAL;
@@ -1778,10 +1810,10 @@ int connect_server(struct session *s) {
      * the port the client connected to with an offset. */
     if (s->srv != NULL && s->srv->state & SRV_MAPPORTS) {
        struct sockaddr_in sockname;
-       socklen_t namelen;
+       socklen_t namelen = sizeof(sockname);
 
-       namelen = sizeof(sockname);
-       if (get_original_dst(s->cli_fd, (struct sockaddr_in *)&sockname, &namelen) == -1)
+       if (!(s->proxy->options & PR_O_TRANSP) ||
+           get_original_dst(s->cli_fd, (struct sockaddr_in *)&sockname, &namelen) == -1)
            getsockname(s->cli_fd, (struct sockaddr *)&sockname, &namelen);
        s->srv_addr.sin_port = htons(ntohs(s->srv_addr.sin_port) + ntohs(sockname.sin_port));
     }
@@ -1821,6 +1853,9 @@ int connect_server(struct session *s) {
        return SN_ERR_INTERNAL;
     }
 
+    if (s->proxy->options & PR_O_TCP_SRV_KA)
+       setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(one));
+
     /* allow specific binding :
      * - server-specific at first
      * - proxy-specific next
@@ -1866,10 +1901,12 @@ int connect_server(struct session *s) {
                     s->proxy->id, s->srv->id, msg);
            return SN_ERR_RESOURCE;
        } else if (errno == ETIMEDOUT) {
+           //qfprintf(stderr,"Connect(): ETIMEDOUT");
            close(fd);
            return SN_ERR_SRVTO;
        } else {
            // (errno == ECONNREFUSED || errno == ENETUNREACH || errno == EACCES || errno == EPERM)
+           //qfprintf(stderr,"Connect(): %d", errno);
            close(fd);
            return SN_ERR_SRVCL;
        }
@@ -1936,9 +1973,9 @@ int event_cli_read(int fd) {
            
 #ifndef MSG_NOSIGNAL
            {
-               int skerr, lskerr;
+               int skerr;
+               socklen_t lskerr = sizeof(skerr);
                
-               lskerr = sizeof(skerr);
                getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
                if (skerr)
                    ret = -1;
@@ -2041,9 +2078,9 @@ int event_srv_read(int fd) {
 
 #ifndef MSG_NOSIGNAL
            {
-               int skerr, lskerr;
+               int skerr;
+               socklen_t lskerr = sizeof(skerr);
 
-               lskerr = sizeof(skerr);
                getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
                if (skerr)
                    ret = -1;
@@ -2126,10 +2163,6 @@ int event_cli_write(int fd) {
        max = b->data + BUFSIZE - b->w;
     
     if (fdtab[fd].state != FD_STERROR) {
-#ifndef MSG_NOSIGNAL
-       int skerr, lskerr;
-#endif
-
        if (max == 0) {
            s->res_cw = RES_NULL;
            task_wakeup(&rq, t);
@@ -2139,12 +2172,16 @@ int event_cli_write(int fd) {
        }
 
 #ifndef MSG_NOSIGNAL
-       lskerr=sizeof(skerr);
-       getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
-       if (skerr)
+       {
+           int skerr;
+           socklen_t lskerr = sizeof(skerr);
+
+           getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
+           if (skerr)
                ret = -1;
-       else
+           else
                ret = send(fd, b->w, max, MSG_DONTWAIT);
+       }
 #else
        ret = send(fd, b->w, max, MSG_DONTWAIT | MSG_NOSIGNAL);
 #endif
@@ -2215,9 +2252,6 @@ int event_srv_write(int fd) {
        max = b->data + BUFSIZE - b->w;
     
     if (fdtab[fd].state != FD_STERROR) {
-#ifndef MSG_NOSIGNAL
-       int skerr, lskerr;
-#endif
        if (max == 0) {
            /* may be we have received a connection acknowledgement in TCP mode without data */
            s->res_sw = RES_NULL;
@@ -2228,14 +2262,16 @@ int event_srv_write(int fd) {
            return 0;
        }
 
-
 #ifndef MSG_NOSIGNAL
-       lskerr=sizeof(skerr);
-       getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
-       if (skerr)
+       {
+           int skerr;
+           socklen_t lskerr = sizeof(skerr);
+           getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
+           if (skerr)
                ret = -1;
-       else
+           else
                ret = send(fd, b->w, max, MSG_DONTWAIT);
+       }
 #else
        ret = send(fd, b->w, max, MSG_DONTWAIT | MSG_NOSIGNAL);
 #endif
@@ -2442,6 +2478,7 @@ int event_accept(int fd) {
     while (p->nbconn < p->maxconn) {
        struct sockaddr_storage addr;
        socklen_t laddr = sizeof(addr);
+
        if ((cfd = accept(fd, (struct sockaddr *)&addr, &laddr)) == -1) {
            switch (errno) {
            case EAGAIN:
@@ -2520,6 +2557,9 @@ int event_accept(int fd) {
            return 0;
        }
 
+       if (p->options & PR_O_TCP_CLI_KA)
+           setsockopt(cfd, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(one));
+
        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;
@@ -2589,10 +2629,10 @@ int event_accept(int fd) {
        if ((p->mode == PR_MODE_TCP || p->mode == PR_MODE_HTTP)
            && (p->logfac1 >= 0 || p->logfac2 >= 0)) {
            struct sockaddr_storage sockname;
-           socklen_t namelen;
+           socklen_t namelen = sizeof(sockname);
 
-           namelen = sizeof(sockname);
            if (addr.ss_family != AF_INET ||
+               !(s->proxy->options & PR_O_TRANSP) ||
                get_original_dst(cfd, (struct sockaddr_in *)&sockname, &namelen) == -1)
                getsockname(cfd, (struct sockaddr *)&sockname, &namelen);
 
@@ -2630,10 +2670,10 @@ int event_accept(int fd) {
 
        if ((global.mode & MODE_DEBUG) && (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) {
            struct sockaddr_in sockname;
-           socklen_t namelen;
+           socklen_t namelen = sizeof(sockname);
            int len;
-           namelen = sizeof(sockname);
            if (addr.ss_family != AF_INET ||
+               !(s->proxy->options & PR_O_TRANSP) ||
                get_original_dst(cfd, (struct sockaddr_in *)&sockname, &namelen) == -1)
                getsockname(cfd, (struct sockaddr *)&sockname, &namelen);
 
@@ -2713,11 +2753,18 @@ int event_accept(int fd) {
            FD_SET(cfd, StaticReadEvent);
        }
 
+#if defined(DEBUG_FULL) && defined(ENABLE_EPOLL)
+       if (PrevReadEvent) {
+           assert(!(FD_ISSET(cfd, PrevReadEvent)));
+           assert(!(FD_ISSET(cfd, PrevWriteEvent)));
+       }
+#endif
        fd_insert(cfd);
 
        tv_eternity(&s->cnexpire);
        tv_eternity(&s->srexpire);
        tv_eternity(&s->swexpire);
+       tv_eternity(&s->crexpire);
        tv_eternity(&s->cwexpire);
 
        if (s->proxy->clitimeout) {
@@ -2738,7 +2785,7 @@ int event_accept(int fd) {
        actconn++;
        totalconn++;
 
-       // fprintf(stderr, "accepting from %p => %d conn, %d total\n", p, actconn, totalconn);
+       // fprintf(stderr, "accepting from %p => %d conn, %d total, task=%p\n", p, actconn, totalconn, t);
     } /* end of while (p->nbconn < p->maxconn) */
     return 0;
 }
@@ -2755,8 +2802,8 @@ int event_srv_chk_w(int fd) {
     struct server *s = t->context;
 
     int skerr;
-    socklen_t lskerr;
-    lskerr = sizeof(skerr);
+    socklen_t lskerr = sizeof(skerr);
+
     getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
     /* in case of TCP only, this tells us if the connection succeeded */
     if (skerr)
@@ -2802,14 +2849,16 @@ int event_srv_chk_r(int fd) {
     struct task *t = fdtab[fd].owner;
     struct server *s = t->context;
 
-    int skerr, lskerr;
-    lskerr = sizeof(skerr);
-
     s->result = len = -1;
 #ifndef MSG_NOSIGNAL
-    getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
-    if (!skerr)
-       len = recv(fd, reply, sizeof(reply), 0);
+    {
+       int skerr;
+       socklen_t lskerr = sizeof(skerr);
+
+       getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
+       if (!skerr)
+           len = recv(fd, reply, sizeof(reply), 0);
+    }
 #else
     /* Warning! Linux returns EAGAIN on SO_ERROR if data are still available
      * but the connection was closed on the remote end. Fortunately, recv still
@@ -3150,8 +3199,8 @@ int process_cli(struct session *t) {
                  asession_temp->sessid = local_asession.sessid;
                  asession_temp->serverid = local_asession.serverid;
                  chtbl_insert(&(t->proxy->htbl_proxy), (void *) asession_temp);
-               } /* end if(chtbl_lookup()) */
-               else{
+               } /* end if (chtbl_lookup()) */
+               else {
                  /*free wasted memory;*/
                  pool_free_to(apools.sessid, local_asession.sessid);
                }
@@ -3175,20 +3224,20 @@ int process_cli(struct session *t) {
                                t->flags |= SN_CK_VALID | SN_DIRECT;
                                t->srv = srv;
                                break;
-                           }else {
+                           } else {
                                t->flags &= ~SN_CK_MASK;
                                t->flags |= SN_CK_DOWN;
                            }
-                       }/* end if(strcmp()) */
+                       } /* end if (strcmp()) */
                        srv = srv->next;
                    }/* end while(srv) */
                }/* end else of if (asession_temp->serverid == NULL) */
-             }/* end if(strncasecmp(request_line,t->proxy->appsession_name,apssesion_name_len) == 0) */
+             }/* end if (strncasecmp(request_line,t->proxy->appsession_name,apssesion_name_len) == 0) */
              else {
                //fprintf(stderr,">>>>>>>>>>>>>>>>>>>>>>NO SESSION\n");
              }
              method_checked = 1;
-           }/* end if(!method_checked ...) */
+           } /* end if (!method_checked ...) */
            else{
              //printf("No Methode-Header with Session-String\n");
            }
@@ -3520,7 +3569,7 @@ int process_cli(struct session *t) {
                            } else {
                                struct server *srv = t->proxy->srv;
                                while (srv) {
-                                   if(strcmp(srv->id, asession_temp->serverid) == 0) {
+                                   if (strcmp(srv->id, asession_temp->serverid) == 0) {
                                        if (srv->state & SRV_RUNNING || t->proxy->options & PR_O_PERSIST) {
                                            /* we found the server and it's usable */
                                            t->flags &= ~SN_CK_MASK;
@@ -3665,6 +3714,7 @@ int process_cli(struct session *t) {
            if (t->proxy->clitimeout)
                tv_delayfrom(&t->crexpire, &now, t->proxy->clitimeout);
            t->cli_state = CL_STSHUTW;
+           //fprintf(stderr,"%p:%s(%d), c=%d, s=%d\n", t, __FUNCTION__, __LINE__, t->cli_state, t->cli_state);
            return 1;
        }
        /* read timeout */
@@ -3830,6 +3880,7 @@ int process_cli(struct session *t) {
                /* stop reading until we get some space */
                FD_CLR(t->cli_fd, StaticReadEvent);
                tv_eternity(&t->crexpire);
+               //fprintf(stderr,"%p:%s(%d), c=%d, s=%d\n", t, __FUNCTION__, __LINE__, t->cli_state, t->cli_state);
            }
        }
        else {
@@ -3840,6 +3891,7 @@ int process_cli(struct session *t) {
                    tv_delayfrom(&t->crexpire, &now, t->proxy->clitimeout);
                else
                    tv_eternity(&t->crexpire);
+               //fprintf(stderr,"%p:%s(%d), c=%d, s=%d\n", t, __FUNCTION__, __LINE__, t->cli_state, t->cli_state);
            }
        }
        return 0;
@@ -4386,10 +4438,10 @@ int process_srv(struct session *t) {
 
                      /* Cool... it's the right one */
 
-                     size_t server_id_len = strlen(t->srv->id)+1;
+                     size_t server_id_len = strlen(t->srv->id) + 1;
                      asession_temp = &local_asession;
                      
-                     if((asession_temp->sessid = pool_alloc_from(apools.sessid, apools.ses_msize)) == NULL){
+                     if ((asession_temp->sessid = pool_alloc_from(apools.sessid, apools.ses_msize)) == NULL) {
                        Alert("Not enought Memory process_srv():asession->sessid:malloc().\n");
                        send_log(t->proxy, LOG_ALERT, "Not enought Memory process_srv():asession->sessid:malloc().\n");
                      }
@@ -4407,22 +4459,22 @@ int process_srv(struct session *t) {
                          asession_temp->sessid = local_asession.sessid;
                          asession_temp->serverid = local_asession.serverid;
                          chtbl_insert(&(t->proxy->htbl_proxy), (void *) asession_temp);
-                     }/* end if(chtbl_lookup()) */
-                     else
-                     {
+                     }/* end if (chtbl_lookup()) */
+                     else {
                        /* free wasted memory */
                        pool_free_to(apools.sessid, local_asession.sessid);
-                     } /* end else from if(chtbl_lookup()) */
+                     } /* end else from if (chtbl_lookup()) */
                      
-                     if(asession_temp->serverid == NULL){
-                       if((asession_temp->serverid = pool_alloc_from(apools.serverid, apools.ser_msize)) == NULL){
+                     if (asession_temp->serverid == NULL) {
+                       if ((asession_temp->serverid = pool_alloc_from(apools.serverid, apools.ser_msize)) == NULL) {
                          Alert("Not enought Memory process_srv():asession->sessid:malloc().\n");
                          send_log(t->proxy, LOG_ALERT, "Not enought Memory process_srv():asession->sessid:malloc().\n");
                        }
                        asession_temp->serverid[0] = '\0';
                      }
                      
-                     if(asession_temp->serverid[0] == '\0') memcpy(asession_temp->serverid,t->srv->id,server_id_len);
+                     if (asession_temp->serverid[0] == '\0')
+                         memcpy(asession_temp->serverid,t->srv->id,server_id_len);
                      
                      tv_delayfrom(&asession_temp->expire, &now, t->proxy->appsession_timeout);
 
@@ -4493,6 +4545,7 @@ int process_srv(struct session *t) {
            tv_eternity(&t->srexpire);
            shutdown(t->srv_fd, SHUT_RD);
            t->srv_state = SV_STSHUTR;
+           //fprintf(stderr,"%p:%s(%d), c=%d, s=%d\n", t, __FUNCTION__, __LINE__, t->cli_state, t->cli_state);
            return 1;
        }       
        /* read timeout : return a 504 to the client.
@@ -4608,6 +4661,7 @@ int process_srv(struct session *t) {
            tv_eternity(&t->srexpire);
            shutdown(t->srv_fd, SHUT_RD);
            t->srv_state = SV_STSHUTR;
+           //fprintf(stderr,"%p:%s(%d), c=%d, s=%d\n", t, __FUNCTION__, __LINE__, t->cli_state, t->cli_state);
            return 1;
        }
        /* end of client read and no more data to send */
@@ -4811,7 +4865,8 @@ int process_srv(struct session *t) {
 /* 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
+ * the time the task accepts to wait, or TIME_ETERNITY for
+ * infinity.
  */
 int process_session(struct task *t) {
     struct session *s = t->context;
@@ -4838,7 +4893,7 @@ int process_session(struct task *t) {
        /* restore t to its place in the task list */
        task_queue(t);
 
-       return tv_remain(&now, &t->expire); /* nothing more to do */
+       return tv_remain2(&now, &t->expire); /* nothing more to do */
     }
 
     s->proxy->nbconn--;
@@ -4862,14 +4917,14 @@ int process_session(struct task *t) {
     task_delete(t);
     session_free(s);
     task_free(t);
-    return -1; /* rest in peace for eternity */
+    return TIME_ETERNITY; /* rest in peace for eternity */
 }
 
 
 
 /*
  * manages a server health-check. Returns
- * the time the task accepts to wait, or -1 for infinity.
+ * the time the task accepts to wait, or TIME_ETERNITY for infinity.
  */
 int process_chk(struct task *t) {
     struct server *s = t->context;
@@ -4882,7 +4937,7 @@ int process_chk(struct task *t) {
        //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);
+           return tv_remain2(&now, &t->expire);
        }
        
        /* we'll initiate a new check */
@@ -5022,7 +5077,7 @@ int process_chk(struct task *t) {
     //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);
+    return tv_remain2(&now, &t->expire);
 }
 
 
@@ -5046,7 +5101,7 @@ int process_runnable_tasks() {
   int time2;
   struct task *t, *tnext;
 
-  next_time = -1; /* set the timer to wait eternally first */
+  next_time = TIME_ETERNITY; /* set the timer to wait eternally first */
 
   /* look for expired tasks and add them to the run queue.
    */
@@ -5056,10 +5111,13 @@ int process_runnable_tasks() {
       if (t->state & TASK_RUNNING)
          continue;
       
+      if (tv_iseternity(&t->expire))
+         continue;
+
       /* 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) {
+      if (tv_cmp_ms(&t->expire, &now) <= 0) {
          task_wakeup(&rq, t);
       }
       else {
@@ -5194,6 +5252,23 @@ int epoll_loop(int action) {
                  ev.events = (sr ? EPOLLIN : 0) | (sw ? EPOLLOUT : 0);
                  ev.data.fd = fd;
 
+#ifdef EPOLL_CTL_MOD_WORKAROUND
+                 /* I encountered a rarely reproducible problem with
+                  * EPOLL_CTL_MOD where a modified FD (systematically
+                  * the one in epoll_events[0], fd#7) would sometimes
+                  * be set EPOLL_OUT while asked for a read ! This is
+                  * with the 2.4 epoll patch. The workaround is to
+                  * delete then recreate in case of modification.
+                  * This is in 2.4 up to epoll-lt-0.21 but not in 2.6
+                  * nor RHEL kernels.
+                  */
+
+                 if ((pr | pw) && fdtab[fd].state != FD_STCLOSE)
+                     epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &ev);
+
+                 if ((sr | sw))
+                     epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
+#else
                  if ((pr | pw)) {
                      /* the file-descriptor already exists... */
                      if ((sr | sw)) {
@@ -5217,6 +5292,7 @@ int epoll_loop(int action) {
                          //  exit(1);
                      }
                  }
+#endif // EPOLL_CTL_MOD_WORKAROUND
              }
              ((int*)PrevReadEvent)[fds] = rn;
              ((int*)PrevWriteEvent)[fds] = wn;
@@ -5538,7 +5614,7 @@ 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(). It returns the time left before next expiration event
- * during stop time, -1 otherwise.
+ * during stop time, TIME_ETERNITY otherwise.
  */
 static int maintain_proxies(void) {
     struct proxy *p;
@@ -5546,7 +5622,7 @@ static int maintain_proxies(void) {
     int tleft; /* time left */
 
     p = proxy;
-    tleft = -1; /* infinite time */
+    tleft = TIME_ETERNITY; /* infinite time */
 
     /* if there are enough free sessions, we'll activate proxies */
     if (actconn < global.maxconn) {
@@ -5587,7 +5663,7 @@ static int maintain_proxies(void) {
        while (p) {
            if (p->state != PR_STDISABLED) {
                int t;
-               t = tv_remain(&now, &p->stop_time);
+               t = tv_remain2(&now, &p->stop_time);
                if (t == 0) {
                    Warning("Proxy %s stopped.\n", p->id);
                    send_log(p, LOG_WARNING, "Proxy %s stopped.\n", p->id);
@@ -6327,6 +6403,18 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
            /* don't log empty requests */
            curproxy->options |= PR_O_NULLNOLOG;
        }
+       else if (!strcmp(args[1], "tcpka")) {
+           /* enable TCP keep-alives on client and server sessions */
+           curproxy->options |= PR_O_TCP_CLI_KA | PR_O_TCP_SRV_KA;
+       }
+       else if (!strcmp(args[1], "clitcpka")) {
+           /* enable TCP keep-alives on client sessions */
+           curproxy->options |= PR_O_TCP_CLI_KA;
+       }
+       else if (!strcmp(args[1], "srvtcpka")) {
+           /* enable TCP keep-alives on server sessions */
+           curproxy->options |= PR_O_TCP_SRV_KA;
+       }
        else if (!strcmp(args[1], "httpchk")) {
            /* use HTTP request to check servers' health */
            if (curproxy->check_req != NULL) {
@@ -7611,7 +7699,7 @@ int start_proxies() {
     return 0;
 }
 
-int match_str(const void *key1, const void *key2){
+int match_str(const void *key1, const void *key2) {
 
     appsess *temp1,*temp2;
     temp1 = (appsess *)key1;
@@ -7623,7 +7711,7 @@ int match_str(const void *key1, const void *key2){
     return (strcmp(temp1->sessid,temp2->sessid) == 0);
 }/* end match_str */
 
-void destroy(void *data){
+void destroy(void *data) {
     appsess *temp1;
 
     //printf("destroy called\n");
@@ -7659,7 +7747,7 @@ void pool_destroy(void **pool)
     }
 }/* end pool_destroy() */
 
-void deinit(void){
+void deinit(void) {
     struct proxy *p = proxy;
     struct cap_hdr *h,*h_next;
     struct server *s,*s_next;
@@ -7680,13 +7768,13 @@ void deinit(void){
 
        /* only strup if the user have set in config.
           When should we free it?!
-          if(p->errmsg.msg400) free(p->errmsg.msg400);
-          if(p->errmsg.msg403) free(p->errmsg.msg403);
-          if(p->errmsg.msg408) free(p->errmsg.msg408);
-          if(p->errmsg.msg500) free(p->errmsg.msg500);
-          if(p->errmsg.msg502) free(p->errmsg.msg502);
-          if(p->errmsg.msg503) free(p->errmsg.msg503);
-          if(p->errmsg.msg504) free(p->errmsg.msg504);
+          if (p->errmsg.msg400) free(p->errmsg.msg400);
+          if (p->errmsg.msg403) free(p->errmsg.msg403);
+          if (p->errmsg.msg408) free(p->errmsg.msg408);
+          if (p->errmsg.msg500) free(p->errmsg.msg500);
+          if (p->errmsg.msg502) free(p->errmsg.msg502);
+          if (p->errmsg.msg503) free(p->errmsg.msg503);
+          if (p->errmsg.msg504) free(p->errmsg.msg504);
        */
        if (p->appsession_name)
            free(p->appsession_name);
@@ -7715,10 +7803,10 @@ void deinit(void){
        s = p->srv;
        while (s) {
            s_next = s->next;
-           if(s->id)
+           if (s->id)
                free(s->id);
            
-           if(s->cookie)
+           if (s->cookie)
                free(s->cookie);
            
            free(s);