[MAJOR] make the client side use stream_sock_process_data()
The client side now relies on stream_sock_process_data(). One
part has not yet been re-implemented, it concerns the calls
to produce_content().
process_session() has been adjusted to correctly check for
changing bits in order not to call useless functions too many
times.
It already appears that stream_sock_process_data() should be
split so that the timeout computations are only performed at
the exit of process_session().
diff --git a/include/types/buffers.h b/include/types/buffers.h
index 6374271..aee48ee 100644
--- a/include/types/buffers.h
+++ b/include/types/buffers.h
@@ -66,6 +66,12 @@
#define BF_SHUTW_NOW 262144 /* the consumer must shut down for writes ASAP */
#define BF_HIJACK 524288 /* the producer is temporarily replaced */
+/* masks which define input bits for stream interfaces and stream analysers */
+#define BF_MASK_INTERFACE_I (BF_FULL|BF_HIJACK|BF_READ_NULL|BF_SHUTR|BF_SHUTR_NOW|BF_SHUTW)
+#define BF_MASK_INTERFACE_O (BF_EMPTY|BF_HIJACK|BF_MAY_FORWARD|BF_SHUTR|BF_SHUTW|BF_SHUTW_NOW)
+#define BF_MASK_INTERFACE (BF_MASK_INTF_I | BF_MASK_INTF_O)
+
+#define BF_MASK_ANALYSER (BF_FULL|BF_READ_ERROR|BF_READ_TIMEOUT|BF_WRITE_ERROR|BF_SHUTW|BF_SHUTR|BF_READ_NULL)
/* Analysers (buffer->analysers).
* Those bits indicate that there are some processing to do on the buffer
diff --git a/src/proto_http.c b/src/proto_http.c
index 4581813..b0ec9e5 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -650,98 +650,45 @@
* to be woken up, or TICK_ETERNITY. In order not to call all functions for
* nothing too many times, the request and response buffers flags are monitored
* and each function is called only if at least another function has changed at
- * least one flag. If one of the functions called returns non-zero, then it
- * will be called once again after all other functions. This permits explicit
- * external loops which may be useful for complex state machines.
+ * least one flag it is interested in.
*/
-#define PROCESS_CLI 0x1
-#define PROCESS_SRV 0x2
-#define PROCESS_REQ 0x4
-#define PROCESS_RTR 0x8
-#define PROCESS_ALL (PROCESS_CLI|PROCESS_SRV|PROCESS_REQ|PROCESS_RTR)
-
void process_session(struct task *t, int *next)
{
struct session *s = t->context;
- unsigned resync = PROCESS_ALL;
- unsigned int rqf;
- unsigned int rpf;
+ int resync;
+ unsigned int rqf_cli, rpf_cli;
+ unsigned int rqf_srv, rpf_srv;
+ unsigned int rqf_req, rpf_rep;
- /* check timeout expiration only once and adjust buffer flags
- * accordingly.
- */
- if (unlikely(tick_is_expired(t->expire, now_ms))) {
- 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->rep->wex, now_ms))
- s->rep->flags |= BF_WRITE_TIMEOUT;
- }
+ /* force one first pass everywhere */
+ rqf_cli = rqf_srv = rqf_req = ~s->req->flags;
+ rpf_cli = rpf_srv = rpf_rep = ~s->rep->flags;
- //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;
- rqf = s->req->flags;
- rpf = s->rep->flags;
-
- /* the analysers must block it themselves */
- s->req->flags |= BF_MAY_FORWARD;
-
- if (s->req->analysers) {
- if (process_request(s))
- resync |= PROCESS_REQ;
+ resync = 0;
- if (rqf != s->req->flags || rpf != s->rep->flags)
- resync |= PROCESS_ALL & ~PROCESS_REQ;
- }
- }
-
- if (resync & PROCESS_RTR) {
- resync &= ~PROCESS_RTR;
- rqf = s->req->flags;
- rpf = s->rep->flags;
-
- /* the analysers must block it themselves */
- s->rep->flags |= BF_MAY_FORWARD;
-
- if (s->rep->analysers) {
- if (process_response(s))
- resync |= PROCESS_RTR;
-
- if (rqf != s->req->flags || rpf != s->rep->flags)
- resync |= PROCESS_ALL & ~PROCESS_RTR;
+ if (((rqf_cli ^ s->req->flags) & BF_MASK_INTERFACE_I) ||
+ ((rpf_cli ^ s->rep->flags) & BF_MASK_INTERFACE_O)) {
+ resync = 1;
+ if (s->rep->cons->state != SI_ST_CLO) {
+ stream_sock_process_data(s->rep->cons->fd);
+ if (unlikely((s->rep->cons->state == SI_ST_CLO) &&
+ (global.mode & MODE_DEBUG) &&
+ (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)))) {
+ int len;
+ len = sprintf(trash, "%08x:%s.clicls[%04x:%04x]\n",
+ s->uniq_id, s->be->id, (unsigned short)s->rep->cons->fd, (unsigned short)s->req->cons->fd);
+ write(1, trash, len);
+ }
}
- }
-
- if (resync & PROCESS_CLI) {
- rqf = s->req->flags;
- rpf = s->rep->flags;
-
- resync &= ~PROCESS_CLI;
- if (process_cli(s))
- resync |= PROCESS_CLI;
-
- if (rqf != s->req->flags || rpf != s->rep->flags)
- resync |= PROCESS_ALL & ~PROCESS_CLI;
+ rqf_cli = s->req->flags;
+ rpf_cli = s->rep->flags;
}
- if (resync & PROCESS_SRV) {
- rqf = s->req->flags;
- rpf = s->rep->flags;
- resync &= ~PROCESS_SRV;
+ if (((rpf_srv ^ s->rep->flags) & BF_MASK_INTERFACE_I) ||
+ ((rqf_srv ^ s->req->flags) & BF_MASK_INTERFACE_O)) {
+ resync = 1;
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);
@@ -757,8 +704,7 @@
buffer_shutw_now(s->req);
}
- if (stream_sock_process_data(s->req->cons->fd))
- resync |= PROCESS_SRV;
+ stream_sock_process_data(s->req->cons->fd);
/* Count server-side errors (but not timeouts). */
if (s->req->flags & BF_WRITE_ERROR) {
@@ -789,12 +735,34 @@
write(1, trash, len);
}
}
+ rqf_srv = s->req->flags;
+ rpf_srv = s->rep->flags;
+ }
+
+ if ((rqf_req ^ s->req->flags) & BF_MASK_ANALYSER) {
+ resync = 1;
+ /* the analysers must block it themselves */
+ s->req->flags |= BF_MAY_FORWARD;
+ if (s->req->analysers) {
+ process_request(s);
+ }
+ rqf_req = s->req->flags;
+ }
+
+ if ((rpf_rep ^ s->rep->flags) & BF_MASK_ANALYSER) {
+ resync = 1;
+ /* the analysers must block it themselves */
+ s->rep->flags |= BF_MAY_FORWARD;
+
+ if (s->rep->analysers) {
+ process_response(s);
+ }
- if (rqf != s->req->flags || rpf != s->rep->flags)
- resync |= PROCESS_ALL & ~PROCESS_SRV;
+ rpf_rep = s->rep->flags;
}
+
} while (resync);
- if (likely(s->cli_state != CL_STCLOSE ||
+ if (likely((s->rep->cons->state != SI_ST_CLO) ||
(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))
@@ -1724,7 +1692,7 @@
*/
if (req->flags & BF_READ_ERROR) {
req->analysers = 0;
- t->fe->failed_req++;
+ //t->fe->failed_req++;
if (!(t->flags & SN_ERR_MASK))
t->flags |= SN_ERR_CLICL;
if (!(t->flags & SN_FINST_MASK))
@@ -1879,14 +1847,12 @@
goto return_bad_req;
}
- /* 2: have we encountered a close ? */
- else if (req->flags & (BF_READ_NULL | BF_SHUTR)) {
- txn->status = 400;
- client_retnclose(t, error_message(t, HTTP_ERR_400));
+ /* 2: 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;
- t->fe->failed_req++;
-
+ //t->fe->failed_req++;
if (!(t->flags & SN_ERR_MASK))
t->flags |= SN_ERR_CLICL;
if (!(t->flags & SN_FINST_MASK))
@@ -1909,12 +1875,14 @@
return 0;
}
- /* 4: have we encountered a read error ? */
- else if (req->flags & BF_READ_ERROR) {
- /* we cannot return any message on error */
+ /* 4: have we encountered a close ? */
+ 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;
req->analysers = 0;
t->fe->failed_req++;
+
if (!(t->flags & SN_ERR_MASK))
t->flags |= SN_ERR_CLICL;
if (!(t->flags & SN_FINST_MASK))
@@ -2816,18 +2784,22 @@
return 0;
}
+ /* too large response does not fit in buffer. */
+ else if (rep->flags & BF_FULL) {
+ goto hdr_response_bad;
+ }
- /* write error to client, read error or close from server */
- if (rep->flags & (BF_WRITE_ERROR|BF_SHUTW|BF_READ_ERROR|BF_SHUTR|BF_READ_NULL)) {
+ /* read error */
+ else if (rep->flags & BF_READ_ERROR) {
buffer_shutr_now(rep);
buffer_shutw_now(req);
//fd_delete(req->cons->fd);
//req->cons->state = SI_ST_CLO;
- if (t->srv) {
+ //if (t->srv) {
//t->srv->cur_sess--;
- t->srv->failed_resp++;
+ //t->srv->failed_resp++;
//sess_change_server(t, NULL);
- }
- t->be->failed_resp++;
+ //}
+ //t->be->failed_resp++;
rep->analysers = 0;
txn->status = 502;
client_return(t, error_message(t, HTTP_ERR_502));
@@ -2841,10 +2813,6 @@
return 0;
}
- /* too large response does not fit in buffer. */
- else if (rep->flags & BF_FULL) {
- goto hdr_response_bad;
- }
/* read timeout : return a 504 to the client. */
else if (rep->flags & BF_READ_TIMEOUT) {
buffer_shutr_now(rep);
@@ -2869,7 +2837,31 @@
// process_srv_queue(t->srv);
return 0;
}
+ /* write error to client, or close from server */
+ else if (rep->flags & (BF_WRITE_ERROR|BF_SHUTW|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->failed_resp++;
+ //sess_change_server(t, NULL);
+ }
+ t->be->failed_resp++;
+ rep->analysers = 0;
+ txn->status = 502;
+ client_return(t, error_message(t, HTTP_ERR_502));
+ if (!(t->flags & SN_ERR_MASK))
+ t->flags |= SN_ERR_SRVCL;
+ 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);
+ return 0;
+ }
rep->flags &= ~BF_MAY_FORWARD;
return 0;
}
@@ -3192,219 +3184,219 @@
return 0;
}
-/*
- * 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: 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,
- 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 (!((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 (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);
- if (!req->analysers) {
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= SN_ERR_CLICL;
- if (!(t->flags & SN_FINST_MASK)) {
- if (req->cons->err_type <= SI_ET_QUEUE_ABRT)
- t->flags |= SN_FINST_Q;
- else if (req->cons->err_type <= SI_ET_CONN_OTHER)
- 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_SHUTR_NOW|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_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);
- shutdown(t->cli_fd, SHUT_WR);
- 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 (req->cons->err_type <= SI_ET_QUEUE_ABRT)
- t->flags |= SN_FINST_Q;
- else if (req->cons->err_type <= SI_ET_CONN_OTHER)
- 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);
- 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 (req->cons->err_type <= SI_ET_QUEUE_ABRT)
- t->flags |= SN_FINST_Q;
- else if (req->cons->err_type <= SI_ET_CONN_OTHER)
- 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|BF_MAY_FORWARD)) != 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;
-}
+///*
+// * 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: 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,
+// 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 (!((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 (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);
+// if (!req->analysers) {
+// if (!(t->flags & SN_ERR_MASK))
+// t->flags |= SN_ERR_CLICL;
+// if (!(t->flags & SN_FINST_MASK)) {
+// if (req->cons->err_type <= SI_ET_QUEUE_ABRT)
+// t->flags |= SN_FINST_Q;
+// else if (req->cons->err_type <= SI_ET_CONN_OTHER)
+// 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_SHUTR_NOW|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_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);
+// shutdown(t->cli_fd, SHUT_WR);
+// 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 (req->cons->err_type <= SI_ET_QUEUE_ABRT)
+// t->flags |= SN_FINST_Q;
+// else if (req->cons->err_type <= SI_ET_CONN_OTHER)
+// 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);
+// 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 (req->cons->err_type <= SI_ET_QUEUE_ABRT)
+// t->flags |= SN_FINST_Q;
+// else if (req->cons->err_type <= SI_ET_CONN_OTHER)
+// 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|BF_MAY_FORWARD)) != 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 the pending connection has failed and should be retried,