]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MAJOR] rework of the server FSM
authorWilly Tarreau <w@1wt.eu>
Sun, 19 Oct 2008 05:30:41 +0000 (07:30 +0200)
committerWilly Tarreau <w@1wt.eu>
Sun, 2 Nov 2008 09:19:04 +0000 (10:19 +0100)
srv_state has been removed from HTTP state machines, and states
have been split in either TCP states or analyzers. For instance,
the TARPIT state has just become a simple analyzer.

New flags have been added to the struct buffer to compensate this.
The high-level stream processors sometimes need to force a disconnection
without touching a file-descriptor (eg: report an error). But if
they touched BF_SHUTW or BF_SHUTR, the file descriptor would not
be closed. Thus, the two SHUT?_NOW flags have been added so that
an application can request a forced close which the stream interface
will be forced to obey.

During this change, a new BF_HIJACK flag was added. It will
be used for data generation, eg during a stats dump. It
prevents the producer on a buffer from sending data into it.

  BF_SHUTR_NOW  /* the producer must shut down for reads ASAP  */
  BF_SHUTW_NOW  /* the consumer must shut down for writes ASAP */
  BF_HIJACK     /* the producer is temporarily replaced        */

BF_SHUTW_NOW has precedence over BF_HIJACK. BF_HIJACK has
precedence over BF_MAY_FORWARD (so that it does not need it).

New functions buffer_shutr_now(), buffer_shutw_now(), buffer_abort()
are provided to manipulate BF_SHUT* flags.

A new type "stream_interface" has been added to describe both
sides of a buffer. A stream interface has states and error
reporting. The session now has two stream interfaces (one per
side). Each buffer has stream_interface pointers to both
consumer and producer sides.

The server-side file descriptor has moved to its stream interface,
so that even the buffer has access to it.

process_srv() has been split into three parts :
  - tcp_get_connection() obtains a connection to the server
  - tcp_connection_failed() tests if a previously attempted
    connection has succeeded or not.
  - process_srv_data() only manages the data phase, and in
    this sense should be roughly equivalent to process_cli.

Little code has been removed, and a lot of old code has been
left in comments for now.

12 files changed:
include/proto/buffers.h
include/proto/proto_http.h
include/proto/stream_sock.h
include/types/buffers.h
include/types/session.h
include/types/stream_interface.h [new file with mode: 0644]
src/backend.c
src/client.c
src/proto_http.c
src/proto_uxst.c
src/senddata.c
src/stream_sock.c

index 4ceedc077e6ac6314ebaf7bb7fb933e496403f93..3b5e2bdc7038d2e459f0f1052069a0166ac4fb44 100644 (file)
@@ -47,6 +47,7 @@ static inline void buffer_init(struct buffer *buf)
 {
        buf->l = buf->total = 0;
        buf->analysers = 0;
+       buf->cons = NULL;
        buf->flags = BF_EMPTY;
        buf->r = buf->lr = buf->w = buf->data;
        buf->rlim = buf->data + BUFSIZE;
@@ -89,6 +90,24 @@ static inline void buffer_shutw(struct buffer *buf)
        buf->flags |= BF_SHUTW;
 }
 
+/* marks the buffer as "shutdown" ASAP for reads */
+static inline void buffer_shutr_now(struct buffer *buf)
+{
+       buf->flags |= BF_SHUTR_NOW;
+}
+
+/* marks the buffer as "shutdown" ASAP for writes */
+static inline void buffer_shutw_now(struct buffer *buf)
+{
+       buf->flags |= BF_SHUTW_NOW;
+}
+
+/* marks the buffer as "shutdown" ASAP in both directions */
+static inline void buffer_abort(struct buffer *buf)
+{
+       buf->flags |= BF_SHUTR_NOW | BF_SHUTW_NOW;
+}
+
 /* returns the maximum number of bytes writable at once in this buffer */
 static inline int buffer_max(const struct buffer *buf)
 {
index fc1e01d38cfc22f3fe559fc09c8dd9ad0d50a55e..17f302dc5cf64c623718981509c2039491476a55 100644 (file)
@@ -60,7 +60,8 @@ extern const char http_is_ver_token[256];
 int event_accept(int fd);
 void process_session(struct task *t, int *next);
 int process_cli(struct session *t);
-int process_srv(struct session *t);
+int process_srv_data(struct session *t);
+int process_srv_conn(struct session *t);
 int process_request(struct session *t);
 int process_response(struct session *t);
 
index 5619e60dcacba4ba64346b407511f1377b408402..d57ddf5b64b5c7b956080502ccc96266b677680e 100644 (file)
@@ -2,7 +2,7 @@
   include/proto/stream_sock.h
   This file contains client-side definitions.
 
-  Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
+  Copyright (C) 2000-2008 Willy Tarreau - w@1wt.eu
   
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
@@ -27,6 +27,7 @@
 #include <sys/types.h>
 
 #include <common/config.h>
+#include <types/stream_interface.h>
 
 
 /* main event functions used to move data between sockets and buffers */
index f516ffe14e51f63549e48767f012b5a87ec8cb0d..637427125dada778cf7c87b6bd723613a1e08239 100644 (file)
 #define BF_READ_TIMEOUT     32768  /* timeout while waiting for producer */
 #define BF_WRITE_TIMEOUT    65536  /* timeout while waiting for consumer */
 
+/* When either BF_SHUTR_NOW or BF_HIJACK is set, it is strictly forbidden for
+ * the stream interface to alter the buffer contents. When BF_SHUTW_NOW is set,
+ * it is strictly forbidden for the stream interface to send anything from the
+ * buffer.
+ */
+#define BF_SHUTR_NOW       131072  /* the producer must shut down for reads ASAP */
+#define BF_SHUTW_NOW       262144  /* the consumer must shut down for writes ASAP */
+#define BF_HIJACK          524288  /* the producer is temporarily replaced */
+
 
 /* Analysers (buffer->analysers).
  * Those bits indicate that there are some processing to do on the buffer
@@ -68,7 +77,8 @@
 #define AN_REQ_INSPECT          0x00000001  /* inspect request contents */
 #define AN_REQ_HTTP_HDR         0x00000002  /* inspect HTTP request headers */
 #define AN_REQ_HTTP_BODY        0x00000004  /* inspect HTTP request body */
-#define AN_RTR_HTTP_HDR         0x00000008  /* inspect HTTP response headers */
+#define AN_REQ_HTTP_TARPIT      0x00000008  /* wait for end of HTTP tarpit */
+#define AN_RTR_HTTP_HDR         0x00000010  /* inspect HTTP response headers */
 
 /* describes a chunk of string */
 struct chunk {
@@ -91,6 +101,8 @@ struct buffer {
        unsigned char xfer_large;       /* number of consecutive large xfers */
        unsigned char xfer_small;       /* number of consecutive small xfers */
        unsigned long long total;       /* total data read */
+       struct stream_interface *prod;  /* producer attached to this buffer */
+       struct stream_interface *cons;  /* consumer attached to this buffer */
        char data[BUFSIZE];
 };
 
index 30359560729bb9f2e7b68dfd2383bdb931ed736b..2d0439b7d6cc1ddd9132fde98f1e722d2605ef32 100644 (file)
@@ -36,6 +36,7 @@
 #include <types/proxy.h>
 #include <types/queue.h>
 #include <types/server.h>
+#include <types/stream_interface.h>
 #include <types/task.h>
 
 
@@ -156,7 +157,6 @@ struct session {
        struct proxy *fe;                       /* the proxy this session depends on for the client side */
        struct proxy *be;                       /* the proxy this session depends on for the server side */
        int cli_fd;                             /* the client side fd */
-       int srv_fd;                             /* the server side fd */
        int cli_state;                          /* state of the client side */
        int srv_state;                          /* state of the server side */
        int conn_retries;                       /* number of connect retries left */
@@ -164,6 +164,7 @@ struct session {
        unsigned term_trace;                    /* term trace: 4*8 bits indicating which part of the code closed */
        struct buffer *req;                     /* request buffer */
        struct buffer *rep;                     /* response buffer */
+       struct stream_interface si[2];          /* client and server stream interfaces */
        struct sockaddr_storage cli_addr;       /* the client address */
        struct sockaddr_storage frt_addr;       /* the frontend address reached by the client if SN_FRT_ADDR_SET is set */
        struct sockaddr_in srv_addr;            /* the address to connect to */
diff --git a/include/types/stream_interface.h b/include/types/stream_interface.h
new file mode 100644 (file)
index 0000000..edfb758
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+  include/types/stream_interface.h
+  This file describes the stream_interface struct and associated constants.
+
+  Copyright (C) 2000-2008 Willy Tarreau - w@1wt.eu
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation, version 2.1
+  exclusively.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#ifndef _TYPES_STREAM_INTERFACE_H
+#define _TYPES_STREAM_INTERFACE_H
+
+#include <stdlib.h>
+
+#include <common/config.h>
+
+/* A stream interface must have its own errors independantly of the buffer's,
+ * so that applications can rely on what the buffer reports while the stream
+ * interface is performing some retries (eg: connection error).
+ */
+enum {
+       SI_ST_INI = 0,           /* interface not initialized yet and might not exist */
+       SI_ST_QUE,               /* interface waiting in queue */
+       SI_ST_TAR,               /* interface in turn-around state after failed connect attempt */
+       SI_ST_ASS,               /* server just assigned to this interface */
+       SI_ST_CON,               /* initiated connection request (resource exists) */
+       SI_ST_EST,               /* connection established (resource exists) */
+       SI_ST_CLO,               /* stream interface closed, might not existing anymore */
+};
+
+/* error types reported on the streams interface for more accurate reporting */
+enum {
+       SI_ET_NONE = 0,         /* no error yet, leave it to zero */
+       SI_ET_QUEUE_TO,         /* queue timeout */
+       SI_ET_QUEUE_ERR,        /* queue error (eg: full) */
+       SI_ET_QUEUE_ABRT,       /* aborted in queue by external cause */
+       SI_ET_CONN_TO,          /* connection timeout */
+       SI_ET_CONN_ERR,         /* connection error (eg: no server available) */
+       SI_ET_CONN_ABRT,        /* connection aborted by external cause (eg: abort) */
+       SI_ET_CONN_OTHER,       /* connection aborted for other reason (eg: 500) */
+       SI_ET_DATA_TO,          /* timeout during data phase */
+       SI_ET_DATA_ERR,         /* error during data phase */
+       SI_ET_DATA_ABRT,        /* data phase aborted by external cause */
+};
+
+struct stream_interface {
+       unsigned int state;     /* SI_ST* */
+       int err_type;           /* first error detected, one of SI_ET_* */
+       void *err_loc;          /* commonly the server, NULL when SI_ET_NONE */
+       int fd;                 /* file descriptor for a stream driver when known */
+};
+
+
+#endif /* _TYPES_STREAM_INTERFACE_H */
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ */
index 2a39d77310ad91b904fdfe59875aff23f184488d..f51ac8fc38ee3bdef8fc4cf99fb852418de63e4b 100644 (file)
@@ -1646,7 +1646,7 @@ int connect_server(struct session *s)
                        return SN_ERR_INTERNAL;
        }
 
-       if ((fd = s->srv_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+       if ((fd = s->req->cons->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
                qfprintf(stderr, "Cannot get a server socket.\n");
 
                if (errno == ENFILE)
@@ -1818,6 +1818,7 @@ int connect_server(struct session *s)
        fd_insert(fd);
        EV_FD_SET(fd, DIR_WR);  /* for connect status */
 
+       s->req->cons->state = SI_ST_CON;
        if (s->srv) {
                s->srv->cur_sess++;
                if (s->srv->cur_sess > s->srv->cur_sess_max)
@@ -1833,8 +1834,8 @@ int connect_server(struct session *s)
 
 /*
  * This function checks the retry count during the connect() job.
- * It updates the session's srv_state and retries, so that the caller knows
- * what it has to do. It uses the last connection error to set the log when
+ * It updates the session's retries, so that the caller knows what it
+ * has to do. It uses the last connection error to set the log when
  * it expires. It returns 1 when it has expired, and 0 otherwise.
  */
 int srv_count_retry_down(struct session *t, int conn_err)
@@ -1844,9 +1845,15 @@ int srv_count_retry_down(struct session *t, int conn_err)
 
        if (t->conn_retries < 0) {
                /* if not retryable anymore, let's abort */
-               t->req->wex = TICK_ETERNITY;
-               srv_close_with_err(t, conn_err, SN_FINST_C,
-                                  503, error_message(t, HTTP_ERR_503));
+               //t->req->wex = TICK_ETERNITY;
+               //srv_close_with_err(t, conn_err, SN_FINST_C,
+               //                 503, error_message(t, HTTP_ERR_503));
+
+               if (!t->req->cons->err_type) {
+                       t->req->cons->err_type = SI_ET_CONN_ERR;
+                       t->req->cons->err_loc = t->srv;
+               }
+
                if (t->srv)
                        t->srv->failed_conns++;
                t->be->failed_conns++;
@@ -1864,9 +1871,9 @@ int srv_count_retry_down(struct session *t, int conn_err)
     
 /*
  * This function performs the retryable part of the connect() job.
- * It updates the session's srv_state and retries, so that the caller knows
- * what it has to do. It returns 1 when it breaks out of the loop, or 0 if
- * it needs to redispatch.
+ * It updates the session's and retries, so that the caller knows
+ * what it has to do. It returns 1 when it breaks out of the loop,
+ * or 0 if it needs to redispatch.
  */
 int srv_retryable_connect(struct session *t)
 {
@@ -1882,15 +1889,19 @@ int srv_retryable_connect(struct session *t)
        
                case SN_ERR_NONE:
                        //fprintf(stderr,"0: c=%d, s=%d\n", c, s);
-                       t->srv_state = SV_STCONN;
                        if (t->srv)
                                t->srv->cum_sess++;
                        return 1;
            
                case SN_ERR_INTERNAL:
-                       t->req->wex = TICK_ETERNITY;
-                       srv_close_with_err(t, SN_ERR_INTERNAL, SN_FINST_C,
-                                          500, error_message(t, HTTP_ERR_500));
+                       if (!t->req->cons->err_type) {
+                               t->req->cons->err_type = SI_ET_CONN_OTHER;
+                               t->req->cons->err_loc = t->srv;
+                       }
+
+                       //t->req->wex = TICK_ETERNITY;
+                       //srv_close_with_err(t, SN_ERR_INTERNAL, SN_FINST_C,
+                       //                 500, error_message(t, HTTP_ERR_500));
                        if (t->srv)
                                t->srv->cum_sess++;
                        if (t->srv)
@@ -1902,9 +1913,8 @@ int srv_retryable_connect(struct session *t)
                        return 1;
                }
                /* ensure that we have enough retries left */
-               if (srv_count_retry_down(t, conn_err)) {
+               if (srv_count_retry_down(t, conn_err))
                        return 1;
-               }
        } while (t->srv == NULL || t->conn_retries > 0 || !(t->be->options & PR_O_REDISP));
 
        /* We're on our last chance, and the REDISP option was specified.
@@ -1959,9 +1969,14 @@ int srv_redispatch_connect(struct session *t)
                        goto redispatch;
                }
 
-               t->req->wex = TICK_ETERNITY;
-               srv_close_with_err(t, SN_ERR_SRVTO, SN_FINST_Q,
-                                  503, error_message(t, HTTP_ERR_503));
+               //t->req->wex = TICK_ETERNITY;
+               //srv_close_with_err(t, SN_ERR_SRVTO, SN_FINST_Q,
+               //                 503, error_message(t, HTTP_ERR_503));
+
+               if (!t->req->cons->err_type) {
+                       t->req->cons->err_type = SI_ET_QUEUE_ERR;
+                       t->req->cons->err_loc = t->srv;
+               }
 
                t->srv->failed_conns++;
                t->be->failed_conns++;
@@ -1969,24 +1984,35 @@ int srv_redispatch_connect(struct session *t)
 
        case SRV_STATUS_NOSRV:
                /* note: it is guaranteed that t->srv == NULL here */
-               t->req->wex = TICK_ETERNITY;
-               srv_close_with_err(t, SN_ERR_SRVTO, SN_FINST_C,
-                                  503, error_message(t, HTTP_ERR_503));
+               //t->req->wex = TICK_ETERNITY;
+               //srv_close_with_err(t, SN_ERR_SRVTO, SN_FINST_C,
+               //                 503, error_message(t, HTTP_ERR_503));
+
+               if (!t->req->cons->err_type) {
+                       t->req->cons->err_type = SI_ET_CONN_ERR;
+                       t->req->cons->err_loc = NULL;
+               }
 
                t->be->failed_conns++;
                return 1;
 
        case SRV_STATUS_QUEUED:
                t->req->wex = tick_add_ifset(now_ms, t->be->timeout.queue);
-               t->srv_state = SV_STIDLE;
+               t->req->cons->state = SI_ST_QUE;
                /* do nothing else and do not wake any other session up */
                return 1;
 
        case SRV_STATUS_INTERNAL:
        default:
-               t->req->wex = TICK_ETERNITY;
-               srv_close_with_err(t, SN_ERR_INTERNAL, SN_FINST_C,
-                                  500, error_message(t, HTTP_ERR_500));
+               //t->req->wex = TICK_ETERNITY;
+               //srv_close_with_err(t, SN_ERR_INTERNAL, SN_FINST_C,
+               //                 500, error_message(t, HTTP_ERR_500));
+
+               if (!t->req->cons->err_type) {
+                       t->req->cons->err_type = SI_ET_CONN_OTHER;
+                       t->req->cons->err_loc = t->srv;
+               }
+
                if (t->srv)
                        t->srv->cum_sess++;
                if (t->srv)
index a6c8ebfb81d4498fcd645f22060159068bbcf07f..5df5c0dff5b92e7d06165a163b03627b2ee509d2 100644 (file)
@@ -168,11 +168,19 @@ int event_accept(int fd) {
                }
 
                s->cli_state = CL_STDATA;
-               s->srv_state = SV_STIDLE;
                s->req = s->rep = NULL; /* will be allocated later */
 
+               s->si[0].state = SI_ST_EST;
+               s->si[0].err_type = SI_ET_NONE;
+               s->si[0].err_loc = NULL;
+               s->si[0].fd = cfd;
                s->cli_fd = cfd;
-               s->srv_fd = -1;
+
+               s->si[1].state = SI_ST_INI;
+               s->si[1].err_type = SI_ET_NONE;
+               s->si[1].err_loc = NULL;
+               s->si[1].fd = -1; /* just to help with debugging */
+
                s->srv = s->prev_srv = s->srv_conn = NULL;
                s->pend_pos = NULL;
                s->conn_retries = s->be->conn_retries;
@@ -326,6 +334,9 @@ int event_accept(int fd) {
                        goto out_fail_req; /* no memory */
 
                buffer_init(s->req);
+               s->req->prod = &s->si[0];
+               s->req->cons = &s->si[1];
+
                if (p->mode == PR_MODE_HTTP) /* reserve some space for header rewriting */
                        s->req->rlim -= MAXREWRITE;
 
@@ -346,6 +357,8 @@ int event_accept(int fd) {
                        goto out_fail_rep; /* no memory */
 
                buffer_init(s->rep);
+               s->rep->prod = &s->si[1];
+               s->rep->cons = &s->si[0];
 
                s->rep->rto = s->be->timeout.server;
                s->rep->wto = s->fe->timeout.client;
index 700a56e1c0ac95abb50fad4f0310a7f6c73f5494..c065fda21b038b9d85aa1366ccb17bcc19bdeed4 100644 (file)
@@ -366,7 +366,6 @@ const char http_is_ver_token[256] = {
 
 #ifdef DEBUG_FULL
 static char *cli_stnames[4] = { "DAT", "SHR", "SHW", "CLS" };
-static char *srv_stnames[6] = { "IDL", "CON", "DAT", "SHR", "SHW", "CLS" };
 #endif
 
 static void http_sess_log(struct session *s);
@@ -537,14 +536,13 @@ int http_find_header(const char *name,
        return http_find_header2(name, strlen(name), sol, idx, ctx);
 }
 
-/* This function turns the server state into the SV_STCLOSE, and sets
- * indicators accordingly. Note that if <status> is 0, or if the message
- * pointer is NULL, then no message is returned.
+/* This function shuts down the buffers on the server side, and sets indicators
+ * accordingly. The server's fd is supposed to already be closed. Note that if
+ * <status> is 0, or if the message pointer is NULL, then no message is returned.
  */
 void srv_close_with_err(struct session *t, int err, int finst,
                        int status, const struct chunk *msg)
 {
-       t->srv_state = SV_STCLOSE;
        t->rep->flags |= BF_MAY_FORWARD;
        buffer_shutw(t->req);
        buffer_shutr(t->rep);
@@ -675,16 +673,22 @@ void process_session(struct task *t, int *next)
                if (tick_is_expired(s->req->rex, now_ms))
                        s->req->flags |= BF_READ_TIMEOUT;
        
-               if (tick_is_expired(s->req->wex, now_ms))
-                       s->req->flags |= BF_WRITE_TIMEOUT;
-       
-               if (tick_is_expired(s->rep->rex, now_ms))
-                       s->rep->flags |= BF_READ_TIMEOUT;
+               //if (tick_is_expired(s->req->wex, now_ms))
+               //      s->req->flags |= BF_WRITE_TIMEOUT;
+               //
+               //if (tick_is_expired(s->rep->rex, now_ms))
+               //      s->rep->flags |= BF_READ_TIMEOUT;
        
                if (tick_is_expired(s->rep->wex, now_ms))
                        s->rep->flags |= BF_WRITE_TIMEOUT;
        }
 
+       //if (fdtab[s->cli_fd].state == FD_STERROR) {
+       //      fprintf(stderr, "s=%p fd=%d req=%p rep=%p cs=%d ss=%d, term=%08x\n",
+       //              s, s->cli_fd, s->req, s->rep, s->cli_state,
+       //              s->si[1].state, s->term_trace);
+       //      sleep(1);
+       //}
        do {
                if (resync & PROCESS_REQ) {
                        resync &= ~PROCESS_REQ;
@@ -737,15 +741,31 @@ void process_session(struct task *t, int *next)
                        rpf = s->rep->flags;
 
                        resync &= ~PROCESS_SRV;
-                       if (process_srv(s))
-                               resync |= PROCESS_SRV;
+                       if (s->req->cons->state != SI_ST_CLO) {
+                               if (s->req->cons->state < SI_ST_EST && s->req->flags & BF_MAY_FORWARD)
+                                       process_srv_conn(s);
+
+                               if (s->req->cons->state == SI_ST_EST) {
+                                       if (process_srv_data(s))
+                                               resync |= PROCESS_SRV;
+                               }
 
+                               if (unlikely((s->req->cons->state == SI_ST_CLO) &&
+                                            (global.mode & MODE_DEBUG) &&
+                                            (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)))) {
+                                               int len;
+                                               len = sprintf(trash, "%08x:%s.srvcls[%04x:%04x]\n",
+                                                             s->uniq_id, s->be->id, (unsigned short)s->cli_fd, (unsigned short)s->req->cons->fd);
+                                               write(1, trash, len);
+                               }
+                       }
                        if (rqf != s->req->flags || rpf != s->rep->flags)
                                resync |= PROCESS_ALL & ~PROCESS_SRV;
                }
        } while (resync);
 
-       if (likely(s->cli_state != CL_STCLOSE || s->srv_state != SV_STCLOSE)) {
+       if (likely(s->cli_state != CL_STCLOSE ||
+                  (s->req->cons->state != SI_ST_CLO && s->req->cons->state != SI_ST_INI))) {
 
                if ((s->fe->options & PR_O_CONTSTATS) && (s->flags & SN_BE_ASSIGNED))
                        session_process_counters(s);
@@ -792,7 +812,7 @@ void process_session(struct task *t, int *next)
                int len;
                len = sprintf(trash, "%08x:%s.closed[%04x:%04x] (term_trace=0x%08x)\n",
                              s->uniq_id, s->be->id,
-                             (unsigned short)s->cli_fd, (unsigned short)s->srv_fd,
+                             (unsigned short)s->cli_fd, (unsigned short)s->req->cons->fd,
                              s->term_trace);
                write(1, trash, len);
        }
@@ -1655,9 +1675,9 @@ int process_request(struct session *t)
        struct buffer *req = t->req;
        struct buffer *rep = t->rep;
 
-       DPRINTF(stderr,"[%u] process_req: c=%s s=%s set(r,w)=%d,%d exp(r,w)=%u,%u req=%08x rep=%08x analysers=%02x\n",
-               now_ms,
-               cli_stnames[t->cli_state], srv_stnames[t->srv_state],
+       DPRINTF(stderr,"[%u] %s: c=%s set(r,w)=%d,%d exp(r,w)=%u,%u req=%08x rep=%08x analysers=%02x\n",
+               now_ms, __FUNCTION__,
+               cli_stnames[t->cli_state],
                t->cli_fd >= 0 && fdtab[t->cli_fd].state != FD_STCLOSE ? EV_FD_ISSET(t->cli_fd, DIR_RD) : 0,
                t->cli_fd >= 0 && fdtab[t->cli_fd].state != FD_STCLOSE ? EV_FD_ISSET(t->cli_fd, DIR_WR) : 0,
                req->rex, rep->wex, req->flags, rep->flags, req->analysers);
@@ -1729,10 +1749,11 @@ int process_request(struct session *t)
                        if (ret) {
                                /* we have a matching rule. */
                                if (rule->action == TCP_ACT_REJECT) {
-                                       buffer_shutr(req);
-                                       buffer_shutw(rep);
-                                       fd_delete(t->cli_fd);
-                                       t->cli_state = CL_STCLOSE;
+                                       buffer_abort(req);
+                                       buffer_abort(rep);
+                                       //FIXME: this delete this
+                                       //fd_delete(t->cli_fd);
+                                       //t->cli_state = CL_STCLOSE;
                                        req->analysers = 0;
                                        t->fe->failed_req++;
                                        if (!(t->flags & SN_ERR_MASK))
@@ -1829,7 +1850,7 @@ int process_request(struct session *t)
                        }
 
                        /* 2: have we encountered a close ? */
-                       else if (req->flags & BF_READ_NULL) {
+                       else if (req->flags & (BF_READ_NULL | BF_SHUTR)) {
                                txn->status = 400;
                                client_retnclose(t, error_message(t, HTTP_ERR_400));
                                msg->msg_state = HTTP_MSG_ERROR;
@@ -1858,8 +1879,8 @@ int process_request(struct session *t)
                                return 0;
                        }
 
-                       /* 4: have we encountered a read error or did we have to shutdown ? */
-                       else if (req->flags & (BF_READ_ERROR | BF_SHUTR)) {
+                       /* 4: have we encountered a read error ? */
+                       else if (req->flags & BF_READ_ERROR) {
                                /* we cannot return any message on error */
                                msg->msg_state = HTTP_MSG_ERROR;
                                req->analysers = 0;
@@ -1898,6 +1919,10 @@ int process_request(struct session *t)
                 */
                txn->meth = find_http_meth(&req->data[msg->som], msg->sl.rq.m_l);
 
+               /* we can make use of server redirect on GET and HEAD */
+               if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)
+                       t->flags |= SN_REDIRECTABLE;
+
                /*
                 * 2: check if the URI matches the monitor_uri.
                 * We have to do this for every request which gets in, because
@@ -2498,17 +2523,18 @@ int process_request(struct session *t)
                /* When a connection is tarpitted, we use the tarpit timeout,
                 * which may be the same as the connect timeout if unspecified.
                 * If unset, then set it to zero because we really want it to
-                * eventually expire.
-                * FIXME: this part should be moved elsewhere (eg: on the server side)
+                * eventually expire. We build the tarpit as an analyser.
                 */
                if (txn->flags & TX_CLTARPIT) {
                        buffer_flush(t->req);
                        /* flush the request so that we can drop the connection early
                         * if the client closes first.
                         */
-                       req->wex = tick_add_ifset(now_ms, t->be->timeout.tarpit);
-                       if (!req->wex)
-                               req->wex = now_ms;
+                       req->flags &= ~BF_MAY_FORWARD;
+                       req->analysers |= AN_REQ_HTTP_TARPIT;
+                       req->analyse_exp = tick_add_ifset(now_ms,  t->be->timeout.tarpit);
+                       if (!req->analyse_exp)
+                               req->analyse_exp = now_ms;
                }
 
                /* OK let's go on with the BODY now */
@@ -2530,6 +2556,42 @@ int process_request(struct session *t)
                ; // to keep gcc happy
        }
 
+       if (req->analysers & AN_REQ_HTTP_TARPIT) {
+               struct http_txn *txn = &t->txn;
+
+               /* This connection is being tarpitted. The CLIENT side has
+                * already set the connect expiration date to the right
+                * timeout. We just have to check that the client is still
+                * there and that the timeout has not expired.
+                */
+               if ((req->flags & (BF_READ_NULL|BF_READ_ERROR)) == 0 &&
+                   !tick_is_expired(req->analyse_exp, now_ms))
+                       return 0;
+
+               /* We will set the queue timer to the time spent, just for
+                * logging purposes. We fake a 500 server error, so that the
+                * attacker will not suspect his connection has been tarpitted.
+                * It will not cause trouble to the logs because we can exclude
+                * the tarpitted connections by filtering on the 'PT' status flags.
+                */
+               trace_term(t, TT_HTTP_SRV_2);
+               t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now);
+
+               txn->status = 500;
+               if (req->flags != BF_READ_ERROR)
+                       client_retnclose(t, error_message(t, HTTP_ERR_500));
+
+               req->analysers = 0;
+               req->analyse_exp = TICK_ETERNITY;
+
+               t->fe->failed_req++;
+               if (!(t->flags & SN_ERR_MASK))
+                       t->flags |= SN_ERR_PRXCOND;
+               if (!(t->flags & SN_FINST_MASK))
+                       t->flags |= SN_FINST_T;
+               return 0;
+       }
+
        if (req->analysers & AN_REQ_HTTP_BODY) {
                /* We have to parse the HTTP request body to find any required data.
                 * "balance url_param check_post" should have been the only way to get
@@ -2585,7 +2647,7 @@ int process_request(struct session *t)
                 * buffer closed).
                 */
                if (req->l - body >= limit ||             /* enough bytes! */
-                   req->flags & (BF_FULL | BF_READ_ERROR | BF_READ_NULL | BF_READ_TIMEOUT) ||
+                   req->flags & (BF_FULL | BF_READ_ERROR | BF_SHUTR | BF_READ_NULL | BF_READ_TIMEOUT) ||
                    tick_is_expired(req->analyse_exp, now_ms)) {
                        /* The situation will not evolve, so let's give up on the analysis. */
                        t->logs.tv_request = now;  /* update the request timer to reflect full request */
@@ -2610,13 +2672,13 @@ int process_request(struct session *t)
         * probably reduce one day's debugging session.
         */
 #ifdef DEBUG_DEV
-       if (req->analysers & ~(AN_REQ_INSPECT | AN_REQ_HTTP_HDR | AN_REQ_HTTP_BODY)) {
+       if (req->analysers & ~(AN_REQ_INSPECT | AN_REQ_HTTP_HDR | AN_REQ_HTTP_TARPIT | AN_REQ_HTTP_BODY)) {
                fprintf(stderr, "FIXME !!!! unknown analysers flags %s:%d = 0x%08X\n",
                        __FILE__, __LINE__, req->analysers);
                ABORT_NOW();
        }
 #endif
-       req->analysers &= AN_REQ_INSPECT | AN_REQ_HTTP_HDR | AN_REQ_HTTP_BODY;
+       req->analysers &= AN_REQ_INSPECT | AN_REQ_HTTP_HDR | AN_REQ_HTTP_TARPIT | AN_REQ_HTTP_BODY;
        return 0;
 }
 
@@ -2632,11 +2694,9 @@ int process_response(struct session *t)
        struct buffer *req = t->req;
        struct buffer *rep = t->rep;
 
-       DPRINTF(stderr,"[%u] process_rep: c=%s s=%s set(r,w)=%d,%d exp(r,w)=%u,%u req=%08x rep=%08x analysers=%02x\n",
-               now_ms,
-               cli_stnames[t->cli_state], srv_stnames[t->srv_state],
-               t->srv_fd >= 0 && fdtab[t->srv_fd].state != FD_STCLOSE ? EV_FD_ISSET(t->srv_fd, DIR_RD) : 0,
-               t->srv_fd >= 0 && fdtab[t->srv_fd].state != FD_STCLOSE ? EV_FD_ISSET(t->srv_fd, DIR_WR) : 0,
+       DPRINTF(stderr,"[%u] %s: c=%s exp(r,w)=%u,%u req=%08x rep=%08x analysers=%02x\n",
+               now_ms, __FUNCTION__,
+               cli_stnames[t->cli_state],
                req->rex, rep->wex, req->flags, rep->flags, rep->analysers);
 
        if (rep->analysers & AN_RTR_HTTP_HDR) { /* receiving server headers */
@@ -2701,16 +2761,18 @@ int process_response(struct session *t)
                        /* Invalid response */
                        if (unlikely(msg->msg_state == HTTP_MSG_ERROR)) {
                        hdr_response_bad:
-                               buffer_shutr(rep);
-                               buffer_shutw(req);
-                               fd_delete(t->srv_fd);
+                               //buffer_shutr(rep);
+                               //buffer_shutw(req);
+                               //fd_delete(req->cons->fd);
+                               //req->cons->state = SI_ST_CLO;
+                               buffer_shutr_now(rep);
+                               buffer_shutw_now(req);
                                if (t->srv) {
-                                       t->srv->cur_sess--;
+                                       //t->srv->cur_sess--;
                                        t->srv->failed_resp++;
-                                       sess_change_server(t, NULL);
+                                       //sess_change_server(t, NULL);
                                }
                                t->be->failed_resp++;
-                               t->srv_state = SV_STCLOSE;
                                rep->analysers = 0;
                                txn->status = 502;
                                client_return(t, error_message(t, HTTP_ERR_502));
@@ -2719,24 +2781,23 @@ int process_response(struct session *t)
                                if (!(t->flags & SN_FINST_MASK))
                                        t->flags |= SN_FINST_H;
 
-                               if (t->srv && may_dequeue_tasks(t->srv, t->be))
-                                       process_srv_queue(t->srv);
+                               //if (t->srv && may_dequeue_tasks(t->srv, t->be))
+                               //      process_srv_queue(t->srv);
 
                                return 0;
                        }
                        /* write error to client, read error or close from server */
-                       if (req->flags & BF_WRITE_ERROR ||
-                           rep->flags & (BF_READ_ERROR | BF_READ_NULL | BF_SHUTW)) {
-                               buffer_shutr(rep);
-                               buffer_shutw(req);
-                               fd_delete(t->srv_fd);
+                       if (rep->flags & (BF_WRITE_ERROR|BF_SHUTW|BF_READ_ERROR|BF_SHUTR|BF_READ_NULL)) {
+                               buffer_shutr_now(rep);
+                               buffer_shutw_now(req);
+                               //fd_delete(req->cons->fd);
+                               //req->cons->state = SI_ST_CLO;
                                if (t->srv) {
-                                       t->srv->cur_sess--;
+                                       //t->srv->cur_sess--;
                                        t->srv->failed_resp++;
-                                       sess_change_server(t, NULL);
+                                       //sess_change_server(t, NULL);
                                }
                                t->be->failed_resp++;
-                               t->srv_state = SV_STCLOSE;
                                rep->analysers = 0;
                                txn->status = 502;
                                client_return(t, error_message(t, HTTP_ERR_502));
@@ -2745,8 +2806,8 @@ int process_response(struct session *t)
                                if (!(t->flags & SN_FINST_MASK))
                                        t->flags |= SN_FINST_H;
 
-                               if (t->srv && may_dequeue_tasks(t->srv, t->be))
-                                       process_srv_queue(t->srv);
+                               //if (t->srv && may_dequeue_tasks(t->srv, t->be))
+                               //      process_srv_queue(t->srv);
 
                                return 0;
                        }
@@ -2756,16 +2817,16 @@ int process_response(struct session *t)
                        }
                        /* read timeout : return a 504 to the client. */
                        else if (rep->flags & BF_READ_TIMEOUT) {
-                               buffer_shutr(rep);
-                               buffer_shutw(req);
-                               fd_delete(t->srv_fd);
+                               buffer_shutr_now(rep);
+                               buffer_shutw_now(req);
+                               //fd_delete(req->cons->fd);
+                               //req->cons->state = SI_ST_CLO;
                                if (t->srv) {
-                                       t->srv->cur_sess--;
+                                       //t->srv->cur_sess--;
                                        t->srv->failed_resp++;
-                                       sess_change_server(t, NULL);
+                                       //sess_change_server(t, NULL);
                                }
                                t->be->failed_resp++;
-                               t->srv_state = SV_STCLOSE;
                                rep->analysers = 0;
                                txn->status = 504;
                                client_return(t, error_message(t, HTTP_ERR_504));
@@ -2774,8 +2835,8 @@ int process_response(struct session *t)
                                if (!(t->flags & SN_FINST_MASK))
                                        t->flags |= SN_FINST_H;
 
-                               if (t->srv && may_dequeue_tasks(t->srv, t->be))
-                                       process_srv_queue(t->srv);
+                               //if (t->srv && may_dequeue_tasks(t->srv, t->be))
+                               //      process_srv_queue(t->srv);
                                return 0;
                        }
 
@@ -2856,16 +2917,16 @@ int process_response(struct session *t)
                                if (apply_filters_to_response(t, rep, rule_set->rsp_exp) < 0) {
                                return_bad_resp:
                                        if (t->srv) {
-                                               t->srv->cur_sess--;
+                                               //t->srv->cur_sess--;
                                                t->srv->failed_resp++;
-                                               sess_change_server(t, NULL);
+                                               //sess_change_server(t, NULL);
                                        }
                                        cur_proxy->failed_resp++;
                                return_srv_prx_502:
-                                       buffer_shutr(rep);
-                                       buffer_shutw(req);
-                                       fd_delete(t->srv_fd);
-                                       t->srv_state = SV_STCLOSE;
+                                       buffer_shutr_now(rep);
+                                       buffer_shutw_now(req);
+                                       //fd_delete(req->cons->fd);
+                                       //req->cons->state = SI_ST_CLO;
                                        rep->analysers = 0;
                                        txn->status = 502;
                                        client_return(t, error_message(t, HTTP_ERR_502));
@@ -2876,8 +2937,8 @@ int process_response(struct session *t)
                                        /* We used to have a free connection slot. Since we'll never use it,
                                         * we have to inform the server that it may be used by another session.
                                         */
-                                       if (t->srv && may_dequeue_tasks(t->srv, cur_proxy))
-                                               process_srv_queue(t->srv);
+                                       //if (t->srv && may_dequeue_tasks(t->srv, cur_proxy))
+                                       //      process_srv_queue(t->srv);
                                        return 0;
                                }
                        }
@@ -2885,9 +2946,9 @@ int process_response(struct session *t)
                        /* has the response been denied ? */
                        if (txn->flags & TX_SVDENY) {
                                if (t->srv) {
-                                       t->srv->cur_sess--;
+                                       //t->srv->cur_sess--;
                                        t->srv->failed_secu++;
-                                       sess_change_server(t, NULL);
+                                       //sess_change_server(t, NULL);
                                }
                                cur_proxy->denied_resp++;
                                goto return_srv_prx_502;
@@ -3024,9 +3085,9 @@ int process_response(struct session *t)
                         * the 'checkcache' option, and send an alert.
                         */
                        if (t->srv) {
-                               t->srv->cur_sess--;
+                               //t->srv->cur_sess--;
                                t->srv->failed_secu++;
-                               sess_change_server(t, NULL);
+                               //sess_change_server(t, NULL);
                        }
                        t->be->denied_resp++;
 
@@ -3063,7 +3124,7 @@ int process_response(struct session *t)
 #ifdef CONFIG_HAP_TCPSPLICE
                if ((t->fe->options & t->be->options) & PR_O_TCPSPLICE) {
                        /* TCP splicing supported by both FE and BE */
-                       tcp_splice_splicefd(t->cli_fd, t->srv_fd, 0);
+                       tcp_splice_splicefd(t->cli_fd, req->cons->fd, 0);
                }
 #endif
                /* if the user wants to log as soon as possible, without counting
@@ -3113,9 +3174,10 @@ int process_cli(struct session *t)
        struct buffer *req = t->req;
        struct buffer *rep = t->rep;
 
-       DPRINTF(stderr,"[%u] process_cli: c=%s s=%s set(r,w)=%d,%d exp(r,w)=%u,%u req=%08x rep=%08x rql=%d rpl=%d\n",
-               now_ms,
-               cli_stnames[t->cli_state], srv_stnames[t->srv_state],
+       DPRINTF(stderr,"[%u] %s: fd=%d[%d] c=%s set(r,w)=%d,%d exp(r,w)=%u,%u req=%08x rep=%08x rql=%d rpl=%d\n",
+               now_ms, __FUNCTION__,
+               t->cli_fd, t->cli_fd >= 0 ? fdtab[t->cli_fd].state : 0, /* fd,state*/
+               cli_stnames[t->cli_state],
                t->cli_fd >= 0 && fdtab[t->cli_fd].state != FD_STCLOSE ? EV_FD_ISSET(t->cli_fd, DIR_RD) : 0,
                t->cli_fd >= 0 && fdtab[t->cli_fd].state != FD_STCLOSE ? EV_FD_ISSET(t->cli_fd, DIR_WR) : 0,
                req->rex, rep->wex,
@@ -3128,17 +3190,20 @@ int process_cli(struct session *t)
         */
        if (t->cli_state == CL_STDATA || t->cli_state == CL_STSHUTR) {
                /* we can skip most of the tests at once if some conditions are not met */
-               if (!((req->flags & (BF_READ_TIMEOUT|BF_READ_ERROR))   ||
-                     (rep->flags & (BF_WRITE_TIMEOUT|BF_WRITE_ERROR)) ||
+               if (!((fdtab[t->cli_fd].state == FD_STERROR)   ||
+                     (req->flags & (BF_READ_TIMEOUT|BF_READ_ERROR|BF_SHUTR_NOW))   ||
+                     (rep->flags & (BF_WRITE_TIMEOUT|BF_WRITE_ERROR|BF_SHUTW_NOW)) ||
                      (!(req->flags & BF_SHUTR) && req->flags & (BF_READ_NULL|BF_SHUTW)) ||
                      (!(rep->flags & BF_SHUTW) &&
                       (rep->flags & (BF_EMPTY|BF_MAY_FORWARD|BF_SHUTR)) == (BF_EMPTY|BF_MAY_FORWARD|BF_SHUTR))))
                        goto update_timeouts;
 
                /* read or write error */
-               if (rep->flags & BF_WRITE_ERROR || req->flags & BF_READ_ERROR) {
+               if (fdtab[t->cli_fd].state == FD_STERROR) {
                        buffer_shutr(req);
+                       req->flags |= BF_READ_ERROR;
                        buffer_shutw(rep);
+                       rep->flags |= BF_WRITE_ERROR;
                        fd_delete(t->cli_fd);
                        t->cli_state = CL_STCLOSE;
                        trace_term(t, TT_HTTP_CLI_1);
@@ -3146,9 +3211,9 @@ int process_cli(struct session *t)
                                if (!(t->flags & SN_ERR_MASK))
                                        t->flags |= SN_ERR_CLICL;
                                if (!(t->flags & SN_FINST_MASK)) {
-                                       if (t->pend_pos)
+                                       if (req->cons->err_type <= SI_ET_QUEUE_ABRT)
                                                t->flags |= SN_FINST_Q;
-                                       else if (t->srv_state == SV_STCONN)
+                                       else if (req->cons->err_type <= SI_ET_CONN_OTHER)
                                                t->flags |= SN_FINST_C;
                                        else
                                                t->flags |= SN_FINST_D;
@@ -3158,7 +3223,7 @@ int process_cli(struct session *t)
                }
                /* last read, or end of server write */
                else if (!(req->flags & BF_SHUTR) &&   /* not already done */
-                        req->flags & (BF_READ_NULL | BF_SHUTW)) {
+                        req->flags & (BF_READ_NULL|BF_SHUTR_NOW|BF_SHUTW)) {
                        buffer_shutr(req);
                        if (!(rep->flags & BF_SHUTW)) {
                                EV_FD_CLR(t->cli_fd, DIR_RD);
@@ -3175,8 +3240,9 @@ int process_cli(struct session *t)
                 * allowed to forward the data.
                 */
                else if (!(rep->flags & BF_SHUTW) &&   /* not already done */
-                        rep->flags & BF_EMPTY && rep->flags & BF_MAY_FORWARD &&
-                        rep->flags & BF_SHUTR && !(t->flags & SN_SELF_GEN)) {
+                        ((rep->flags & BF_SHUTW_NOW) ||
+                         (rep->flags & BF_EMPTY && rep->flags & BF_MAY_FORWARD &&
+                          rep->flags & BF_SHUTR && !(t->flags & SN_SELF_GEN)))) {
                        buffer_shutw(rep);
                        if (!(req->flags & BF_SHUTR)) {
                                EV_FD_CLR(t->cli_fd, DIR_WR);
@@ -3209,9 +3275,9 @@ int process_cli(struct session *t)
                                if (!(t->flags & SN_ERR_MASK))
                                        t->flags |= SN_ERR_CLITO;
                                if (!(t->flags & SN_FINST_MASK)) {
-                                       if (t->pend_pos)
+                                       if (req->cons->err_type <= SI_ET_QUEUE_ABRT)
                                                t->flags |= SN_FINST_Q;
-                                       else if (t->srv_state == SV_STCONN)
+                                       else if (req->cons->err_type <= SI_ET_CONN_OTHER)
                                                t->flags |= SN_FINST_C;
                                        else
                                                t->flags |= SN_FINST_D;
@@ -3239,9 +3305,9 @@ int process_cli(struct session *t)
                                if (!(t->flags & SN_ERR_MASK))
                                        t->flags |= SN_ERR_CLITO;
                                if (!(t->flags & SN_FINST_MASK)) {
-                                       if (t->pend_pos)
+                                       if (req->cons->err_type <= SI_ET_QUEUE_ABRT)
                                                t->flags |= SN_FINST_Q;
-                                       else if (t->srv_state == SV_STCONN)
+                                       else if (req->cons->err_type <= SI_ET_CONN_OTHER)
                                                t->flags |= SN_FINST_C;
                                        else
                                                t->flags |= SN_FINST_D;
@@ -3282,7 +3348,7 @@ int process_cli(struct session *t)
                        }
 
                        /* we don't enable client write if the buffer is empty, nor if the server has to analyze it */
-                       if ((rep->flags & BF_EMPTY) || !(rep->flags & BF_MAY_FORWARD)) {
+                       if ((rep->flags & (BF_EMPTY|BF_MAY_FORWARD)) != BF_MAY_FORWARD) {
                                if (EV_FD_COND_C(t->cli_fd, DIR_WR)) {
                                        /* stop writing */
                                        rep->wex = TICK_ETERNITY;
@@ -3306,7 +3372,7 @@ int process_cli(struct session *t)
        else if (t->cli_state == CL_STCLOSE) { /* CL_STCLOSE: nothing to do */
                if ((global.mode & MODE_DEBUG) && (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) {
                        int len;
-                       len = sprintf(trash, "%08x:%s.clicls[%04x:%04x]\n", t->uniq_id, t->be->id, (unsigned short)t->cli_fd, (unsigned short)t->srv_fd);
+                       len = sprintf(trash, "%08x:%s.clicls[%04x:%04x]\n", t->uniq_id, t->be->id, (unsigned short)t->cli_fd, (unsigned short)req->cons->fd);
                        write(1, trash, len);
                }
                return 0;
@@ -3319,529 +3385,1471 @@ int process_cli(struct session *t)
 }
 
 
-/*
- * Manages the server FSM and its socket. It normally returns zero, but may
- * return 1 if it absolutely wants to be called again.
- *
- * Note: process_srv is the ONLY function allowed to set srv_state to anything
- *       but SV_STCLOSE. The only exception is for functions called from this
- *       one (eg: those in backend.c).
+/* Return 1 if the pending connection has failed and should be retried,
+ * otherwise zero. We may only come here in SI_ST_CON state, which means that
+ * the socket's file descriptor is known.
  */
-int process_srv(struct session *t)
+int tcp_connection_status(struct session *t)
 {
-       struct http_txn *txn = &t->txn;
        struct buffer *req = t->req;
        struct buffer *rep = t->rep;
-       int conn_err;
+       int conn_err = 0;
 
-       DPRINTF(stderr,"[%u] process_srv: c=%s s=%s set(r,w)=%d,%d exp(r,w)=%u,%u req=%08x rep=%08x rql=%d rpl=%d\n",
-               now_ms,
-               cli_stnames[t->cli_state], srv_stnames[t->srv_state],
-               t->srv_fd >= 0 && fdtab[t->srv_fd].state != FD_STCLOSE ? EV_FD_ISSET(t->srv_fd, DIR_RD) : 0,
-               t->srv_fd >= 0 && fdtab[t->srv_fd].state != FD_STCLOSE ? EV_FD_ISSET(t->srv_fd, DIR_WR) : 0,
+       DPRINTF(stderr,"[%u] %s: c=%s exp(r,w)=%u,%u req=%08x rep=%08x rql=%d rpl=%d\n",
+               now_ms, __FUNCTION__,
+               cli_stnames[t->cli_state],
                rep->rex, req->wex,
                req->flags, rep->flags,
                req->l, rep->l);
 
- update_state:
-       if (t->srv_state == SV_STIDLE) {
-               if ((rep->flags & BF_SHUTW) ||
-                        ((req->flags & BF_SHUTR) &&
-                         (req->flags & BF_EMPTY || t->be->options & PR_O_ABRT_CLOSE))) { /* give up */
-                       req->wex = TICK_ETERNITY;
-                       if (t->pend_pos)
+       if ((req->flags & BF_SHUTW_NOW) ||
+           (rep->flags & BF_SHUTW) ||
+           ((req->flags & BF_SHUTR) && /* FIXME: this should not prevent a connection from establishing */
+            ((req->flags & BF_EMPTY && !(req->flags & BF_WRITE_STATUS)) ||
+             t->be->options & PR_O_ABRT_CLOSE))) { /* give up */
+
+               trace_term(t, TT_HTTP_SRV_5);
+               req->wex = TICK_ETERNITY;
+               fd_delete(req->cons->fd);
+               if (t->srv) {
+                       t->srv->cur_sess--;
+                       sess_change_server(t, NULL);
+               }
+               /* note that this must not return any error because it would be able to
+                * overwrite the client_retnclose() output.
+                */
+               //srv_close_with_err(t, SN_ERR_CLICL, SN_FINST_C, 0, NULL);
+
+               // FIXME: should we set rep->MAY_FORWARD ?
+               buffer_shutw(req);
+               buffer_shutr(rep);
+               req->cons->state = SI_ST_CLO;
+               if (!req->cons->err_type)
+                       req->cons->err_type = SI_ET_CONN_ABRT;
+               req->cons->err_loc  = t->srv;
+               return 0;
+       }
+
+       /* check for timeouts and asynchronous connect errors */
+       if (fdtab[req->cons->fd].state == FD_STERROR) {
+               conn_err = SI_ET_CONN_ERR;
+               if (!req->cons->err_type)
+                       req->cons->err_type = SI_ET_CONN_ERR;
+       }
+       else if (!(req->flags & BF_WRITE_STATUS)) {
+               /* nothing happened, maybe we timed out */
+               if (tick_is_expired(req->wex, now_ms)) {
+                       conn_err = SI_ET_CONN_TO;
+                       if (!req->cons->err_type)
+                               req->cons->err_type = SI_ET_CONN_TO;
+               }
+               else
+                       return 0; /* let's wait a bit more */
+       }
+
+       if (conn_err) {
+               fd_delete(req->cons->fd);
+               req->cons->state = SI_ST_CLO;
+
+               if (t->srv) {
+                       t->srv->cur_sess--;
+                       sess_change_server(t, NULL);
+                       req->cons->err_loc  = t->srv;
+               }
+
+               /* ensure that we have enough retries left */
+               if (srv_count_retry_down(t, conn_err))
+                       return 0;
+
+               if (conn_err == SI_ET_CONN_ERR) {
+                       /* we encountered an immediate connection error, and we
+                        * will have to retry connecting to the same server, most
+                        * likely leading to the same result. To avoid this, we
+                        * fake a connection timeout to retry after a turn-around
+                        * time of 1 second. We will wait in the previous if block.
+                        */
+                       req->cons->state = SI_ST_TAR;
+                       req->wex = tick_add(now_ms, MS_TO_TICKS(1000));
+                       return 0;
+               }
+
+               if (t->srv && t->conn_retries == 0 && t->be->options & PR_O_REDISP) {
+                       /* We're on our last chance, and the REDISP option was specified.
+                        * We will ignore cookie and force to balance or use the dispatcher.
+                        */
+                       /* let's try to offer this slot to anybody */
+                       if (may_dequeue_tasks(t->srv, t->be))
+                               process_srv_queue(t->srv);
+
+                       /* it's left to the dispatcher to choose a server */
+                       t->flags &= ~(SN_DIRECT | SN_ASSIGNED | SN_ADDR_SET);
+                       t->prev_srv = t->srv;
+               } else {
+                       /* we just want to retry */
+                       if (t->srv)
+                               t->srv->retries++;
+                       t->be->retries++;
+
+                       /* Now we will try to either reconnect to the same server or
+                        * connect to another server. If the connection gets queued
+                        * because all servers are saturated, then we will go back to
+                        * the idle state where the buffer's consumer is marked as
+                        * unknown.
+                        */
+                       if (srv_retryable_connect(t)) {
+                               /* success or unrecoverable error */
                                t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now);
-                       /* note that this must not return any error because it would be able to
-                        * overwrite the client_retnclose() output.
+                               return 0;
+                       }
+               }
+
+               /* We'll rely on the caller to try to get a connection again */
+               return 1;
+       }
+       else {
+               /* no error and write OK : connection succeeded */
+               t->logs.t_connect = tv_ms_elapsed(&t->logs.tv_accept, &now);
+               req->cons->state    = SI_ST_EST;
+               req->cons->err_type = SI_ET_NONE;
+               req->cons->err_loc  = NULL;
+
+               if (req->flags & BF_EMPTY) {
+                       EV_FD_CLR(req->cons->fd, DIR_WR);
+                       req->wex = TICK_ETERNITY;
+               } else {
+                       EV_FD_SET(req->cons->fd, DIR_WR);
+                       req->wex = tick_add_ifset(now_ms, t->be->timeout.server);
+                       if (tick_isset(req->wex)) {
+                               /* FIXME: to prevent the server from expiring read timeouts during writes,
+                                * we refresh it. */
+                               rep->rex = req->wex;
+                       }
+               }
+
+               if (t->be->mode == PR_MODE_TCP) { /* let's allow immediate data connection in this case */
+                       if (!(rep->flags & BF_HIJACK)) {
+                               EV_FD_SET(req->cons->fd, DIR_RD);
+                               rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
+                       }
+                       buffer_set_rlim(rep, BUFSIZE); /* no rewrite needed */
+
+                       /* if the user wants to log as soon as possible, without counting
+                          bytes from the server, then this is the right moment. */
+                       if (t->fe->to_log && !(t->logs.logwait & LW_BYTES)) {
+                               t->logs.t_close = t->logs.t_connect; /* to get a valid end date */
+                               tcp_sess_log(t);
+                       }
+#ifdef CONFIG_HAP_TCPSPLICE
+                       if ((t->fe->options & t->be->options) & PR_O_TCPSPLICE) {
+                               /* TCP splicing supported by both FE and BE */
+                               tcp_splice_splicefd(t->cli_fd, req->cons->fd, 0);
+                       }
+#endif
+               }
+               else {
+                       rep->analysers |= AN_RTR_HTTP_HDR;
+                       buffer_set_rlim(rep, BUFSIZE - MAXREWRITE); /* rewrite needed */
+                       t->txn.rsp.msg_state = HTTP_MSG_RPBEFORE;
+                       /* reset hdr_idx which was already initialized by the request.
+                        * right now, the http parser does it.
+                        * hdr_idx_init(&t->txn.hdr_idx);
                         */
-                       if (txn->flags & TX_CLTARPIT)
-                               srv_close_with_err(t, SN_ERR_CLICL, SN_FINST_T, 0, NULL);
-                       else
-                               srv_close_with_err(t, SN_ERR_CLICL, t->pend_pos ? SN_FINST_Q : SN_FINST_C, 0, NULL);
+               }
+
+               if (!rep->analysers)
+                       t->rep->flags |= BF_MAY_FORWARD;
+               req->wex = TICK_ETERNITY;
+               return 0;
+       }
+}
+
+
+/*
+ * This function tries to assign a server to a stream_sock interface.
+ * It may be called only for t->req->cons->state = one of { SI_ST_INI,
+ * SI_ST_TAR, SI_ST_QUE }. It returns one of those states, SI_ST_ASS
+ * in case of success, or SI_ST_CLO in case of failure. It returns 1 if
+ * it returns SI_ST_ASS, otherwise zero.
+ */
+int stream_sock_assign_server(struct session *t)
+{
+       DPRINTF(stderr,"[%u] %s: c=%s exp(r,w)=%u,%u req=%08x rep=%08x rql=%d rpl=%d\n",
+               now_ms, __FUNCTION__,
+               cli_stnames[t->cli_state],
+               t->rep->rex, t->req->wex,
+               t->req->flags, t->rep->flags,
+               t->req->l, t->rep->l);
+
+       if (t->req->cons->state == SI_ST_TAR) {
+               /* connection might be aborted */
+               if ((t->req->flags & BF_SHUTW_NOW) ||
+                   (t->rep->flags & BF_SHUTW) ||
+                   ((t->req->flags & BF_SHUTR) && /* FIXME: this should not prevent a connection from establishing */
+                    (t->req->flags & BF_EMPTY || t->be->options & PR_O_ABRT_CLOSE))) { /* give up */
 
                        trace_term(t, TT_HTTP_SRV_1);
-                       goto update_state;
+                       t->req->wex = TICK_ETERNITY;
+
+                       // FIXME: should we set rep->MAY_FORWARD ?
+                       buffer_shutr(t->rep);
+                       buffer_shutw(t->req);
+                       if (!t->req->cons->err_type)
+                               t->req->cons->err_type = SI_ET_CONN_ABRT;
+                       t->req->cons->state = SI_ST_CLO;
+                       return 0;
                }
-               else if (req->flags & BF_MAY_FORWARD) {
-                       /* the client allows the server to connect */
-                       if (txn->flags & TX_CLTARPIT) {
-                               /* This connection is being tarpitted. The CLIENT side has
-                                * already set the connect expiration date to the right
-                                * timeout. We just have to check that it has not expired.
-                                */
-                               if (!(req->flags & BF_WRITE_TIMEOUT))
-                                       return 0;
 
-                               /* We will set the queue timer to the time spent, just for
-                                * logging purposes. We fake a 500 server error, so that the
-                                * attacker will not suspect his connection has been tarpitted.
-                                * It will not cause trouble to the logs because we can exclude
-                                * the tarpitted connections by filtering on the 'PT' status flags.
-                                */
-                               req->wex = TICK_ETERNITY;
+               if (!tick_is_expired(t->req->wex, now_ms))
+                       return 0;  /* still in turn-around */
+
+               t->req->cons->state = SI_ST_INI;
+       }
+       else if (t->req->cons->state == SI_ST_QUE) {
+               if (t->pend_pos) {
+                       /* request still in queue... */
+                       if (tick_is_expired(t->req->wex, now_ms)) {
+                               /* ... and timeout expired */
+                               trace_term(t, TT_HTTP_SRV_3);
+                               t->req->wex = TICK_ETERNITY;
                                t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now);
-                               srv_close_with_err(t, SN_ERR_PRXCOND, SN_FINST_T,
-                                                  500, error_message(t, HTTP_ERR_500));
-                               trace_term(t, TT_HTTP_SRV_2);
-                               goto update_state;
+                               if (t->srv)
+                                       t->srv->failed_conns++;
+                               t->be->failed_conns++;
+
+                               // FIXME: should we set rep->MAY_FORWARD ?
+                               buffer_shutr(t->rep);
+                               buffer_shutw(t->req);
+                               t->req->flags |= BF_WRITE_TIMEOUT;
+                               if (!t->req->cons->err_type)
+                                       t->req->cons->err_type = SI_ET_QUEUE_TO;
+                               t->req->cons->state = SI_ST_CLO;
+                               return 0;
                        }
+                       /* connection remains in queue, check if we have to abort it */
+                       if ((t->req->flags & BF_SHUTW_NOW) ||
+                           (t->rep->flags & BF_SHUTW) ||
+                           ((t->req->flags & BF_SHUTR) && /* FIXME: this should not prevent a connection from establishing */
+                            (t->req->flags & BF_EMPTY || t->be->options & PR_O_ABRT_CLOSE))) {
+                               /* give up */
+                               trace_term(t, TT_HTTP_SRV_1);
+                               t->req->wex = TICK_ETERNITY;
+                               t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now);
 
-                       /* Right now, we will need to create a connection to the server.
-                        * We might already have tried, and got a connection pending, in
-                        * which case we will not do anything till it's pending. It's up
-                        * to any other session to release it and wake us up again.
-                        */
-                       if (t->pend_pos) {
-                               if (!(req->flags & BF_WRITE_TIMEOUT)) {
-                                       return 0;
-                               } else {
-                                       /* we've been waiting too long here */
-                                       req->wex = TICK_ETERNITY;
-                                       t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now);
-                                       srv_close_with_err(t, SN_ERR_SRVTO, SN_FINST_Q,
-                                                          503, error_message(t, HTTP_ERR_503));
-                                       trace_term(t, TT_HTTP_SRV_3);
-                                       if (t->srv)
-                                               t->srv->failed_conns++;
-                                       t->be->failed_conns++;
-                                       goto update_state;
-                               }
+                               // FIXME: should we set rep->MAY_FORWARD ?
+                               buffer_shutr(t->rep);
+                               buffer_shutw(t->req);
+                               if (!t->req->cons->err_type)
+                                       t->req->cons->err_type = SI_ET_QUEUE_ABRT;
+                               t->req->cons->state = SI_ST_CLO;
                        }
+                       return 0;
+               }
+               /* The connection is not in the queue anymore */
+               t->req->cons->state = SI_ST_INI;
+       }
 
-                       do {
-                               /* first, get a connection */
-                               if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)
-                                       t->flags |= SN_REDIRECTABLE;
+       /* we may get here from above */
+       if (t->req->cons->state == SI_ST_INI) {
+               /* no connection in progress, we have to get a new one */
 
-                               if (srv_redispatch_connect(t)) {
-                                       if (t->srv_state == SV_STIDLE)
-                                               return 0;
-                                       goto update_state;
-                               }
+               /* first, check if the connection has been aborted */
+               if ((t->req->flags & BF_SHUTW_NOW) ||
+                   (t->rep->flags & BF_SHUTW) ||
+                   ((t->req->flags & BF_SHUTR) &&
+                    (t->req->flags & BF_EMPTY || t->be->options & PR_O_ABRT_CLOSE))) { /* give up */
 
-                               if ((t->flags & SN_REDIRECTABLE) && t->srv && t->srv->rdr_len) {
-                                       /* Server supporting redirection and it is possible.
-                                        * Invalid requests are reported as such. It concerns all
-                                        * the largest ones.
-                                        */
-                                       struct chunk rdr;
-                                       char *path;
-                                       int len;
+                       trace_term(t, TT_HTTP_SRV_1);
+                       t->req->wex = TICK_ETERNITY;
+
+                       // FIXME: should we set rep->MAY_FORWARD ?
+                       buffer_shutr(t->rep);
+                       buffer_shutw(t->req);
+                       if (!t->req->cons->err_type)
+                               t->req->cons->err_type = SI_ET_CONN_ABRT;
+                       t->req->cons->state = SI_ST_CLO;
+                       return 0;
+               }
 
-                                       /* 1: create the response header */
-                                       rdr.len = strlen(HTTP_302);
-                                       rdr.str = trash;
-                                       memcpy(rdr.str, HTTP_302, rdr.len);
+               /* try to get a server assigned */
+               if (srv_redispatch_connect(t) != 0) {
+                       /* we did not get any server, let's check the cause */
+                       if (t->req->cons->state == SI_ST_QUE) {
+                               /* the connection was queued, that's OK */
+                               return 0;
+                       }
 
-                                       /* 2: add the server's prefix */
-                                       if (rdr.len + t->srv->rdr_len > sizeof(trash))
-                                               goto cancel_redir;
+                       trace_term(t, TT_HTTP_SRV_2);
+                       t->req->wex = TICK_ETERNITY;
 
-                                       memcpy(rdr.str + rdr.len, t->srv->rdr_pfx, t->srv->rdr_len);
-                                       rdr.len += t->srv->rdr_len;
+                       // FIXME: should we set rep->MAY_FORWARD ?
+                       buffer_shutr(t->rep);
+                       buffer_shutw(t->req);
+                       t->req->flags |= BF_WRITE_ERROR;
+                       if (!t->req->cons->err_type)
+                               t->req->cons->err_type = SI_ET_CONN_OTHER;
+                       t->req->cons->state = SI_ST_CLO;
+                       return 0;
+               }
 
-                                       /* 3: add the request URI */
-                                       path = http_get_path(txn);
-                                       if (!path)
-                                               goto cancel_redir;
-                                       len = txn->req.sl.rq.u_l + (txn->req.sol+txn->req.sl.rq.u) - path;
-                                       if (rdr.len + len > sizeof(trash) - 4) /* 4 for CRLF-CRLF */
-                                               goto cancel_redir;
+               t->req->cons->state = SI_ST_ASS;
+               /* Once the server is assigned, we have to return because
+                * the caller might be interested in checking several
+                * things before connecting.
+                */
+               return 1;
+       }
+       return 0;
+}
 
-                                       memcpy(rdr.str + rdr.len, path, len);
-                                       rdr.len += len;
-                                       memcpy(rdr.str + rdr.len, "\r\n\r\n", 4);
-                                       rdr.len += 4;
 
-                                       srv_close_with_err(t, SN_ERR_PRXCOND, SN_FINST_C, 302, &rdr);
-                                       trace_term(t, TT_HTTP_SRV_3);
+/*
+ * This function tries to establish a connection to an assigned server. It also
+ * performs connection retries. It may only be called with t->req->cons->state
+ * in { SI_ST_ASS, SI_ST_CON }. It may also set the state to SI_ST_INI,
+ * SI_ST_EST, or SI_ST_CLO.
+ */
+int stream_sock_connect_server(struct session *t)
+{
+       if (t->req->cons->state == SI_ST_ASS) {
+               /* server assigned to request, we have to try to connect now */
 
-                                       /* FIXME: we should increase a counter of redirects per server and per backend. */
-                                       if (t->srv)
-                                               t->srv->cum_sess++;
-                                       goto update_state;
-                               cancel_redir:
-                                       txn->status = 400;
-                                       t->fe->failed_req++;
-                                       srv_close_with_err(t, SN_ERR_PRXCOND, SN_FINST_C,
-                                                          400, error_message(t, HTTP_ERR_400));
-                                       trace_term(t, TT_HTTP_SRV_4);
-                                       goto update_state;
-                               }
+               if (!srv_retryable_connect(t)) {
+                       /* we need to redispatch */
+                       t->req->cons->state = SI_ST_INI;
+                       return 0;
+               }
 
-                               /* try to (re-)connect to the server, and fail if we expire the
-                                * number of retries.
-                                */
-                               if (srv_retryable_connect(t)) {
-                                       t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now);
-                                       if (t->srv_state == SV_STIDLE)
-                                               return 0;
-                                       goto update_state;
-                               }
-                       } while (1);
-               } /* end if may_forward */
-               return 0;
+               t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now);
+               if (t->req->cons->state != SI_ST_CON) {
+                       /* it was an error */
+                       trace_term(t, TT_HTTP_SRV_4);
+                       t->req->wex = TICK_ETERNITY;
+
+                       // FIXME: should we set rep->MAY_FORWARD ?
+                       buffer_shutr(t->rep);
+                       buffer_shutw(t->req);
+                       t->req->flags |= BF_WRITE_ERROR;
+                       if (!t->req->cons->err_type)
+                               t->req->cons->err_type = SI_ET_CONN_OTHER;
+                       t->req->cons->state = SI_ST_CLO;
+                       return 0;
+               }
+               /* We have a socket and switched to SI_ST_CON */
        }
-       else if (t->srv_state == SV_STCONN) { /* connection in progress */
-               if ((rep->flags & BF_SHUTW) ||
-                   ((req->flags & BF_SHUTR) &&
-                    ((req->flags & BF_EMPTY && !(req->flags & BF_WRITE_STATUS)) ||
-                     t->be->options & PR_O_ABRT_CLOSE))) { /* give up */
-                       req->wex = TICK_ETERNITY;
-                       if (!(t->flags & SN_CONN_TAR)) {
-                               /* if we are in turn-around, we have already closed the FD */
-                               fd_delete(t->srv_fd);
-                               if (t->srv) {
-                                       t->srv->cur_sess--;
-                                       sess_change_server(t, NULL);
-                               }
-                       }
 
-                       /* note that this must not return any error because it would be able to
-                        * overwrite the client_retnclose() output.
-                        */
-                       srv_close_with_err(t, SN_ERR_CLICL, SN_FINST_C, 0, NULL);
-                       trace_term(t, TT_HTTP_SRV_5);
-                       goto update_state;
-               }
-               if (!(req->flags & (BF_WRITE_STATUS | BF_WRITE_TIMEOUT))) {
-                       return 0; /* nothing changed */
+       /* we may also get here from above */
+       if (t->req->cons->state == SI_ST_CON) {
+               /* connection in progress or just completed */
+               if (!tcp_connection_status(t))
+                       return 0;
+       }
+       return 0;
+}
+
+
+/*
+ * Tries to establish a connection to the server and associate it to the
+ * request buffer's consumer side. It is assumed that this function will not be
+ * be called with SI_ST_EST nor with BF_MAY_FORWARD cleared. It normally
+ * returns zero, but may return 1 if it absolutely wants to be called again.
+ */
+int process_srv_conn(struct session *t)
+{
+       DPRINTF(stderr,"[%u] %s: c=%s exp(r,w)=%u,%u req=%08x rep=%08x rql=%d rpl=%d\n",
+               now_ms, __FUNCTION__,
+               cli_stnames[t->cli_state],
+               t->rep->rex, t->req->wex,
+               t->req->flags, t->rep->flags,
+               t->req->l, t->rep->l);
+
+       do {
+               if (t->req->cons->state == SI_ST_INI ||
+                   t->req->cons->state == SI_ST_TAR ||
+                   t->req->cons->state == SI_ST_QUE)   {
+                       /* try to assign a server */
+                       if (!stream_sock_assign_server(t))
+                               return 0;
                }
-               else if (!(req->flags & BF_WRITE_STATUS) || (req->flags & BF_WRITE_ERROR)) {
-                       /* timeout, asynchronous connect error or first write error */
-                       if (t->flags & SN_CONN_TAR) {
-                               /* We are doing a turn-around waiting for a new connection attempt. */
-                               if (!(req->flags & BF_WRITE_TIMEOUT))
-                                       return 0;
-                               t->flags &= ~SN_CONN_TAR;
-                       }
-                       else {
-                               fd_delete(t->srv_fd);
-                               if (t->srv) {
-                                       t->srv->cur_sess--;
-                                       sess_change_server(t, NULL);
-                               }
 
-                               if (!(req->flags & BF_WRITE_STATUS))
-                                       conn_err = SN_ERR_SRVTO; // it was a connect timeout.
-                               else
-                                       conn_err = SN_ERR_SRVCL; // it was an asynchronous connect error.
+               if (t->req->cons->state == SI_ST_ASS &&
+                   t->srv && t->srv->rdr_len && t->flags & SN_REDIRECTABLE) {
+                       /* Server supporting redirection and it is possible.
+                        * Invalid requests are reported as such. It concerns all
+                        * the largest ones.
+                        */
+                       struct http_txn *txn = &t->txn;
+                       struct chunk rdr;
+                       char *path;
+                       int len;
+
+                       /* 1: create the response header */
+                       rdr.len = strlen(HTTP_302);
+                       rdr.str = trash;
+                       memcpy(rdr.str, HTTP_302, rdr.len);
 
-                               /* ensure that we have enough retries left */
-                               if (srv_count_retry_down(t, conn_err)) {
-                                       goto update_state;
-                               }
+                       /* 2: add the server's prefix */
+                       if (rdr.len + t->srv->rdr_len > sizeof(trash))
+                               goto cancel_redir;
 
-                               if (req->flags & BF_WRITE_ERROR) {
-                                       /* we encountered an immediate connection error, and we
-                                        * will have to retry connecting to the same server, most
-                                        * likely leading to the same result. To avoid this, we
-                                        * fake a connection timeout to retry after a turn-around
-                                        * time of 1 second. We will wait in the previous if block.
-                                        */
-                                       t->flags |= SN_CONN_TAR;
-                                       req->wex = tick_add(now_ms, MS_TO_TICKS(1000));
-                                       return 0;
-                               }
-                       }
+                       memcpy(rdr.str + rdr.len, t->srv->rdr_pfx, t->srv->rdr_len);
+                       rdr.len += t->srv->rdr_len;
 
-                       if (t->srv && t->conn_retries == 0 && t->be->options & PR_O_REDISP) {
-                               /* We're on our last chance, and the REDISP option was specified.
-                                * We will ignore cookie and force to balance or use the dispatcher.
-                                */
-                               /* let's try to offer this slot to anybody */
-                               if (may_dequeue_tasks(t->srv, t->be))
-                                       process_srv_queue(t->srv);
-
-                               /* it's left to the dispatcher to choose a server */
-                               t->flags &= ~(SN_DIRECT | SN_ASSIGNED | SN_ADDR_SET);
-                               t->prev_srv = t->srv;
-
-                               /* first, get a connection */
-                               if (srv_redispatch_connect(t)) {
-                                       if (t->srv_state == SV_STCONN)
-                                               return 0;
-                                       goto update_state;
-                               }
-                       } else {
-                               if (t->srv)
-                                       t->srv->retries++;
-                               t->be->retries++;
-                       }
+                       /* 3: add the request URI */
+                       path = http_get_path(txn);
+                       if (!path)
+                               goto cancel_redir;
+                       len = txn->req.sl.rq.u_l + (txn->req.sol+txn->req.sl.rq.u) - path;
+                       if (rdr.len + len > sizeof(trash) - 4) /* 4 for CRLF-CRLF */
+                               goto cancel_redir;
 
-                       do {
-                               /* Now we will try to either reconnect to the same server or
-                                * connect to another server. If the connection gets queued
-                                * because all servers are saturated, then we will go back to
-                                * the SV_STIDLE state.
-                                */
-                               if (srv_retryable_connect(t)) {
-                                       t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now);
-                                       if (t->srv_state == SV_STCONN)
-                                               return 0;
-                                       goto update_state;
-                               }
+                       memcpy(rdr.str + rdr.len, path, len);
+                       rdr.len += len;
+                       memcpy(rdr.str + rdr.len, "\r\n\r\n", 4);
+                       rdr.len += 4;
 
-                               /* we need to redispatch the connection to another server */
-                               if (srv_redispatch_connect(t)) {
-                                       if (t->srv_state == SV_STCONN)
-                                               return 0;
-                                       goto update_state;
-                               }
-                       } while (1);
+                       srv_close_with_err(t, SN_ERR_PRXCOND, SN_FINST_C, 302, &rdr);
+                       trace_term(t, TT_HTTP_SRV_3);
+
+                       /* FIXME: we should increase a counter of redirects per server and per backend. */
+                       if (t->srv)
+                               t->srv->cum_sess++;
+
+                       t->req->cons->state = SI_ST_CLO;
+                       return 0;
+               cancel_redir:
+                       //txn->status = 400;
+                       //t->fe->failed_req++;
+                       //srv_close_with_err(t, SN_ERR_PRXCOND, SN_FINST_C,
+                       //                 400, error_message(t, HTTP_ERR_400));
+                       trace_term(t, TT_HTTP_SRV_4);
+
+                       // FIXME: should we set rep->MAY_FORWARD ?
+                       buffer_shutw(t->req);
+                       buffer_shutr(t->rep);
+                       if (!t->req->cons->err_type)
+                               t->req->cons->err_type = SI_ET_CONN_OTHER;
+                       t->req->cons->state = SI_ST_CLO;
+                       return 0;
                }
-               else { /* no error or write 0 */
-                       t->logs.t_connect = tv_ms_elapsed(&t->logs.tv_accept, &now);
 
-                       if (req->flags & BF_EMPTY) {
-                               EV_FD_CLR(t->srv_fd, DIR_WR);
-                               req->wex = TICK_ETERNITY;
-                       } else {
-                               EV_FD_SET(t->srv_fd, DIR_WR);
-                               req->wex = tick_add_ifset(now_ms, t->be->timeout.server);
-                               if (tick_isset(req->wex)) {
-                                       /* FIXME: to prevent the server from expiring read timeouts during writes,
-                                        * we refresh it. */
-                                       rep->rex = req->wex;
-                               }
-                       }
+               if (t->req->cons->state == SI_ST_CON ||
+                   t->req->cons->state == SI_ST_ASS) {
+                       stream_sock_connect_server(t);
+               }
+       } while (t->req->cons->state != SI_ST_CLO &&
+                t->req->cons->state != SI_ST_CON &&
+                t->req->cons->state != SI_ST_EST);
+       return 0;
+}
 
-                       if (t->be->mode == PR_MODE_TCP) { /* let's allow immediate data connection in this case */
-                               EV_FD_SET(t->srv_fd, DIR_RD);
-                               rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
-                               buffer_set_rlim(rep, BUFSIZE); /* no rewrite needed */
 
-                               /* if the user wants to log as soon as possible, without counting
-                                  bytes from the server, then this is the right moment. */
-                               if (t->fe->to_log && !(t->logs.logwait & LW_BYTES)) {
-                                       t->logs.t_close = t->logs.t_connect; /* to get a valid end date */
-                                       tcp_sess_log(t);
-                               }
-#ifdef CONFIG_HAP_TCPSPLICE
-                               if ((t->fe->options & t->be->options) & PR_O_TCPSPLICE) {
-                                       /* TCP splicing supported by both FE and BE */
-                                       tcp_splice_splicefd(t->cli_fd, t->srv_fd, 0);
-                               }
-#endif
-                       }
-                       else {
-                               rep->analysers |= AN_RTR_HTTP_HDR;
-                               buffer_set_rlim(rep, BUFSIZE - MAXREWRITE); /* rewrite needed */
-                               t->txn.rsp.msg_state = HTTP_MSG_RPBEFORE;
-                               /* reset hdr_idx which was already initialized by the request.
-                                * right now, the http parser does it.
-                                * hdr_idx_init(&t->txn.hdr_idx);
-                                */
-                       }
+/*
+ * Manages the server FSM and its socket during the DATA phase. It must not be
+ * called when a file descriptor is not attached to the buffer. It must only be
+ * called during SI_ST_EST. It normally returns zero, but may return 1 if it
+ * absolutely wants to be called again.
+ */
+int process_srv_data(struct session *t)
+{
+       struct buffer *req = t->req;
+       struct buffer *rep = t->rep;
+       int fd = req->cons->fd;
 
-                       t->srv_state = SV_STDATA;
-                       /* FIXME: this should be turned into t->rep->flags |= BF_PROD_READY */
-                       if (!rep->analysers)
-                               t->rep->flags |= BF_MAY_FORWARD;
-                       req->wex = TICK_ETERNITY;
-                       goto update_state;
-               } /* else no error or write 0 */
+       DPRINTF(stderr,"[%u] %s: c=%s exp(r,w)=%u,%u req=%08x rep=%08x rql=%d rpl=%d\n",
+               now_ms, __FUNCTION__,
+               cli_stnames[t->cli_state],
+               rep->rex, req->wex,
+               req->flags, rep->flags,
+               req->l, rep->l);
+
+       if (req->flags & (BF_WRITE_ERROR | BF_WRITE_TIMEOUT) ||
+           rep->flags & (BF_READ_ERROR | BF_READ_TIMEOUT)) {
+               /* nothing more to be done here */
+               fprintf(stderr, "Hey what are you doing there? t=%p fd=%d state=%d\n",
+                       t, t->req->cons->fd, t->req->cons->state);
+               return 0;
        }
-       else if (t->srv_state == SV_STDATA) {
-               /* we can skip most of the tests at once if some conditions are not met */
-               if (!((req->flags & (BF_WRITE_TIMEOUT|BF_WRITE_ERROR)) ||
-                     (!(req->flags & BF_SHUTW) &&
-                      (req->flags & (BF_EMPTY|BF_MAY_FORWARD)) == (BF_EMPTY|BF_MAY_FORWARD)) ||
-                     (rep->flags & (BF_READ_TIMEOUT|BF_READ_ERROR)) ||
-                     (!(rep->flags & BF_SHUTR) && rep->flags & (BF_READ_NULL|BF_SHUTW))))
-                       goto update_timeouts;
 
-               /* read or write error */
-               /* FIXME: what happens when we have to deal with HTTP ??? */
-               if (req->flags & BF_WRITE_ERROR || rep->flags & BF_READ_ERROR) {
-                       buffer_shutr(rep);
-                       buffer_shutw(req);
-                       fd_delete(t->srv_fd);
+       /* we can skip most of the tests at once if some conditions are not met */
+       /* FIXME: place req->BF_SHUTW_NOW here */
+       //if (!((fdtab[fd].state == FD_STERROR) ||
+       //      (!(req->flags & BF_SHUTW) &&
+       //       (req->flags & (BF_EMPTY|BF_MAY_FORWARD)) == (BF_EMPTY|BF_MAY_FORWARD)) ||
+       //      (rep->flags & (BF_READ_TIMEOUT|BF_READ_ERROR)) ||
+       //      (!(rep->flags & BF_SHUTR) && rep->flags & (BF_READ_NULL|BF_SHUTR_NOW|BF_SHUTW))))
+       //      goto update_timeouts;
+
+       /* read or write error */
+       /* FIXME: what happens when we have to deal with HTTP ??? */
+       if (fdtab[fd].state == FD_STERROR) {
+               trace_term(t, TT_HTTP_SRV_6);
+               buffer_shutw(req);
+               req->flags |= BF_WRITE_ERROR;
+               buffer_shutr(rep);
+               rep->flags |= BF_READ_ERROR;
+               fd_delete(fd);
+               req->cons->state = SI_ST_CLO;
+               if (t->srv) {
+                       t->srv->cur_sess--;
+                       //t->srv->failed_resp++;
+                       //FIXME: si on ne traite pas l'erreur ici, le serveur est perdu et on ne la comptabilisera plus ensuite.
+                       //il va donc falloir stocker l'info du dernier serveur en erreur pour que les couches du dessus traitent.
+                       sess_change_server(t, NULL);
+               }
+               //t->be->failed_resp++;
+               //if (!rep->analysers) {
+               //      if (!(t->flags & SN_ERR_MASK))
+               //              t->flags |= SN_ERR_SRVCL;
+               //      if (!(t->flags & SN_FINST_MASK))
+               //              t->flags |= SN_FINST_D;
+               //}
+               if (may_dequeue_tasks(t->srv, t->be))
+                       process_srv_queue(t->srv);
+
+               return 0;
+       }
+
+       /* last read, or end of client write */
+       if (!(rep->flags & BF_SHUTR) &&   /* not already done */
+           rep->flags & (BF_READ_NULL|BF_SHUTR_NOW|BF_SHUTW)) {
+               buffer_shutr(rep);
+               if (!(req->flags & BF_SHUTW)) {
+                       EV_FD_CLR(fd, DIR_RD);
+                       trace_term(t, TT_HTTP_SRV_7);
+               } else {
+                       /* output was already closed */
+                       trace_term(t, TT_HTTP_SRV_8);
+                       fd_delete(fd);
+                       req->cons->state = SI_ST_CLO;
                        if (t->srv) {
                                t->srv->cur_sess--;
-                               t->srv->failed_resp++;
                                sess_change_server(t, NULL);
                        }
-                       t->be->failed_resp++;
-                       t->srv_state = SV_STCLOSE;
-                       trace_term(t, TT_HTTP_SRV_6);
-                       if (!rep->analysers) {
-                               if (!(t->flags & SN_ERR_MASK))
-                                       t->flags |= SN_ERR_SRVCL;
-                               if (!(t->flags & SN_FINST_MASK))
-                                       t->flags |= SN_FINST_D;
-                       }
+
                        if (may_dequeue_tasks(t->srv, t->be))
                                process_srv_queue(t->srv);
-
-                       goto update_state;
+                       return 0;
                }
-               /* last read, or end of client write */
-               else if (!(rep->flags & BF_SHUTR) &&   /* not already done */
-                        rep->flags & (BF_READ_NULL | BF_SHUTW)) {
-                       buffer_shutr(rep);
-                       if (!(req->flags & BF_SHUTW)) {
-                               EV_FD_CLR(t->srv_fd, DIR_RD);
-                               trace_term(t, TT_HTTP_SRV_7);
-                       } else {
-                               /* output was already closed */
-                               fd_delete(t->srv_fd);
-                               if (t->srv) {
-                                       t->srv->cur_sess--;
-                                       sess_change_server(t, NULL);
-                               }
-                               t->srv_state = SV_STCLOSE;
-                               trace_term(t, TT_HTTP_SRV_8);
+       }
+       /* end of client read and no more data to send. We can forward
+        * the close when we're allowed to forward data (anytime right
+        * now). If we're using option forceclose, then we may also
+        * shutdown the outgoing write channel once the response starts
+        * coming from the server.
+        */
 
-                               if (may_dequeue_tasks(t->srv, t->be))
-                                       process_srv_queue(t->srv);
+       // FIXME: option FORCE_CLOSE should move to upper layer.
+       if (!(req->flags & BF_SHUTW) && /* not already done */
+           (req->flags & BF_SHUTW_NOW ||
+            (req->flags & BF_EMPTY && req->flags & BF_MAY_FORWARD &&
+             (req->flags & BF_SHUTR ||
+              (t->be->options & PR_O_FORCE_CLO && rep->flags & BF_READ_STATUS))))) {
+               buffer_shutw(req);
+               if (!(rep->flags & BF_SHUTR)) {
+                       trace_term(t, TT_HTTP_SRV_9);
+                       EV_FD_CLR(fd, DIR_WR);
+                       shutdown(fd, SHUT_WR);
+                       /* We must ensure that the read part is still alive when switching to shutw */
+                       /* FIXME: is this still true ? */
+                       EV_FD_SET(fd, DIR_RD);
+                       rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
+               } else {
+                       trace_term(t, TT_HTTP_SRV_10);
+                       fd_delete(fd);
+                       req->cons->state = SI_ST_CLO;
+                       if (t->srv) {
+                               t->srv->cur_sess--;
+                               sess_change_server(t, NULL);
                        }
-                       goto update_state;
-               }
-               /* end of client read and no more data to send. We can forward
-                * the close when we're allowed to forward data (anytime right
-                * now). If we're using option forceclose, then we may also
-                * shutdown the outgoing write channel once the response starts
-                * coming from the server.
-                */
-               else if (!(req->flags & BF_SHUTW) && /* not already done */
-                        req->flags & BF_EMPTY && req->flags & BF_MAY_FORWARD &&
-                        (req->flags & BF_SHUTR ||
-                         (t->be->options & PR_O_FORCE_CLO && rep->flags & BF_READ_STATUS))) {
-                       buffer_shutw(req);
-                       if (!(rep->flags & BF_SHUTR)) {
-                               EV_FD_CLR(t->srv_fd, DIR_WR);
-                               shutdown(t->srv_fd, SHUT_WR);
-                               trace_term(t, TT_HTTP_SRV_9);
-                               /* We must ensure that the read part is still alive when switching to shutw */
-                               /* FIXME: is this still true ? */
-                               EV_FD_SET(t->srv_fd, DIR_RD);
-                               rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
-                       } else {
-                               fd_delete(t->srv_fd);
-                               if (t->srv) {
-                                       t->srv->cur_sess--;
-                                       sess_change_server(t, NULL);
-                               }
-                               t->srv_state = SV_STCLOSE;
-                               trace_term(t, TT_HTTP_SRV_10);
 
-                               if (may_dequeue_tasks(t->srv, t->be))
-                                       process_srv_queue(t->srv);
-                       }
-                       goto update_state;
+                       if (may_dequeue_tasks(t->srv, t->be))
+                               process_srv_queue(t->srv);
+                       return 0;
                }
-               /* read timeout */
-               else if ((rep->flags & (BF_SHUTR|BF_READ_TIMEOUT)) == BF_READ_TIMEOUT) {
-                       buffer_shutr(rep);
-                       if (!(req->flags & BF_SHUTW)) {
-                               EV_FD_CLR(t->srv_fd, DIR_RD);
-                               trace_term(t, TT_HTTP_SRV_11);
-                       } else {
-                               fd_delete(t->srv_fd);
-                               if (t->srv) {
-                                       t->srv->cur_sess--;
-                                       sess_change_server(t, NULL);
-                               }
-                               t->srv_state = SV_STCLOSE;
-                               trace_term(t, TT_HTTP_SRV_12);
+       }
 
-                               if (may_dequeue_tasks(t->srv, t->be))
-                                       process_srv_queue(t->srv);
-                       }
-                       if (!rep->analysers) {
-                               if (!(t->flags & SN_ERR_MASK))
-                                       t->flags |= SN_ERR_SRVTO;
-                               if (!(t->flags & SN_FINST_MASK))
-                                       t->flags |= SN_FINST_D;
+       /* read timeout */
+       if ((rep->flags & (BF_SHUTR|BF_READ_TIMEOUT)) == 0 &&
+           tick_is_expired(rep->rex, now_ms)) {
+               rep->flags |= BF_READ_TIMEOUT;
+               //if (!rep->analysers) {
+               //      if (!(t->flags & SN_ERR_MASK))
+               //              t->flags |= SN_ERR_SRVTO;
+               //      if (!(t->flags & SN_FINST_MASK))
+               //              t->flags |= SN_FINST_D;
+               //}
+               buffer_shutr(rep);
+               if (!(req->flags & BF_SHUTW)) {
+                       trace_term(t, TT_HTTP_SRV_11);
+                       EV_FD_CLR(fd, DIR_RD);
+               } else {
+                       trace_term(t, TT_HTTP_SRV_12);
+                       fd_delete(fd);
+                       req->cons->state = SI_ST_CLO;
+                       if (t->srv) {
+                               t->srv->cur_sess--;
+                               sess_change_server(t, NULL);
                        }
 
-                       goto update_state;
-               }       
-               /* write timeout */
-               else if ((req->flags & (BF_SHUTW|BF_WRITE_TIMEOUT)) == BF_WRITE_TIMEOUT) {
-                       buffer_shutw(req);
-                       if (!(rep->flags & BF_SHUTR)) {
-                               EV_FD_CLR(t->srv_fd, DIR_WR);
-                               shutdown(t->srv_fd, SHUT_WR);
-                               /* We must ensure that the read part is still alive when switching to shutw */
-                               /* FIXME: is this still needed ? */
-                               EV_FD_SET(t->srv_fd, DIR_RD);
-                               rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
-                               trace_term(t, TT_HTTP_SRV_13);
-                       } else {
-                               fd_delete(t->srv_fd);
-                               if (t->srv) {
-                                       t->srv->cur_sess--;
-                                       sess_change_server(t, NULL);
-                               }
-                               t->srv_state = SV_STCLOSE;
-                               trace_term(t, TT_HTTP_SRV_14);
-
-                               if (may_dequeue_tasks(t->srv, t->be))
-                                       process_srv_queue(t->srv);
-                       }
-                       if (!rep->analysers) {
-                               if (!(t->flags & SN_ERR_MASK))
-                                       t->flags |= SN_ERR_SRVTO;
-                               if (!(t->flags & SN_FINST_MASK))
-                                       t->flags |= SN_FINST_D;
-                       }
-                       goto update_state;
+                       if (may_dequeue_tasks(t->srv, t->be))
+                               process_srv_queue(t->srv);
+                       return 0;
                }
+       }
 
-       update_timeouts:
-               /* manage read timeout */
+       /* write timeout */
+       if ((req->flags & (BF_SHUTW|BF_WRITE_TIMEOUT)) == 0 &&
+           tick_is_expired(req->wex, now_ms)) {
+               req->flags |= BF_WRITE_TIMEOUT;
+               //if (!rep->analysers) {
+               //      if (!(t->flags & SN_ERR_MASK))
+               //              t->flags |= SN_ERR_SRVTO;
+               //      if (!(t->flags & SN_FINST_MASK))
+               //              t->flags |= SN_FINST_D;
+               //}
+               buffer_shutw(req);
                if (!(rep->flags & BF_SHUTR)) {
-                       if (rep->flags & BF_FULL) {
-                               if (EV_FD_COND_C(t->srv_fd, DIR_RD))
-                                       rep->rex = TICK_ETERNITY;
-                       } else {
-                               EV_FD_COND_S(t->srv_fd, DIR_RD);
-                               rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
+                       trace_term(t, TT_HTTP_SRV_13);
+                       EV_FD_CLR(fd, DIR_WR);
+                       shutdown(fd, SHUT_WR);
+                       /* We must ensure that the read part is still alive when switching to shutw */
+                       /* FIXME: is this still needed ? */
+                       EV_FD_SET(fd, DIR_RD);
+                       rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
+               } else {
+                       trace_term(t, TT_HTTP_SRV_14);
+                       fd_delete(fd);
+                       req->cons->state = SI_ST_CLO;
+                       if (t->srv) {
+                               t->srv->cur_sess--;
+                               sess_change_server(t, NULL);
                        }
+
+                       if (may_dequeue_tasks(t->srv, t->be))
+                               process_srv_queue(t->srv);
+                       return 0;
                }
+       }
 
-               /* manage write timeout */
-               if (!(req->flags & BF_SHUTW)) {
-                       if (req->flags & BF_EMPTY || !(req->flags & BF_MAY_FORWARD)) {
-                               /* stop writing */
-                               if (EV_FD_COND_C(t->srv_fd, DIR_WR))
-                                       req->wex = TICK_ETERNITY;
-                       } else {
-                               /* buffer not empty, there are still data to be transferred */
-                               EV_FD_COND_S(t->srv_fd, DIR_WR);
-                               if (!tick_isset(req->wex)) {
-                                       /* restart writing */
-                                       req->wex = tick_add_ifset(now_ms, t->be->timeout.server);
-                                       if (!(rep->flags & BF_SHUTR) && tick_isset(req->wex) && tick_isset(rep->rex)) {
-                                               /* FIXME: to prevent the server from expiring read timeouts during writes,
-                                                * we refresh it, except if it was already infinite.
-                                                */
-                                               rep->rex = req->wex;
-                                       }
-                               }
-                       }
+ update_timeouts:
+       /* manage read timeout */
+       if (!(rep->flags & BF_SHUTR)) {
+               if (rep->flags & (BF_FULL|BF_HIJACK)) {
+                       if (EV_FD_COND_C(fd, DIR_RD))
+                               rep->rex = TICK_ETERNITY;
+               } else {
+                       EV_FD_COND_S(fd, DIR_RD);
+                       rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
                }
-               return 0; /* other cases change nothing */
        }
-       else if (t->srv_state == SV_STCLOSE) { /* SV_STCLOSE : nothing to do */
-               if ((global.mode & MODE_DEBUG) && (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) {
-                       int len;
-                       len = sprintf(trash, "%08x:%s.srvcls[%04x:%04x]\n",
-                                     t->uniq_id, t->be->id, (unsigned short)t->cli_fd, (unsigned short)t->srv_fd);
-                       write(1, trash, len);
+
+       /* manage write timeout */
+       if (!(req->flags & BF_SHUTW)) {
+               if ((req->flags & (BF_EMPTY|BF_MAY_FORWARD)) != BF_MAY_FORWARD) {
+                       /* stop writing */
+                       if (EV_FD_COND_C(fd, DIR_WR))
+                               req->wex = TICK_ETERNITY;
+               } else {
+                       /* buffer not empty, there are still data to be transferred */
+                       EV_FD_COND_S(fd, DIR_WR);
+                       if (!tick_isset(req->wex)) {
+                               /* restart writing */
+                               req->wex = tick_add_ifset(now_ms, t->be->timeout.server);
+                               if (!(rep->flags & BF_SHUTR) && tick_isset(req->wex) && tick_isset(rep->rex)) {
+                                       /* FIXME: to prevent the server from expiring read timeouts during writes,
+                                        * we refresh it, except if it was already infinite.
+                                        */
+                                       rep->rex = req->wex;
+                               }
+                       }
                }
-               return 0;
        }
-#ifdef DEBUG_DEV
-       fprintf(stderr, "FIXME !!!! impossible state at %s:%d = %d\n", __FILE__, __LINE__, t->srv_state);
-       ABORT_NOW();
-#endif
-       return 0;
+       return 0; /* other cases change nothing */
 }
 
 
+///*
+// * Manages the client FSM and its socket. It normally returns zero, but may
+// * return 1 if it absolutely wants to be called again.
+// *
+// * Note: process_cli is the ONLY function allowed to set cli_state to anything
+// *       but CL_STCLOSE.
+// */
+//int process_cli(struct session *t)
+//{
+//     struct buffer *req = t->req;
+//     struct buffer *rep = t->rep;
+//
+//     DPRINTF(stderr,"[%u] %s: c=%s set(r,w)=%d,%d exp(r,w)=%u,%u req=%08x rep=%08x rql=%d rpl=%d\n",
+//             now_ms, __FUNCTION__,
+//             cli_stnames[t->cli_state],
+//             t->cli_fd >= 0 && fdtab[t->cli_fd].state != FD_STCLOSE ? EV_FD_ISSET(t->cli_fd, DIR_RD) : 0,
+//             t->cli_fd >= 0 && fdtab[t->cli_fd].state != FD_STCLOSE ? EV_FD_ISSET(t->cli_fd, DIR_WR) : 0,
+//             req->rex, rep->wex,
+//             req->flags, rep->flags,
+//             req->l, rep->l);
+//
+// update_state:
+//     /* FIXME: we still have to check for CL_STSHUTR because client_retnclose
+//      * still set this state (and will do until unix sockets are converted).
+//      */
+//     if (t->cli_state == CL_STDATA || t->cli_state == CL_STSHUTR) {
+//             /* we can skip most of the tests at once if some conditions are not met */
+//             if (!((req->flags & (BF_READ_TIMEOUT|BF_READ_ERROR))   ||
+//                   (rep->flags & (BF_WRITE_TIMEOUT|BF_WRITE_ERROR)) ||
+//                   (!(req->flags & BF_SHUTR) && req->flags & (BF_READ_NULL|BF_SHUTW)) ||
+//                   (!(rep->flags & BF_SHUTW) &&
+//                    (rep->flags & (BF_EMPTY|BF_MAY_FORWARD|BF_SHUTR)) == (BF_EMPTY|BF_MAY_FORWARD|BF_SHUTR))))
+//                     goto update_timeouts;
+//
+//             /* read or write error */
+//             if (rep->flags & BF_WRITE_ERROR || req->flags & BF_READ_ERROR) {
+//                     buffer_shutr(req);
+//                     buffer_shutw(rep);
+//                     fd_delete(t->cli_fd);
+//                     t->cli_state = CL_STCLOSE;
+//                     trace_term(t, TT_HTTP_CLI_1);
+//                     if (!req->analysers) {
+//                             if (!(t->flags & SN_ERR_MASK))
+//                                     t->flags |= SN_ERR_CLICL;
+//                             if (!(t->flags & SN_FINST_MASK)) {
+//                                     if (t->pend_pos)
+//                                             t->flags |= SN_FINST_Q;
+//                                     else if (!(req->flags & BF_CONNECTED))
+//                                             t->flags |= SN_FINST_C;
+//                                     else
+//                                             t->flags |= SN_FINST_D;
+//                             }
+//                     }
+//                     goto update_state;
+//             }
+//             /* last read, or end of server write */
+//             else if (!(req->flags & BF_SHUTR) &&   /* not already done */
+//                      req->flags & (BF_READ_NULL | BF_SHUTW)) {
+//                     buffer_shutr(req);
+//                     if (!(rep->flags & BF_SHUTW)) {
+//                             EV_FD_CLR(t->cli_fd, DIR_RD);
+//                             trace_term(t, TT_HTTP_CLI_2);
+//                     } else {
+//                             /* output was already closed */
+//                             fd_delete(t->cli_fd);
+//                             t->cli_state = CL_STCLOSE;
+//                             trace_term(t, TT_HTTP_CLI_3);
+//                     }
+//                     goto update_state;
+//             }
+//             /* last server read and buffer empty : we only check them when we're
+//              * allowed to forward the data.
+//              */
+//             else if (!(rep->flags & BF_SHUTW) &&   /* not already done */
+//                      rep->flags & BF_EMPTY && rep->flags & BF_MAY_FORWARD &&
+//                      rep->flags & BF_SHUTR && !(t->flags & SN_SELF_GEN)) {
+//                     buffer_shutw(rep);
+//                     if (!(req->flags & BF_SHUTR)) {
+//                             EV_FD_CLR(t->cli_fd, DIR_WR);
+//                             shutdown(t->cli_fd, SHUT_WR);
+//                             /* We must ensure that the read part is still alive when switching to shutw */
+//                             /* FIXME: is this still true ? */
+//                             EV_FD_SET(t->cli_fd, DIR_RD);
+//                             req->rex = tick_add_ifset(now_ms, t->fe->timeout.client);
+//                             trace_term(t, TT_HTTP_CLI_4);
+//                     } else {
+//                             fd_delete(t->cli_fd);
+//                             t->cli_state = CL_STCLOSE;
+//                             trace_term(t, TT_HTTP_CLI_5);
+//                     }
+//                     goto update_state;
+//             }
+//             /* read timeout */
+//             else if ((req->flags & (BF_SHUTR|BF_READ_TIMEOUT)) == BF_READ_TIMEOUT) {
+//                     buffer_shutr(req);
+//                     if (!(rep->flags & BF_SHUTW)) {
+//                             EV_FD_CLR(t->cli_fd, DIR_RD);
+//                             trace_term(t, TT_HTTP_CLI_6);
+//                     } else {
+//                             /* output was already closed */
+//                             fd_delete(t->cli_fd);
+//                             t->cli_state = CL_STCLOSE;
+//                             trace_term(t, TT_HTTP_CLI_7);
+//                     }
+//                     if (!req->analysers) {
+//                             if (!(t->flags & SN_ERR_MASK))
+//                                     t->flags |= SN_ERR_CLITO;
+//                             if (!(t->flags & SN_FINST_MASK)) {
+//                                     if (t->pend_pos)
+//                                             t->flags |= SN_FINST_Q;
+//                                     else if (!(req->flags & BF_CONNECTED))
+//                                             t->flags |= SN_FINST_C;
+//                                     else
+//                                             t->flags |= SN_FINST_D;
+//                             }
+//                     }
+//                     goto update_state;
+//             }
+//             /* write timeout */
+//             else if ((rep->flags & (BF_SHUTW|BF_WRITE_TIMEOUT)) == BF_WRITE_TIMEOUT) {
+//                     buffer_shutw(rep);
+//                     if (!(req->flags & BF_SHUTR)) {
+//                             EV_FD_CLR(t->cli_fd, DIR_WR);
+//                             shutdown(t->cli_fd, SHUT_WR);
+//                             /* We must ensure that the read part is still alive when switching to shutw */
+//                             /* FIXME: is this still true ? */
+//                             EV_FD_SET(t->cli_fd, DIR_RD);
+//                             req->rex = tick_add_ifset(now_ms, t->fe->timeout.client);
+//                             trace_term(t, TT_HTTP_CLI_8);
+//                     } else {
+//                             fd_delete(t->cli_fd);
+//                             t->cli_state = CL_STCLOSE;
+//                             trace_term(t, TT_HTTP_CLI_9);
+//                     }
+//                     if (!req->analysers) {
+//                             if (!(t->flags & SN_ERR_MASK))
+//                                     t->flags |= SN_ERR_CLITO;
+//                             if (!(t->flags & SN_FINST_MASK)) {
+//                                     if (t->pend_pos)
+//                                             t->flags |= SN_FINST_Q;
+//                                     else if (!(req->flags & BF_CONNECTED))
+//                                             t->flags |= SN_FINST_C;
+//                                     else
+//                                             t->flags |= SN_FINST_D;
+//                             }
+//                     }
+//                     goto update_state;
+//             }
+//
+//     update_timeouts:
+//             /* manage read timeout */
+//             if (!(req->flags & BF_SHUTR)) {
+//                     if (req->flags & BF_FULL) {
+//                             /* no room to read more data */
+//                             if (EV_FD_COND_C(t->cli_fd, DIR_RD)) {
+//                                     /* stop reading until we get some space */
+//                                     req->rex = TICK_ETERNITY;
+//                             }
+//                     } else {
+//                             EV_FD_COND_S(t->cli_fd, DIR_RD);
+//                             req->rex = tick_add_ifset(now_ms, t->fe->timeout.client);
+//                     }
+//             }
+//
+//             /* manage write timeout */
+//             if (!(rep->flags & BF_SHUTW)) {
+//                     /* first, we may have to produce data (eg: stats).
+//                      * right now, this is limited to the SHUTR state.
+//                      */
+//                     if (req->flags & BF_SHUTR && t->flags & SN_SELF_GEN) {
+//                             produce_content(t);
+//                             if (rep->flags & BF_EMPTY) {
+//                                     buffer_shutw(rep);
+//                                     fd_delete(t->cli_fd);
+//                                     t->cli_state = CL_STCLOSE;
+//                                     trace_term(t, TT_HTTP_CLI_10);
+//                                     goto update_state;
+//                             }
+//                     }
+//
+//                     /* we don't enable client write if the buffer is empty, nor if the server has to analyze it */
+//                     if ((rep->flags & BF_EMPTY) || !(rep->flags & BF_MAY_FORWARD)) {
+//                             if (EV_FD_COND_C(t->cli_fd, DIR_WR)) {
+//                                     /* stop writing */
+//                                     rep->wex = TICK_ETERNITY;
+//                             }
+//                     } else {
+//                             /* buffer not empty */
+//                             EV_FD_COND_S(t->cli_fd, DIR_WR);
+//                             if (!tick_isset(rep->wex)) {
+//                                     /* restart writing */
+//                                     rep->wex = tick_add_ifset(now_ms, t->fe->timeout.client);
+//                                     if (!(req->flags & BF_SHUTR) && tick_isset(rep->wex) && tick_isset(req->rex)) {
+//                                             /* FIXME: to prevent the client from expiring read timeouts during writes,
+//                                              * we refresh it, except if it was already infinite. */
+//                                             req->rex = rep->wex;
+//                                     }
+//                             }
+//                     }
+//             }
+//             return 0; /* other cases change nothing */
+//     }
+//     else if (t->cli_state == CL_STCLOSE) { /* CL_STCLOSE: nothing to do */
+//             if ((global.mode & MODE_DEBUG) && (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) {
+//                     int len;
+//                     len = sprintf(trash, "%08x:%s.clicls[%04x:%04x]\n", t->uniq_id, t->be->id, (unsigned short)t->cli_fd, (unsigned short)req->cons->fd);
+//                     write(1, trash, len);
+//             }
+//             return 0;
+//     }
+//#ifdef DEBUG_DEV
+//     fprintf(stderr, "FIXME !!!! impossible state at %s:%d = %d\n", __FILE__, __LINE__, t->cli_state);
+//     ABORT_NOW();
+//#endif
+//     return 0;
+//}
+//
+//
+///* Return 1 if we could get a new connection for session t, otherwise zero */
+//int tcp_get_connection(struct session *t)
+//{
+//     struct http_txn *txn = &t->txn;
+//     struct buffer *req = t->req;
+//     struct buffer *rep = t->rep;
+//
+//     DPRINTF(stderr,"[%u] %s: c=%s exp(r,w)=%u,%u req=%08x rep=%08x rql=%d rpl=%d\n",
+//             now_ms, __FUNCTION__,
+//             cli_stnames[t->cli_state],
+//             rep->rex, req->wex,
+//             req->flags, rep->flags,
+//             req->l, rep->l);
+//
+//
+//     if ((rep->flags & BF_SHUTW) ||
+//         ((req->flags & BF_SHUTR) &&
+//          (req->flags & BF_EMPTY || t->be->options & PR_O_ABRT_CLOSE))) { /* give up */
+//             req->wex = TICK_ETERNITY;
+//             if (t->pend_pos)
+//                     t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now);
+//             /* note that this must not return any error because it would be able to
+//              * overwrite the client_retnclose() output.
+//              */
+//             if (txn->flags & TX_CLTARPIT)
+//                     srv_close_with_err(t, SN_ERR_CLICL, SN_FINST_T, 0, NULL);
+//             else
+//                     srv_close_with_err(t, SN_ERR_CLICL, t->pend_pos ? SN_FINST_Q : SN_FINST_C, 0, NULL);
+//
+//             trace_term(t, TT_HTTP_SRV_1);
+//             return 0;
+//     }
+//
+//     /* stop here if we're not allowed to connect */
+//     if (!(req->flags & BF_MAY_FORWARD))
+//             return 0;
+//
+//     /* the client allows the server to connect */
+//     if (txn->flags & TX_CLTARPIT) {
+//             /* This connection is being tarpitted. The CLIENT side has
+//              * already set the connect expiration date to the right
+//              * timeout. We just have to check that it has not expired.
+//              */
+//             if (!(req->flags & BF_WRITE_TIMEOUT))
+//                     return 0;
+//
+//             /* We will set the queue timer to the time spent, just for
+//              * logging purposes. We fake a 500 server error, so that the
+//              * attacker will not suspect his connection has been tarpitted.
+//              * It will not cause trouble to the logs because we can exclude
+//              * the tarpitted connections by filtering on the 'PT' status flags.
+//              */
+//             req->wex = TICK_ETERNITY;
+//             t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now);
+//             srv_close_with_err(t, SN_ERR_PRXCOND, SN_FINST_T,
+//                                500, error_message(t, HTTP_ERR_500));
+//             trace_term(t, TT_HTTP_SRV_2);
+//             return 0;
+//     }
+//
+//     /* Right now, we will need to create a connection to the server.
+//      * We might already have tried, and got a connection pending, in
+//      * which case we will not do anything till it's pending. It's up
+//      * to any other session to release it and wake us up again.
+//      */
+//     if (t->pend_pos) {
+//             if (!(req->flags & BF_WRITE_TIMEOUT)) {
+//                     return 0;
+//             } else {
+//                     /* we've been waiting too long here */
+//                     req->wex = TICK_ETERNITY;
+//                     t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now);
+//                     srv_close_with_err(t, SN_ERR_SRVTO, SN_FINST_Q,
+//                                        503, error_message(t, HTTP_ERR_503));
+//                     trace_term(t, TT_HTTP_SRV_3);
+//                     if (t->srv)
+//                             t->srv->failed_conns++;
+//                     t->be->failed_conns++;
+//                     return 0;
+//             }
+//     }
+//
+//     do {
+//             if (srv_redispatch_connect(t) != 0)
+//                     return 0;
+//
+//             if (t->srv && t->srv->rdr_len && t->flags & SN_REDIRECTABLE) {
+//                     /* Server supporting redirection and it is possible.
+//                      * Invalid requests are reported as such. It concerns all
+//                      * the largest ones.
+//                      */
+//                     struct chunk rdr;
+//                     char *path;
+//                     int len;
+//
+//                     /* 1: create the response header */
+//                     rdr.len = strlen(HTTP_302);
+//                     rdr.str = trash;
+//                     memcpy(rdr.str, HTTP_302, rdr.len);
+//
+//                     /* 2: add the server's prefix */
+//                     if (rdr.len + t->srv->rdr_len > sizeof(trash))
+//                             goto cancel_redir;
+//
+//                     memcpy(rdr.str + rdr.len, t->srv->rdr_pfx, t->srv->rdr_len);
+//                     rdr.len += t->srv->rdr_len;
+//
+//                     /* 3: add the request URI */
+//                     path = http_get_path(txn);
+//                     if (!path)
+//                             goto cancel_redir;
+//                     len = txn->req.sl.rq.u_l + (txn->req.sol+txn->req.sl.rq.u) - path;
+//                     if (rdr.len + len > sizeof(trash) - 4) /* 4 for CRLF-CRLF */
+//                             goto cancel_redir;
+//
+//                     memcpy(rdr.str + rdr.len, path, len);
+//                     rdr.len += len;
+//                     memcpy(rdr.str + rdr.len, "\r\n\r\n", 4);
+//                     rdr.len += 4;
+//
+//                     srv_close_with_err(t, SN_ERR_PRXCOND, SN_FINST_C, 302, &rdr);
+//                     trace_term(t, TT_HTTP_SRV_3);
+//
+//                     /* FIXME: we should increase a counter of redirects per server and per backend. */
+//                     if (t->srv)
+//                             t->srv->cum_sess++;
+//                     return 0;
+//             cancel_redir:
+//                     txn->status = 400;
+//                     t->fe->failed_req++;
+//                     srv_close_with_err(t, SN_ERR_PRXCOND, SN_FINST_C,
+//                                        400, error_message(t, HTTP_ERR_400));
+//                     trace_term(t, TT_HTTP_SRV_4);
+//                     return 0;
+//             }
+//
+//             /* try to (re-)connect to the server, and fail if we expire the
+//              * number of retries.
+//              */
+//             if (srv_retryable_connect(t)) {
+//                     t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now);
+//                     if (!(req->cons.flags & BC_KNOWN))
+//                             return 0;
+//                     /* We got an FD */
+//                     return 1;
+//             }
+//     } while (1);
+//}
+//
+//
+///* Return 1 if the pending connection has failed and should be retried,
+// * otherwise zero.
+// */
+//int tcp_connection_failed(struct session *t)
+//{
+//     struct buffer *req = t->req;
+//     struct buffer *rep = t->rep;
+//     int conn_err;
+//
+//     DPRINTF(stderr,"[%u] %s: c=%s exp(r,w)=%u,%u req=%08x rep=%08x rql=%d rpl=%d\n",
+//             now_ms, __FUNCTION__,
+//             cli_stnames[t->cli_state],
+//             rep->rex, req->wex,
+//             req->flags, rep->flags,
+//             req->l, rep->l);
+//
+//     if ((rep->flags & BF_SHUTW) ||
+//         ((req->flags & BF_SHUTR) &&
+//          ((req->flags & BF_EMPTY && !(req->flags & BF_WRITE_STATUS)) ||
+//           t->be->options & PR_O_ABRT_CLOSE))) { /* give up */
+//             req->wex = TICK_ETERNITY;
+//             if (!(t->flags & SN_CONN_TAR)) {
+//                     /* if we are in turn-around, we have already closed the FD */
+//                     fd_delete(req->cons->fd);
+//                     req->cons->state = SI_ST_CLO;
+//                     if (t->srv) {
+//                             t->srv->cur_sess--;
+//                             sess_change_server(t, NULL);
+//                     }
+//             }
+//
+//             /* note that this must not return any error because it would be able to
+//              * overwrite the client_retnclose() output.
+//              */
+//             srv_close_with_err(t, SN_ERR_CLICL, SN_FINST_C, 0, NULL);
+//             trace_term(t, TT_HTTP_SRV_5);
+//             return 0;
+//     }
+//
+//     if (!(req->flags & (BF_WRITE_STATUS | BF_WRITE_TIMEOUT)))
+//             return 0; /* nothing changed */
+//
+//     if (!(req->flags & BF_WRITE_STATUS) || (req->flags & BF_WRITE_ERROR)) {
+//             /* timeout, asynchronous connect error or first write error */
+//             if (t->flags & SN_CONN_TAR) {
+//                     /* We are doing a turn-around waiting for a new connection attempt. */
+//                     if (!(req->flags & BF_WRITE_TIMEOUT))
+//                             return 0;
+//                     t->flags &= ~SN_CONN_TAR;
+//             }
+//             else {
+//                     fd_delete(req->cons->fd);
+//                     req->cons->state = SI_ST_CLO;
+//                     if (t->srv) {
+//                             t->srv->cur_sess--;
+//                             sess_change_server(t, NULL);
+//                     }
+//
+//                     if (!(req->flags & BF_WRITE_STATUS))
+//                             conn_err = SN_ERR_SRVTO; // it was a connect timeout.
+//                     else
+//                             conn_err = SN_ERR_SRVCL; // it was an asynchronous connect error.
+//
+//                     /* ensure that we have enough retries left */
+//                     if (srv_count_retry_down(t, conn_err))
+//                             return 0;
+//
+//                     if (req->flags & BF_WRITE_ERROR) {
+//                             /* we encountered an immediate connection error, and we
+//                              * will have to retry connecting to the same server, most
+//                              * likely leading to the same result. To avoid this, we
+//                              * fake a connection timeout to retry after a turn-around
+//                              * time of 1 second. We will wait in the previous if block.
+//                              */
+//                             t->flags |= SN_CONN_TAR;
+//                             req->wex = tick_add(now_ms, MS_TO_TICKS(1000));
+//                             return 0;
+//                     }
+//             }
+//
+//             if (t->srv && t->conn_retries == 0 && t->be->options & PR_O_REDISP) {
+//                     /* We're on our last chance, and the REDISP option was specified.
+//                      * We will ignore cookie and force to balance or use the dispatcher.
+//                      */
+//                     /* let's try to offer this slot to anybody */
+//                     if (may_dequeue_tasks(t->srv, t->be))
+//                             process_srv_queue(t->srv);
+//
+//                     /* it's left to the dispatcher to choose a server */
+//                     t->flags &= ~(SN_DIRECT | SN_ASSIGNED | SN_ADDR_SET);
+//                     t->prev_srv = t->srv;
+//
+//                     /* first, get a connection */
+//                     if (srv_redispatch_connect(t)) {
+//                             if (req->cons.flags & BC_KNOWN)
+//                                     return 0;
+//                             /* we need to get a connection */
+//                             return 1;
+//                     }
+//             } else {
+//                     if (t->srv)
+//                             t->srv->retries++;
+//                     t->be->retries++;
+//             }
+//
+//             do {
+//                     /* Now we will try to either reconnect to the same server or
+//                      * connect to another server. If the connection gets queued
+//                      * because all servers are saturated, then we will go back to
+//                      * the idle state where the buffer's consumer is marked as
+//                      * unknown.
+//                      */
+//                     if (srv_retryable_connect(t)) {
+//                             t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now);
+//                             if (req->cons.flags & BC_KNOWN)
+//                                     return 0;
+//                             /* we did not get a connection */
+//                             return 1;
+//                     }
+//
+//                     /* we need to redispatch the connection to another server */
+//                     if (srv_redispatch_connect(t)) {
+//                             if (req->cons.flags & BC_KNOWN)
+//                                     return 0;
+//                             /* we need to get a connection */
+//                             return 1;
+//                     }
+//             } while (1);
+//     }
+//     else { /* no error and write OK */
+//             t->logs.t_connect = tv_ms_elapsed(&t->logs.tv_accept, &now);
+//
+//             if (req->flags & BF_EMPTY) {
+//                     EV_FD_CLR(req->cons->fd, DIR_WR);
+//                     req->wex = TICK_ETERNITY;
+//             } else {
+//                     EV_FD_SET(req->cons->fd, DIR_WR);
+//                     req->wex = tick_add_ifset(now_ms, t->be->timeout.server);
+//                     if (tick_isset(req->wex)) {
+//                             /* FIXME: to prevent the server from expiring read timeouts during writes,
+//                              * we refresh it. */
+//                             rep->rex = req->wex;
+//                     }
+//             }
+//
+//             if (t->be->mode == PR_MODE_TCP) { /* let's allow immediate data connection in this case */
+//                     EV_FD_SET(req->cons->fd, DIR_RD);
+//                     rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
+//                     buffer_set_rlim(rep, BUFSIZE); /* no rewrite needed */
+//
+//                     /* if the user wants to log as soon as possible, without counting
+//                        bytes from the server, then this is the right moment. */
+//                     if (t->fe->to_log && !(t->logs.logwait & LW_BYTES)) {
+//                             t->logs.t_close = t->logs.t_connect; /* to get a valid end date */
+//                             tcp_sess_log(t);
+//                     }
+//#ifdef CONFIG_HAP_TCPSPLICE
+//                     if ((t->fe->options & t->be->options) & PR_O_TCPSPLICE) {
+//                             /* TCP splicing supported by both FE and BE */
+//                             tcp_splice_splicefd(t->cli_fd, req->cons->fd, 0);
+//                     }
+//#endif
+//             }
+//             else {
+//                     rep->analysers |= AN_RTR_HTTP_HDR;
+//                     buffer_set_rlim(rep, BUFSIZE - MAXREWRITE); /* rewrite needed */
+//                     t->txn.rsp.msg_state = HTTP_MSG_RPBEFORE;
+//                     /* reset hdr_idx which was already initialized by the request.
+//                      * right now, the http parser does it.
+//                      * hdr_idx_init(&t->txn.hdr_idx);
+//                      */
+//             }
+//
+//             req->flags |= BF_CONNECTED;
+//             if (!rep->analysers)
+//                     t->rep->flags |= BF_MAY_FORWARD;
+//             req->wex = TICK_ETERNITY;
+//             return 0;
+//     }
+//}
+//
+//
+///*
+// * Tries to establish a connection to the server and associate it to the
+// * request buffer's consumer side. It normally returns zero, but may return 1
+// * if it absolutely wants to be called again.
+// */
+//int process_srv_conn(struct session *t)
+//{
+//     DPRINTF(stderr,"[%u] %s: c=%s exp(r,w)=%u,%u req=%08x rep=%08x rql=%d rpl=%d\n",
+//             now_ms, __FUNCTION__,
+//             cli_stnames[t->cli_state],
+//             t->rep->rex, t->req->wex,
+//             t->req->flags, t->rep->flags,
+//             t->req->l, t->rep->l);
+//
+//     while (!(t->req->flags & BF_CONNECTED)) {
+//             if (!(t->req->cons.flags & BC_KNOWN)) {
+//                     /* no connection in progress, get a new one */
+//                     if (!tcp_get_connection(t))
+//                             break;
+//             } else {
+//                     /* connection in progress or just completed */
+//                     if (!tcp_connection_failed(t))
+//                             break;
+//             }
+//     }
+//     return 0;
+//}
+//
+//
+///*
+// * Manages the server FSM and its socket during the DATA phase. It must not
+// * be called when a file descriptor is not attached to the buffer. It normally
+// * returns zero, but may return 1 if it absolutely wants to be called again.
+// */
+//int process_srv_data(struct session *t)
+//{
+//     struct buffer *req = t->req;
+//     struct buffer *rep = t->rep;
+//
+//     DPRINTF(stderr,"[%u] %s: c=%s exp(r,w)=%u,%u req=%08x rep=%08x rql=%d rpl=%d\n",
+//             now_ms, __FUNCTION__,
+//             cli_stnames[t->cli_state],
+//             rep->rex, req->wex,
+//             req->flags, rep->flags,
+//             req->l, rep->l);
+//
+//     /* we can skip most of the tests at once if some conditions are not met */
+//     if (!((req->flags & (BF_WRITE_TIMEOUT|BF_WRITE_ERROR)) ||
+//           (!(req->flags & BF_SHUTW) &&
+//            (req->flags & (BF_EMPTY|BF_MAY_FORWARD)) == (BF_EMPTY|BF_MAY_FORWARD)) ||
+//           (rep->flags & (BF_READ_TIMEOUT|BF_READ_ERROR)) ||
+//           (!(rep->flags & BF_SHUTR) && rep->flags & (BF_READ_NULL|BF_SHUTW))))
+//             goto update_timeouts;
+//
+//     /* read or write error */
+//     /* FIXME: what happens when we have to deal with HTTP ??? */
+//     if (req->flags & BF_WRITE_ERROR || rep->flags & BF_READ_ERROR) {
+//             buffer_shutr(rep);
+//             buffer_shutw(req);
+//             fd_delete(req->cons->fd);
+//             req->cons->state = SI_ST_CLO;
+//             if (t->srv) {
+//                     t->srv->cur_sess--;
+//                     t->srv->failed_resp++;
+//                     sess_change_server(t, NULL);
+//             }
+//             t->be->failed_resp++;
+//             trace_term(t, TT_HTTP_SRV_6);
+//             if (!rep->analysers) {
+//                     if (!(t->flags & SN_ERR_MASK))
+//                             t->flags |= SN_ERR_SRVCL;
+//                     if (!(t->flags & SN_FINST_MASK))
+//                             t->flags |= SN_FINST_D;
+//             }
+//             if (may_dequeue_tasks(t->srv, t->be))
+//                     process_srv_queue(t->srv);
+//
+//             return 0;
+//     }
+//
+//     /* last read, or end of client write */
+//     if (!(rep->flags & BF_SHUTR) &&   /* not already done */
+//              rep->flags & (BF_READ_NULL | BF_SHUTW)) {
+//             buffer_shutr(rep);
+//             if (!(req->flags & BF_SHUTW)) {
+//                     EV_FD_CLR(req->cons->fd, DIR_RD);
+//                     trace_term(t, TT_HTTP_SRV_7);
+//             } else {
+//                     /* output was already closed */
+//                     fd_delete(req->cons->fd);
+//                     req->cons->state = SI_ST_CLO;
+//                     if (t->srv) {
+//                             t->srv->cur_sess--;
+//                             sess_change_server(t, NULL);
+//                     }
+//                     trace_term(t, TT_HTTP_SRV_8);
+//
+//                     if (may_dequeue_tasks(t->srv, t->be))
+//                             process_srv_queue(t->srv);
+//                     return 0;
+//             }
+//     }
+//     /* end of client read and no more data to send. We can forward
+//      * the close when we're allowed to forward data (anytime right
+//      * now). If we're using option forceclose, then we may also
+//      * shutdown the outgoing write channel once the response starts
+//      * coming from the server.
+//      */
+//     if (!(req->flags & BF_SHUTW) && /* not already done */
+//         req->flags & BF_EMPTY && req->flags & BF_MAY_FORWARD &&
+//         (req->flags & BF_SHUTR ||
+//          (t->be->options & PR_O_FORCE_CLO && rep->flags & BF_READ_STATUS))) {
+//             buffer_shutw(req);
+//             if (!(rep->flags & BF_SHUTR)) {
+//                     EV_FD_CLR(req->cons->fd, DIR_WR);
+//                     shutdown(req->cons->fd, SHUT_WR);
+//                     trace_term(t, TT_HTTP_SRV_9);
+//                     /* We must ensure that the read part is still alive when switching to shutw */
+//                     /* FIXME: is this still true ? */
+//                     EV_FD_SET(req->cons->fd, DIR_RD);
+//                     rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
+//             } else {
+//                     fd_delete(req->cons->fd);
+//                     req->cons->state = SI_ST_CLO;
+//                     if (t->srv) {
+//                             t->srv->cur_sess--;
+//                             sess_change_server(t, NULL);
+//                     }
+//                     trace_term(t, TT_HTTP_SRV_10);
+//
+//                     if (may_dequeue_tasks(t->srv, t->be))
+//                             process_srv_queue(t->srv);
+//                     return 0;
+//             }
+//     }
+//
+//     /* read timeout */
+//     if ((rep->flags & (BF_SHUTR|BF_READ_TIMEOUT)) == BF_READ_TIMEOUT) {
+//             if (!rep->analysers) {
+//                     if (!(t->flags & SN_ERR_MASK))
+//                             t->flags |= SN_ERR_SRVTO;
+//                     if (!(t->flags & SN_FINST_MASK))
+//                             t->flags |= SN_FINST_D;
+//             }
+//             buffer_shutr(rep);
+//             if (!(req->flags & BF_SHUTW)) {
+//                     EV_FD_CLR(req->cons->fd, DIR_RD);
+//                     trace_term(t, TT_HTTP_SRV_11);
+//             } else {
+//                     fd_delete(req->cons->fd);
+//                     req->cons->state = SI_ST_CLO;
+//                     if (t->srv) {
+//                             t->srv->cur_sess--;
+//                             sess_change_server(t, NULL);
+//                     }
+//                     trace_term(t, TT_HTTP_SRV_12);
+//
+//                     if (may_dequeue_tasks(t->srv, t->be))
+//                             process_srv_queue(t->srv);
+//                     return 0;
+//             }
+//     }
+//
+//     /* write timeout */
+//     if ((req->flags & (BF_SHUTW|BF_WRITE_TIMEOUT)) == BF_WRITE_TIMEOUT) {
+//             if (!rep->analysers) {
+//                     if (!(t->flags & SN_ERR_MASK))
+//                             t->flags |= SN_ERR_SRVTO;
+//                     if (!(t->flags & SN_FINST_MASK))
+//                             t->flags |= SN_FINST_D;
+//             }
+//             buffer_shutw(req);
+//             if (!(rep->flags & BF_SHUTR)) {
+//                     EV_FD_CLR(req->cons->fd, DIR_WR);
+//                     shutdown(req->cons->fd, SHUT_WR);
+//                     /* We must ensure that the read part is still alive when switching to shutw */
+//                     /* FIXME: is this still needed ? */
+//                     EV_FD_SET(req->cons->fd, DIR_RD);
+//                     rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
+//                     trace_term(t, TT_HTTP_SRV_13);
+//             } else {
+//                     fd_delete(req->cons->fd);
+//                     req->cons->state = SI_ST_CLO;
+//                     if (t->srv) {
+//                             t->srv->cur_sess--;
+//                             sess_change_server(t, NULL);
+//                     }
+//                     trace_term(t, TT_HTTP_SRV_14);
+//
+//                     if (may_dequeue_tasks(t->srv, t->be))
+//                             process_srv_queue(t->srv);
+//                     return 0;
+//             }
+//     }
+//
+// update_timeouts:
+//     /* manage read timeout */
+//     if (!(rep->flags & BF_SHUTR)) {
+//             if (rep->flags & BF_FULL) {
+//                     if (EV_FD_COND_C(req->cons->fd, DIR_RD))
+//                             rep->rex = TICK_ETERNITY;
+//             } else {
+//                     EV_FD_COND_S(req->cons->fd, DIR_RD);
+//                     rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
+//             }
+//     }
+//
+//     /* manage write timeout */
+//     if (!(req->flags & BF_SHUTW)) {
+//             if (req->flags & BF_EMPTY || !(req->flags & BF_MAY_FORWARD)) {
+//                     /* stop writing */
+//                     if (EV_FD_COND_C(req->cons->fd, DIR_WR))
+//                             req->wex = TICK_ETERNITY;
+//             } else {
+//                     /* buffer not empty, there are still data to be transferred */
+//                     EV_FD_COND_S(req->cons->fd, DIR_WR);
+//                     if (!tick_isset(req->wex)) {
+//                             /* restart writing */
+//                             req->wex = tick_add_ifset(now_ms, t->be->timeout.server);
+//                             if (!(rep->flags & BF_SHUTR) && tick_isset(req->wex) && tick_isset(rep->rex)) {
+//                                     /* FIXME: to prevent the server from expiring read timeouts during writes,
+//                                      * we refresh it, except if it was already infinite.
+//                                      */
+//                                     rep->rex = req->wex;
+//                             }
+//                     }
+//             }
+//     }
+//     return 0; /* other cases change nothing */
+//}
+//
+
 /*
  * Produces data for the session <s> depending on its source. Expects to be
  * called with client socket shut down on input. Right now, only statistics can
@@ -5267,7 +6275,7 @@ void debug_hdr(const char *dir, struct session *t, const char *start, const char
 {
        int len, max;
        len = sprintf(trash, "%08x:%s.%s[%04x:%04x]: ", t->uniq_id, t->be->id,
-                     dir, (unsigned  short)t->cli_fd, (unsigned short)t->srv_fd);
+                     dir, (unsigned  short)t->cli_fd, (unsigned short)t->req->cons->fd);
        max = end - start;
        UBOUND(max, sizeof(trash) - len - 1);
        len += strlcpy2(trash + len, start, max + 1);
index a56ab5d275391280e858a6d3d31b779f1ae9d836..050c38a533fc38ee241e4d739fd0cbecf3f6f08b 100644 (file)
@@ -454,7 +454,6 @@ int uxst_event_accept(int fd) {
                s->req = s->rep = NULL; /* will be allocated later */
 
                s->cli_fd = cfd;
-               s->srv_fd = -1;
                s->srv = NULL;
                s->pend_pos = NULL;
 
@@ -791,7 +790,7 @@ static int process_uxst_cli(struct session *t)
                if ((global.mode & MODE_DEBUG) && (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) {
                        int len;
                        len = sprintf(trash, "%08x:%s.clicls[%04x:%04x]\n", t->uniq_id, t->be?t->be->id:"",
-                                     (unsigned short)t->cli_fd, (unsigned short)t->srv_fd);
+                                     (unsigned short)t->cli_fd, (unsigned short)t->req->cons->fd);
                        write(1, trash, len);
                }
                return 0;
index 6bc5915b49c03ea59cf7b43db65e671dc097f0b4..dea2c9674dde66555213dd4c2eb8dff3ea0dd17a 100644 (file)
  */
 void client_retnclose(struct session *s, const struct chunk *msg)
 {
-       EV_FD_CLR(s->cli_fd, DIR_RD);
-       EV_FD_SET(s->cli_fd, DIR_WR);
-       buffer_shutr(s->req);
-       buffer_flush(s->req);
-       s->rep->wex = tick_add_ifset(now_ms, s->rep->wto);
-       s->rep->flags |= BF_MAY_FORWARD;
+       //FIXME: must move to lower level
+       //EV_FD_CLR(s->cli_fd, DIR_RD);
+       //EV_FD_SET(s->cli_fd, DIR_WR);
+       buffer_abort(s->req);
+
        s->cli_state = CL_STSHUTR; // FIXME: still used by unix sockets
        buffer_flush(s->rep);
+       buffer_shutr_now(s->rep);
        if (msg && msg->len)
                buffer_write(s->rep, msg->str, msg->len);
+
+       s->rep->wex = tick_add_ifset(now_ms, s->rep->wto);
+       s->rep->flags |= BF_MAY_FORWARD;
 }
 
 
index b35b9fe76723bc5428d26cad67de0e65fd91633a..cc33b815d884ae7fda7c0f0f74f340015ec51cbb 100644 (file)
@@ -223,9 +223,12 @@ int stream_sock_read(int fd) {
        if (tick_isset(b->rex) && b->flags & BF_PARTIAL_READ)
                b->rex = tick_add_ifset(now_ms, b->rto);
 
+       if (!(b->flags & BF_READ_STATUS))
+               goto out_skip_wakeup;
  out_wakeup:
-       if (b->flags & BF_READ_STATUS)
-               task_wakeup(fdtab[fd].owner);
+       task_wakeup(fdtab[fd].owner);
+
+ out_skip_wakeup:
        fdtab[fd].ev &= ~FD_POLL_IN;
        return retval;
 
@@ -241,7 +244,6 @@ int stream_sock_read(int fd) {
         */
        fdtab[fd].state = FD_STERROR;
        fdtab[fd].ev &= ~FD_POLL_STICKY;
-       b->flags |= BF_READ_ERROR;
        b->rex = TICK_ETERNITY;
        goto out_wakeup;
 }
@@ -296,7 +298,7 @@ int stream_sock_write(int fd) {
 
                                if (errno == EALREADY || errno == EINPROGRESS) {
                                        retval = 0;
-                                       goto out_wakeup;
+                                       goto out_may_wakeup;
                                }
 
                                if (errno && errno != EISCONN)
@@ -392,9 +394,13 @@ int stream_sock_write(int fd) {
                }
        }
 
+ out_may_wakeup:
+       if (!(b->flags & BF_WRITE_STATUS))
+               goto out_skip_wakeup;
  out_wakeup:
-       if (b->flags & BF_WRITE_STATUS)
-               task_wakeup(fdtab[fd].owner);
+       task_wakeup(fdtab[fd].owner);
+
+ out_skip_wakeup:
        fdtab[fd].ev &= ~FD_POLL_OUT;
        return retval;
 
@@ -404,7 +410,6 @@ int stream_sock_write(int fd) {
         */
        fdtab[fd].state = FD_STERROR;
        fdtab[fd].ev &= ~FD_POLL_STICKY;
-       b->flags |= BF_WRITE_ERROR;
        b->wex = TICK_ETERNITY;
        goto out_wakeup;
 }