[MAJOR] make unix sockets work again with stats
The unix protocol handler had not been updated during the last
stream_sock changes. This has been done now. There is still a
lot of duplicated code between session.c and proto_uxst.c due
to the way the session is handled. Session.c relies on the existence
of a frontend while it does not exist here.
It is easier to see the difference between the stats part (placed
in dumpstats.c) and the unix-stream part (in proto_uxst.c).
The hijacking function still needs to be dynamically set into the
response buffer, and some cleanup is still required, then all those
changes should be forward-ported to the HTTP part. Adding support
for new keywords should not cause trouble now.
diff --git a/src/proto_uxst.c b/src/proto_uxst.c
index cbc859e..0cb365c 100644
--- a/src/proto_uxst.c
+++ b/src/proto_uxst.c
@@ -48,6 +48,7 @@
#include <proto/proto_uxst.h>
#include <proto/queue.h>
#include <proto/session.h>
+#include <proto/stream_interface.h>
#include <proto/stream_sock.h>
#include <proto/task.h>
@@ -245,7 +246,7 @@
static int uxst_bind_listener(struct listener *listener)
{
int fd;
-
+
if (listener->state != LI_ASSIGNED)
return ERR_NONE; /* already bound */
@@ -255,7 +256,7 @@
listener->perm.ux.mode);
if (fd == -1)
return ERR_FATAL;
-
+
/* the socket is now listening */
listener->fd = fd;
listener->state = LI_LISTEN;
@@ -441,7 +442,7 @@
s->be = NULL;
s->cli_state = CL_STDATA;
- s->srv_state = SV_STIDLE;
+ s->ana_state = 0;
s->req = s->rep = NULL; /* will be allocated later */
s->si[0].state = s->si[0].prev_state = SI_ST_EST;
@@ -478,13 +479,13 @@
if ((s->req = pool_alloc2(pool2_buffer)) == NULL)
goto out_free_task;
+ buffer_init(s->req);
s->req->prod = &s->si[0];
s->req->cons = &s->si[1];
s->si[0].ib = s->si[1].ob = s->req;
s->req->flags |= BF_READ_ATTACHED; /* the producer is already connected */
- if (!s->req->analysers)
- buffer_write_ena(s->req); /* don't wait to establish connection */
+ s->req->analysers = l->analysers;
s->req->wto = TICK_ETERNITY;
s->req->cto = TICK_ETERNITY;
@@ -539,10 +540,7 @@
}
actconn++;
totalconn++;
-
- //fprintf(stderr, "accepting from %p => %d conn, %d total, task=%p, cfd=%d, maxfd=%d\n", p, actconn, totalconn, t, cfd, maxfd);
- } /* end of while (p->feconn < p->maxconn) */
- //fprintf(stderr,"fct %s:%d\n", __FUNCTION__, __LINE__);
+ }
return 0;
out_free_req:
@@ -557,929 +555,381 @@
return 0;
}
-/*
- * manages the client FSM and its socket. It returns 1 if a state has changed
- * (and a resync may be needed), otherwise 0.
+/* Parses the request line in <cmd> and possibly starts dumping stats on
+ * s->rep with the hijack bit set. Returns 1 if OK, 0 in case of any error.
+ * The line is modified after parsing.
*/
-static int process_uxst_cli(struct session *t)
+int unix_sock_parse_request(struct session *s, char *line)
{
- int s = t->srv_state;
- int c = t->cli_state;
- struct buffer *req = t->req;
- struct buffer *rep = t->rep;
- //fprintf(stderr,"fct %s:%d\n", __FUNCTION__, __LINE__);
- if (c == CL_STDATA) {
- /* FIXME: this error handling is partly buggy because we always report
- * a 'DATA' phase while we don't know if the server was in IDLE, CONN
- * or HEADER phase. BTW, it's not logical to expire the client while
- * we're waiting for the server to connect.
- */
- /* 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;
- 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 (s == SV_STCONN)
- t->flags |= SN_FINST_C;
- else
- t->flags |= SN_FINST_D;
- }
- return 1;
- }
- /* last read, or end of server write */
- else if (req->flags & BF_READ_NULL || s == SV_STSHUTW || s == SV_STCLOSE) {
- EV_FD_CLR(t->cli_fd, DIR_RD);
- buffer_shutr(req);
- t->cli_state = CL_STSHUTR;
- return 1;
- }
- /* last server read and buffer empty */
- else if ((s == SV_STSHUTR || s == SV_STCLOSE) && (rep->flags & BF_EMPTY)) {
- EV_FD_CLR(t->cli_fd, DIR_WR);
- buffer_shutw(rep);
- shutdown(t->cli_fd, SHUT_WR);
- /* We must ensure that the read part is still alive when switching
- * to shutw */
- EV_FD_SET(t->cli_fd, DIR_RD);
- req->rex = tick_add_ifset(now_ms, req->rto);
- t->cli_state = CL_STSHUTW;
- //fprintf(stderr,"%p:%s(%d), c=%d, s=%d\n", t, __FUNCTION__, __LINE__, t->cli_state, t->cli_state);
- return 1;
- }
- /* read timeout */
- else if (tick_is_expired(req->rex, now_ms)) {
- EV_FD_CLR(t->cli_fd, DIR_RD);
- buffer_shutr(req);
- t->cli_state = CL_STSHUTR;
- 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 (s == SV_STCONN)
- t->flags |= SN_FINST_C;
- else
- t->flags |= SN_FINST_D;
- }
- return 1;
- }
- /* write timeout */
- else if (tick_is_expired(rep->wex, now_ms)) {
- EV_FD_CLR(t->cli_fd, DIR_WR);
- buffer_shutw(rep);
- shutdown(t->cli_fd, SHUT_WR);
- /* We must ensure that the read part is still alive when switching
- * to shutw */
- EV_FD_SET(t->cli_fd, DIR_RD);
- req->rex = tick_add_ifset(now_ms, req->rto);
+ char *args[MAX_UXST_ARGS + 1];
+ int arg;
- t->cli_state = CL_STSHUTW;
- 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 (s == SV_STCONN)
- t->flags |= SN_FINST_C;
- else
- t->flags |= SN_FINST_D;
- }
- return 1;
- }
+ while (isspace((unsigned char)*line))
+ line++;
- 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 {
- /* there's still some space in the buffer */
- if (EV_FD_COND_S(t->cli_fd, DIR_RD)) {
- if (!req->rto ||
- (t->srv_state < SV_STDATA && req->wto))
- /* If the client has no timeout, or if the server not ready yet, and we
- * know for sure that it can expire, then it's cleaner to disable the
- * timeout on the client side so that too low values cannot make the
- * sessions abort too early.
- */
- req->rex = TICK_ETERNITY;
- else
- req->rex = tick_add(now_ms, req->rto);
- }
- }
+ arg = 0;
+ args[arg] = line;
- if ((rep->flags & BF_EMPTY) ||
- ((s < SV_STDATA) /* FIXME: this may be optimized && (rep->w == rep->h)*/)) {
- if (EV_FD_COND_C(t->cli_fd, DIR_WR)) {
- /* stop writing */
- rep->wex = TICK_ETERNITY;
- }
- } else {
- /* buffer not empty */
- if (EV_FD_COND_S(t->cli_fd, DIR_WR)) {
- /* restart writing */
- rep->wex = tick_add_ifset(now_ms, rep->wto);
- if (rep->wex) {
- /* FIXME: to prevent the client from expiring read timeouts during writes,
- * we refresh it. */
- req->rex = rep->wex;
- }
- }
- }
- return 0; /* other cases change nothing */
- }
- else if (c == CL_STSHUTR) {
- if (rep->flags & BF_WRITE_ERROR) {
- buffer_shutw(rep);
- fd_delete(t->cli_fd);
- t->cli_state = CL_STCLOSE;
- 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 (s == SV_STCONN)
- t->flags |= SN_FINST_C;
- else
- t->flags |= SN_FINST_D;
- }
- return 1;
- }
- else if ((s == SV_STSHUTR || s == SV_STCLOSE) && (rep->flags & BF_EMPTY)) {
- buffer_shutw(rep);
- fd_delete(t->cli_fd);
- t->cli_state = CL_STCLOSE;
- return 1;
- }
- else if (tick_is_expired(rep->wex, now_ms)) {
- buffer_shutw(rep);
- fd_delete(t->cli_fd);
- t->cli_state = CL_STCLOSE;
- 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 (s == SV_STCONN)
- t->flags |= SN_FINST_C;
- else
- t->flags |= SN_FINST_D;
- }
- return 1;
- }
+ while (*line && arg < MAX_UXST_ARGS) {
+ if (isspace((unsigned char)*line)) {
+ *line++ = '\0';
- if (rep->flags & BF_EMPTY) {
- if (EV_FD_COND_C(t->cli_fd, DIR_WR)) {
- /* stop writing */
- rep->wex = TICK_ETERNITY;
- }
- } else {
- /* buffer not empty */
- if (EV_FD_COND_S(t->cli_fd, DIR_WR)) {
- /* restart writing */
- rep->wex = tick_add_ifset(now_ms, rep->wto);
- }
+ while (isspace((unsigned char)*line))
+ line++;
+
+ args[++arg] = line;
+ continue;
}
- return 0;
+
+ line++;
}
- else if (c == CL_STSHUTW) {
- if (req->flags & BF_READ_ERROR) {
- buffer_shutr(req);
- fd_delete(t->cli_fd);
- t->cli_state = CL_STCLOSE;
- 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 (s == SV_STCONN)
- t->flags |= SN_FINST_C;
- else
- t->flags |= SN_FINST_D;
- }
- return 1;
- }
- else if (req->flags & BF_READ_NULL || s == SV_STSHUTW || s == SV_STCLOSE) {
- buffer_shutr(req);
- fd_delete(t->cli_fd);
- t->cli_state = CL_STCLOSE;
- return 1;
- }
- else if (tick_is_expired(req->rex, now_ms)) {
- buffer_shutr(req);
- fd_delete(t->cli_fd);
- t->cli_state = CL_STCLOSE;
- 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 (s == SV_STCONN)
- t->flags |= SN_FINST_C;
- else
- t->flags |= SN_FINST_D;
- }
- return 1;
- }
- else if (req->flags & BF_FULL) {
- /* no room to read more data */
- /* FIXME-20050705: is it possible for a client to maintain a session
- * after the timeout by sending more data after it receives a close ?
- */
+ while (++arg <= MAX_UXST_ARGS)
+ args[arg] = line;
- if (EV_FD_COND_C(t->cli_fd, DIR_RD)) {
- /* stop reading until we get some space */
- req->rex = TICK_ETERNITY;
+ if (strcmp(args[0], "show") == 0) {
+ if (strcmp(args[1], "stat") == 0) {
+ if (*args[2] && *args[3] && *args[4]) {
+ s->data_ctx.stats.flags |= STAT_BOUND;
+ s->data_ctx.stats.iid = atoi(args[2]);
+ s->data_ctx.stats.type = atoi(args[3]);
+ s->data_ctx.stats.sid = atoi(args[4]);
}
- } else {
- /* there's still some space in the buffer */
- if (EV_FD_COND_S(t->cli_fd, DIR_RD)) {
- req->rex = tick_add_ifset(now_ms, req->rto);
- }
+
+ s->data_ctx.stats.flags |= STAT_SHOW_STAT;
+ s->data_ctx.stats.flags |= STAT_FMT_CSV;
+ s->ana_state = STATS_ST_REP;
+ buffer_start_hijack(s->rep);
+ stats_dump_raw_to_buffer(s, s->rep);
}
- return 0;
- }
- else { /* 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?t->be->id:"",
- (unsigned short)t->cli_fd, (unsigned short)t->req->cons->fd);
- write(1, trash, len);
+ else if (strcmp(args[1], "info") == 0) {
+ s->data_ctx.stats.flags |= STAT_SHOW_INFO;
+ s->data_ctx.stats.flags |= STAT_FMT_CSV;
+ s->ana_state = STATS_ST_REP;
+ buffer_start_hijack(s->rep);
+ stats_dump_raw_to_buffer(s, s->rep);
+ }
+ else { /* neither "stat" nor "info" */
+ return 0;
}
+ }
+ else { /* not "show" */
return 0;
}
- return 0;
+ return 1;
}
-#if 0
- /* FIXME! This part has not been completely converted yet, and it may
- * still be very specific to TCPv4 ! Also, it relies on some parameters
- * such as conn_retries which are not set upon accept().
- */
-/*
- * Manages the server FSM and its socket. It returns 1 if a state has changed
- * (and a resync may be needed), otherwise 0.
+/* Processes the stats interpreter on the statistics socket.
+ * In order to ease the transition, we simply simulate the server status
+ * for now. It only knows states STATS_ST_INIT, STATS_ST_REQ, STATS_ST_REP, and
+ * STATS_ST_CLOSE. It removes the AN_REQ_UNIX_STATS bit from req->analysers
+ * once done. It always returns 0.
*/
-static int process_uxst_srv(struct session *t)
+int uxst_req_analyser_stats(struct session *s, struct buffer *req)
{
- int s = t->srv_state;
- int c = t->cli_state;
- struct buffer *req = t->req;
- struct buffer *rep = t->rep;
- int conn_err;
+ char *line, *p;
- if (s == SV_STIDLE) {
- if (c == CL_STCLOSE || c == CL_STSHUTW ||
- (c == CL_STSHUTR &&
- (t->req->flags & BF_EMPTY || t->be->options & PR_O_ABRT_CLOSE))) { /* give up */
- tv_eternity(&req->cex);
- if (t->pend_pos)
- t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now);
- srv_close_with_err(t, SN_ERR_CLICL, t->pend_pos ? SN_FINST_Q : SN_FINST_C);
- return 1;
- }
- else {
- /* FIXME: reimplement the TARPIT check here */
+ switch (s->ana_state) {
+ case STATS_ST_INIT:
+ /* Stats output not initialized yet */
+ memset(&s->data_ctx.stats, 0, sizeof(s->data_ctx.stats));
+ s->data_source = DATA_SRC_STATS;
+ s->ana_state = STATS_ST_REQ;
+ /* fall through */
- /* 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 (!tv_isle(&req->cex, &now))
- return 0;
- else {
- /* we've been waiting too long here */
- tv_eternity(&req->cex);
- t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now);
- srv_close_with_err(t, SN_ERR_SRVTO, SN_FINST_Q);
- if (t->srv)
- t->srv->failed_conns++;
- if (t->fe)
- t->fe->failed_conns++;
- return 1;
- }
- }
+ case STATS_ST_REQ:
+ /* Now, stats are initialized, hijack is not set, and
+ * we are waiting for a complete request line.
+ */
- do {
- /* first, get a connection */
- if (srv_redispatch_connect(t))
- return t->srv_state != SV_STIDLE;
+ line = s->req->data;
+ p = memchr(line, '\n', s->req->l);
- /* 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);
- return t->srv_state != SV_STIDLE;
- }
- } while (1);
+ if (p) {
+ *p = '\0';
+ if (!unix_sock_parse_request(s, line)) {
+ /* invalid request */
+ buffer_shutw_now(s->req);
+ s->ana_state = 0;
+ req->analysers = 0;
+ return 0;
+ }
}
- }
- else if (s == SV_STCONN) { /* connection in progress */
- if (c == CL_STCLOSE || c == CL_STSHUTW ||
- (c == CL_STSHUTR &&
- ((t->req->flags & BF_EMPTY && !(req->flags & BF_WRITE_ACTIVITY)) ||
- t->be->options & PR_O_ABRT_CLOSE))) { /* give up */
- tv_eternity(&req->cex);
- fd_delete(t->srv_fd);
- if (t->srv)
- t->srv->cur_sess--;
- srv_close_with_err(t, SN_ERR_CLICL, SN_FINST_C);
- return 1;
+ /* processing a valid or incomplete request */
+ if ((req->flags & BF_FULL) || /* invalid request */
+ (req->flags & BF_READ_ERROR) || /* input error */
+ (req->flags & BF_READ_TIMEOUT) || /* read timeout */
+ tick_is_expired(req->analyse_exp, now_ms) || /* request timeout */
+ (req->flags & BF_SHUTR)) { /* input closed */
+ buffer_shutw_now(s->req);
+ s->ana_state = 0;
+ req->analysers = 0;
+ return 0;
}
- if (!(req->flags & BF_WRITE_ACTIVITY) && !tv_isle(&req->cex, &now)) {
- //fprintf(stderr,"1: c=%d, s=%d, now=%d.%06d, exp=%d.%06d\n", c, s, now.tv_sec, now.tv_usec, req->cex.tv_sec, req->cex.tv_usec);
- return 0; /* nothing changed */
- }
- else if (!(req->flags & BF_WRITE_ACTIVITY) || (req->flags & BF_WRITE_ERROR)) {
- /* timeout, asynchronous connect error or first write error */
- //fprintf(stderr,"2: c=%d, s=%d\n", c, s);
-
- fd_delete(t->srv_fd);
- if (t->srv)
- t->srv->cur_sess--;
- if (!(req->flags & BF_WRITE_ACTIVITY))
- conn_err = SN_ERR_SRVTO; // it was a connect timeout.
- else
- conn_err = SN_ERR_SRVCL; // it was an asynchronous connect error.
+ /* don't forward nor abort */
+ buffer_write_dis(req);
+ return 0;
- /* ensure that we have enough retries left */
- if (srv_count_retry_down(t, conn_err))
- return 1;
+ case STATS_ST_REP:
+ /* do nothing while response is being processed */
+ buffer_write_dis(s->req);
+ 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);
+ case STATS_ST_CLOSE:
+ /* end of dump */
+ s->req->analysers &= ~AN_REQ_UNIX_STATS;
+ s->ana_state = 0;
+ break;
+ }
+ return 0;
+}
- if (t->srv)
- t->srv->failed_conns++;
- t->be->failed_conns++;
- t->flags &= ~(SN_DIRECT | SN_ASSIGNED | SN_ADDR_SET);
- t->srv = NULL; /* it's left to the dispatcher to choose a server */
+/* This function is the unix-stream equivalent of the global process_session().
+ * It is currently limited to unix-stream processing on control sockets such as
+ * stats, and has no server-side. The two functions should be merged into one
+ * once client and server sides are better delimited. Note that the server-side
+ * still exists but remains in SI_ST_INI state forever, so that any call is a
+ * NOP.
+ */
+void uxst_process_session(struct task *t, int *next)
+{
+ struct session *s = t->context;
+ struct listener *listener;
+ int resync;
+ unsigned int rqf_last, rpf_last;
- /* first, get a connection */
- if (srv_redispatch_connect(t))
- return t->srv_state != SV_STIDLE;
- }
+ /* 1a: Check for low level timeouts if needed. We just set a flag on
+ * stream interfaces when their timeouts have expired.
+ */
+ if (unlikely(t->state & TASK_WOKEN_TIMER)) {
+ stream_int_check_timeouts(&s->si[0]);
+ buffer_check_timeouts(s->req);
+ buffer_check_timeouts(s->rep);
+ }
- 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);
- return t->srv_state != SV_STCONN;
- }
+ /* copy req/rep flags so that we can detect shutdowns */
+ rqf_last = s->req->flags;
+ rpf_last = s->rep->flags;
- /* we need to redispatch the connection to another server */
- if (srv_redispatch_connect(t))
- return t->srv_state != SV_STCONN;
- } while (1);
+ /* 1b: check for low-level errors reported at the stream interface. */
+ if (unlikely(s->si[0].flags & SI_FL_ERR)) {
+ if (s->si[0].state == SI_ST_EST || s->si[0].state == SI_ST_DIS) {
+ s->si[0].shutr(&s->si[0]);
+ s->si[0].shutw(&s->si[0]);
+ stream_int_report_error(&s->si[0]);
}
- else { /* no error or write 0 */
- t->logs.t_connect = tv_ms_elapsed(&t->logs.tv_accept, &now);
-
- //fprintf(stderr,"3: c=%d, s=%d\n", c, s);
- if (req->flags & BF_EMPTY) /* nothing to write */ {
- EV_FD_CLR(t->srv_fd, DIR_WR);
- tv_eternity(&req->wex);
- } else /* need the right to write */ {
- EV_FD_SET(t->srv_fd, DIR_WR);
- if (tv_add_ifset(&req->wex, &now, &req->wto)) {
- /* FIXME: to prevent the server from expiring read timeouts during writes,
- * we refresh it. */
- rep->rex = req->wex;
- }
- else
- tv_eternity(&req->wex);
- }
+ }
- EV_FD_SET(t->srv_fd, DIR_RD);
- if (!tv_add_ifset(&rep->rex, &now, &rep->rto))
- tv_eternity(&rep->rex);
-
- t->srv_state = SV_STDATA;
- if (t->srv)
- t->srv->cum_sess++;
- buffer_set_rlim(rep, BUFSIZE); /* no rewrite needed */
+ /* check buffer timeouts, and close the corresponding stream interfaces
+ * for future reads or writes. Note: this will also concern upper layers
+ * but we do not touch any other flag. We must be careful and correctly
+ * detect state changes when calling them.
+ */
+ if (unlikely(s->req->flags & (BF_READ_TIMEOUT|BF_WRITE_TIMEOUT))) {
+ if (s->req->flags & BF_READ_TIMEOUT)
+ s->req->prod->shutr(s->req->prod);
+ if (s->req->flags & BF_WRITE_TIMEOUT)
+ s->req->cons->shutw(s->req->cons);
+ }
- /* 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 && t->fe->to_log && !(t->logs.logwait & LW_BYTES)) {
- t->logs.t_close = t->logs.t_connect; /* to get a valid end date */
- //uxst_sess_log(t);
- }
- tv_eternity(&req->cex);
- return 1;
- }
+ if (unlikely(s->rep->flags & (BF_READ_TIMEOUT|BF_WRITE_TIMEOUT))) {
+ if (s->rep->flags & BF_READ_TIMEOUT)
+ s->rep->prod->shutr(s->rep->prod);
+ if (s->rep->flags & BF_WRITE_TIMEOUT)
+ s->rep->cons->shutw(s->rep->cons);
}
- else if (s == SV_STDATA) {
- /* read or write error */
- if (req->flags & BF_WRITE_ERROR || rep->flags & BF_READ_ERROR) {
- buffer_shutr(rep);
- buffer_shutw(req);
- fd_delete(t->srv_fd);
- if (t->srv) {
- t->srv->cur_sess--;
- t->srv->failed_resp++;
- }
- t->be->failed_resp++;
- t->srv_state = SV_STCLOSE;
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= SN_ERR_SRVCL;
- if (!(t->flags & SN_FINST_MASK))
- t->flags |= SN_FINST_D;
- /* 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 (may_dequeue_tasks(t->srv, t->be))
- process_srv_queue(t->srv);
- return 1;
- }
- /* last read, or end of client write */
- else if (rep->flags & BF_READ_NULL || c == CL_STSHUTW || c == CL_STCLOSE) {
- EV_FD_CLR(t->srv_fd, DIR_RD);
- buffer_shutr(rep);
- t->srv_state = SV_STSHUTR;
- //fprintf(stderr,"%p:%s(%d), c=%d, s=%d\n", t, __FUNCTION__, __LINE__, t->cli_state, t->cli_state);
- return 1;
- }
- /* end of client read and no more data to send */
- else if ((c == CL_STSHUTR || c == CL_STCLOSE) && (req->flags & BF_EMPTY)) {
- EV_FD_CLR(t->srv_fd, DIR_WR);
- buffer_shutw(req);
- shutdown(t->srv_fd, SHUT_WR);
- /* We must ensure that the read part is still alive when switching
- * to shutw */
- EV_FD_SET(t->srv_fd, DIR_RD);
- tv_add_ifset(&rep->rex, &now, &rep->rto);
+ /* Check for connection closure */
- t->srv_state = SV_STSHUTW;
- return 1;
- }
- /* read timeout */
- else if (tv_isle(&rep->rex, &now)) {
- EV_FD_CLR(t->srv_fd, DIR_RD);
- buffer_shutr(rep);
- t->srv_state = SV_STSHUTR;
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= SN_ERR_SRVTO;
- if (!(t->flags & SN_FINST_MASK))
- t->flags |= SN_FINST_D;
- return 1;
- }
- /* write timeout */
- else if (tv_isle(&req->wex, &now)) {
- EV_FD_CLR(t->srv_fd, DIR_WR);
- buffer_shutw(req);
- shutdown(t->srv_fd, SHUT_WR);
- /* We must ensure that the read part is still alive when switching
- * to shutw */
- EV_FD_SET(t->srv_fd, DIR_RD);
- tv_add_ifset(&rep->rex, &now, &rep->rto);
- t->srv_state = SV_STSHUTW;
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= SN_ERR_SRVTO;
- if (!(t->flags & SN_FINST_MASK))
- t->flags |= SN_FINST_D;
- return 1;
- }
+ resync_stream_interface:
- /* recompute request time-outs */
- if (req->flags & BF_EMPTY) {
- if (EV_FD_COND_C(t->srv_fd, DIR_WR)) {
- /* stop writing */
- tv_eternity(&req->wex);
- }
- }
- else { /* buffer not empty, there are still data to be transferred */
- if (EV_FD_COND_S(t->srv_fd, DIR_WR)) {
- /* restart writing */
- if (tv_add_ifset(&req->wex, &now, &req->wto)) {
- /* FIXME: to prevent the server from expiring read timeouts during writes,
- * we refresh it. */
- rep->rex = req->wex;
- }
- else
- tv_eternity(&req->wex);
- }
- }
+ /* nothing special to be done on client side */
+ if (unlikely(s->req->prod->state == SI_ST_DIS))
+ s->req->prod->state = SI_ST_CLO;
- /* recompute response time-outs */
- if (rep->l == BUFSIZE) { /* no room to read more data */
- if (EV_FD_COND_C(t->srv_fd, DIR_RD)) {
- tv_eternity(&rep->rex);
- }
- }
- else {
- if (EV_FD_COND_S(t->srv_fd, DIR_RD)) {
- if (!tv_add_ifset(&rep->rex, &now, &rep->rto))
- tv_eternity(&rep->rex);
- }
- }
+ /*
+ * Note: of the transient states (REQ, CER, DIS), only REQ may remain
+ * at this point.
+ */
- return 0; /* other cases change nothing */
- }
- else if (s == SV_STSHUTR) {
- if (req->flags & BF_WRITE_ERROR) {
- //EV_FD_CLR(t->srv_fd, DIR_WR);
- buffer_shutw(req);
- fd_delete(t->srv_fd);
- if (t->srv) {
- t->srv->cur_sess--;
- t->srv->failed_resp++;
- }
- t->be->failed_resp++;
- //close(t->srv_fd);
- t->srv_state = SV_STCLOSE;
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= SN_ERR_SRVCL;
- if (!(t->flags & SN_FINST_MASK))
- t->flags |= SN_FINST_D;
- /* 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 (may_dequeue_tasks(t->srv, t->be))
- process_srv_queue(t->srv);
+ /**** Process layer 7 below ****/
- return 1;
- }
- else if ((c == CL_STSHUTR || c == CL_STCLOSE) && (req->flags & BF_EMPTY)) {
- //EV_FD_CLR(t->srv_fd, DIR_WR);
- buffer_shutw(req);
- fd_delete(t->srv_fd);
- if (t->srv)
- t->srv->cur_sess--;
- //close(t->srv_fd);
- t->srv_state = SV_STCLOSE;
- /* 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 (may_dequeue_tasks(t->srv, t->be))
- process_srv_queue(t->srv);
+ resync = 0;
- return 1;
- }
- else if (tv_isle(&req->wex, &now)) {
- //EV_FD_CLR(t->srv_fd, DIR_WR);
- buffer_shutw(req);
- fd_delete(t->srv_fd);
- if (t->srv)
- t->srv->cur_sess--;
- //close(t->srv_fd);
- t->srv_state = SV_STCLOSE;
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= SN_ERR_SRVTO;
- if (!(t->flags & SN_FINST_MASK))
- t->flags |= SN_FINST_D;
- /* 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 (may_dequeue_tasks(t->srv, t->be))
- process_srv_queue(t->srv);
+ /* Analyse request */
+ if ((s->req->flags & BF_MASK_ANALYSER) ||
+ (s->req->flags ^ rqf_last) & BF_MASK_STATIC) {
+ unsigned int flags = s->req->flags;
- return 1;
- }
- else if (req->flags & BF_EMPTY) {
- if (EV_FD_COND_C(t->srv_fd, DIR_WR)) {
- /* stop writing */
- tv_eternity(&req->wex);
- }
- }
- else { /* buffer not empty */
- if (EV_FD_COND_S(t->srv_fd, DIR_WR)) {
- /* restart writing */
- if (!tv_add_ifset(&req->wex, &now, &req->wto))
- tv_eternity(&req->wex);
- }
- }
- return 0;
- }
- else if (s == SV_STSHUTW) {
- if (rep->flags & BF_READ_ERROR) {
- //EV_FD_CLR(t->srv_fd, DIR_RD);
- buffer_shutr(rep);
- fd_delete(t->srv_fd);
- if (t->srv) {
- t->srv->cur_sess--;
- t->srv->failed_resp++;
- }
- t->be->failed_resp++;
- //close(t->srv_fd);
- t->srv_state = SV_STCLOSE;
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= SN_ERR_SRVCL;
- if (!(t->flags & SN_FINST_MASK))
- t->flags |= SN_FINST_D;
- /* 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 (may_dequeue_tasks(t->srv, t->be))
- process_srv_queue(t->srv);
+ if (s->req->prod->state >= SI_ST_EST) {
+ /* it's up to the analysers to reset write_ena */
+ buffer_write_ena(s->req);
- return 1;
- }
- else if (rep->flags & BF_READ_NULL || c == CL_STSHUTW || c == CL_STCLOSE) {
- //EV_FD_CLR(t->srv_fd, DIR_RD);
- buffer_shutr(rep);
- fd_delete(t->srv_fd);
- if (t->srv)
- t->srv->cur_sess--;
- //close(t->srv_fd);
- t->srv_state = SV_STCLOSE;
- /* 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.
+ /* We will call all analysers for which a bit is set in
+ * s->req->analysers, following the bit order from LSB
+ * to MSB. The analysers must remove themselves from
+ * the list when not needed. This while() loop is in
+ * fact a cleaner if().
*/
- if (may_dequeue_tasks(t->srv, t->be))
- process_srv_queue(t->srv);
+ while (s->req->analysers) {
+ if (s->req->analysers & AN_REQ_UNIX_STATS)
+ if (!uxst_req_analyser_stats(s, s->req))
+ break;
- return 1;
- }
- else if (tv_isle(&rep->rex, &now)) {
- //EV_FD_CLR(t->srv_fd, DIR_RD);
- buffer_shutr(rep);
- fd_delete(t->srv_fd);
- if (t->srv)
- t->srv->cur_sess--;
- //close(t->srv_fd);
- t->srv_state = SV_STCLOSE;
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= SN_ERR_SRVTO;
- if (!(t->flags & SN_FINST_MASK))
- t->flags |= SN_FINST_D;
- /* 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 (may_dequeue_tasks(t->srv, t->be))
- process_srv_queue(t->srv);
+ /* Just make sure that nobody set a wrong flag causing an endless loop */
+ s->req->analysers &= AN_REQ_UNIX_STATS;
- return 1;
- }
- else if (rep->l == BUFSIZE) { /* no room to read more data */
- if (EV_FD_COND_C(t->srv_fd, DIR_RD)) {
- tv_eternity(&rep->rex);
+ /* we don't want to loop anyway */
+ break;
}
}
- else {
- if (EV_FD_COND_S(t->srv_fd, DIR_RD)) {
- if (!tv_add_ifset(&rep->rex, &now, &rep->rto))
- tv_eternity(&rep->rex);
- }
- }
- return 0;
- }
- else { /* 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);
- }
- return 0;
+ s->req->flags &= BF_CLEAR_READ & BF_CLEAR_WRITE & BF_CLEAR_TIMEOUT;
+ flags &= BF_CLEAR_READ & BF_CLEAR_WRITE & BF_CLEAR_TIMEOUT;
+ if (s->req->flags != flags)
+ resync = 1;
}
- return 0;
-}
-/* Processes the client and server jobs of a session task, then
- * puts it back to the wait queue in a clean state, or
- * cleans up its resources if it must be deleted. Returns
- * the time the task accepts to wait, or TIME_ETERNITY for
- * infinity.
- */
-void process_uxst_session(struct task *t, int *next)
-{
- struct session *s = t->context;
- int fsm_resync = 0;
+ /* reflect what the L7 analysers have seen last */
+ rqf_last = s->req->flags;
- do {
- fsm_resync = 0;
- fsm_resync |= process_uxst_cli(s);
- if (s->srv_state == SV_STIDLE) {
- if (s->cli_state == CL_STCLOSE || s->cli_state == CL_STSHUTW) {
- s->srv_state = SV_STCLOSE;
- fsm_resync |= 1;
- continue;
- }
- if (s->cli_state == CL_STSHUTR ||
- (s->req->flags & BF_FULL)) {
- if (s->req->flags & BF_EMPTY) {
- s->srv_state = SV_STCLOSE;
- fsm_resync |= 1;
- continue;
- }
- /* OK we have some remaining data to process */
- /* Just as an exercice, we copy the req into the resp,
- * and flush the req.
- */
- memcpy(s->rep->data, s->req->data, sizeof(s->rep->data));
- s->rep->l = s->req->l;
- buffer_set_rlim(s->rep, BUFSIZE);
- s->rep->w = s->rep->data;
- s->rep->lr = s->rep->r = s->rep->data + s->rep->l;
+ /*
+ * Now forward all shutdown requests between both sides of the buffer
+ */
- s->req->l = 0;
- s->srv_state = SV_STCLOSE;
+ /* first, let's check if the request buffer needs to shutdown(write) */
+ if (unlikely((s->req->flags & (BF_SHUTW|BF_SHUTW_NOW|BF_EMPTY|BF_HIJACK|BF_WRITE_ENA|BF_SHUTR)) ==
+ (BF_EMPTY|BF_WRITE_ENA|BF_SHUTR)))
+ buffer_shutw_now(s->req);
- fsm_resync |= 1;
- continue;
- }
- }
- } while (fsm_resync);
+ /* shutdown(write) pending */
+ if (unlikely((s->req->flags & (BF_SHUTW|BF_SHUTW_NOW)) == BF_SHUTW_NOW))
+ s->req->cons->shutw(s->req->cons);
- if (likely(s->cli_state != CL_STCLOSE || s->srv_state != SV_STCLOSE)) {
+ /* shutdown(write) done on server side, we must stop the client too */
+ if (unlikely((s->req->flags & (BF_SHUTW|BF_SHUTR|BF_SHUTR_NOW)) == BF_SHUTW &&
+ !s->req->analysers))
+ buffer_shutr_now(s->req);
- if ((s->fe->options & PR_O_CONTSTATS) && (s->flags & SN_BE_ASSIGNED))
- session_process_counters(s);
+ /* shutdown(read) pending */
+ if (unlikely((s->req->flags & (BF_SHUTR|BF_SHUTR_NOW)) == BF_SHUTR_NOW))
+ s->req->prod->shutr(s->req->prod);
- s->req->flags &= BF_CLEAR_READ & BF_CLEAR_WRITE;
- s->rep->flags &= BF_CLEAR_READ & BF_CLEAR_WRITE;
+ /*
+ * Here we want to check if we need to resync or not.
+ */
+ if ((s->req->flags ^ rqf_last) & BF_MASK_STATIC)
+ resync = 1;
- t->expire = s->req->rex;
- tv_min(&t->expire, &s->req->rex, &s->req->wex);
- tv_bound(&t->expire, &s->req->cex);
- tv_bound(&t->expire, &s->rep->rex);
- tv_bound(&t->expire, &s->rep->wex);
+ s->req->flags &= BF_CLEAR_READ & BF_CLEAR_WRITE & BF_CLEAR_TIMEOUT;
- /* restore t to its place in the task list */
- task_queue(t);
+ /* according to benchmarks, it makes sense to resync now */
+ if (resync)
+ goto resync_stream_interface;
- *next = t->expire;
- return; /* nothing more to do */
- }
- if (s->fe)
- s->fe->feconn--;
- if (s->be && (s->flags & SN_BE_ASSIGNED))
- s->be->beconn--;
- actconn--;
-
- if (unlikely((global.mode & MODE_DEBUG) &&
- (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)))) {
- int len;
- len = sprintf(trash, "%08x:%s.closed[%04x:%04x]\n",
- s->uniq_id, s->be->id,
- (unsigned short)s->cli_fd, (unsigned short)s->srv_fd);
- write(1, trash, len);
- }
+ /* Analyse response */
- s->logs.t_close = tv_ms_elapsed(&s->logs.tv_accept, &now);
- session_process_counters(s);
+ buffer_write_ena(s->rep);
+ if (unlikely(s->rep->flags & BF_HIJACK)) {
+ /* In inject mode, we wake up everytime something has
+ * happened on the write side of the buffer.
+ */
+ unsigned int flags = s->rep->flags;
- /* let's do a final log if we need it */
- if (s->logs.logwait &&
- !(s->flags & SN_MONITOR) &&
- (s->req->total || !(s->fe && s->fe->options & PR_O_NULLNOLOG))) {
- //uxst_sess_log(s);
+ if ((s->rep->flags & (BF_WRITE_PARTIAL|BF_WRITE_ERROR|BF_SHUTW)) &&
+ !(s->rep->flags & BF_FULL)) {
+ /* it is the only hijacker right now */
+ stats_dump_raw_to_buffer(s, s->rep);
+ }
+ s->rep->flags &= BF_CLEAR_READ & BF_CLEAR_WRITE & BF_CLEAR_TIMEOUT;
+ flags &= BF_CLEAR_READ & BF_CLEAR_WRITE & BF_CLEAR_TIMEOUT;
+ if (s->rep->flags != flags)
+ resync = 1;
}
-
- /* the task MUST not be in the run queue anymore */
- task_delete(t);
- session_free(s);
- task_free(t);
- tv_eternity(next);
-}
-#endif /* not converted */
-
-
-/* Processes data exchanges on the statistics socket. The client processing
- * is called and the task is put back in the wait queue or it is cleared.
- * In order to ease the transition, we simply simulate the server status
- * for now. It only knows states SV_STIDLE, SV_STCONN, SV_STDATA, and
- * SV_STCLOSE. Returns in <next> the task's expiration date.
- */
-void process_uxst_stats(struct task *t, int *next)
-{
- struct session *s = t->context;
- struct listener *listener;
- int fsm_resync = 0;
- int last_rep_l;
-
- do {
- char *args[MAX_UXST_ARGS + 1];
- char *line, *p;
- int arg;
-
- fsm_resync = process_uxst_cli(s);
+ else if ((s->rep->flags & BF_MASK_ANALYSER) ||
+ (s->rep->flags ^ rpf_last) & BF_MASK_STATIC) {
+ unsigned int flags = s->rep->flags;
- if (s->cli_state == CL_STCLOSE || s->cli_state == CL_STSHUTW) {
- s->srv_state = SV_STCLOSE;
- break;
+ if (s->rep->prod->state >= SI_ST_EST) {
+ /* it's up to the analysers to reset write_ena */
+ buffer_write_ena(s->rep);
}
-
- switch (s->srv_state) {
- case SV_STIDLE:
- /* stats output not initialized yet */
- memset(&s->data_ctx.stats, 0, sizeof(s->data_ctx.stats));
- s->data_source = DATA_SRC_STATS;
- s->srv_state = SV_STCONN;
- fsm_resync |= 1;
- break;
-
- case SV_STCONN: /* should be changed to SV_STHEADERS or something more obvious */
- /* stats initialized, but waiting for the command */
- line = s->req->data;
- p = memchr(line, '\n', s->req->l);
-
- if (!p)
- continue;
+ s->rep->flags &= BF_CLEAR_READ & BF_CLEAR_WRITE & BF_CLEAR_TIMEOUT;
+ flags &= BF_CLEAR_READ & BF_CLEAR_WRITE & BF_CLEAR_TIMEOUT;
+ if (s->rep->flags != flags)
+ resync = 1;
+ }
- *p = '\0';
+ /* reflect what the L7 analysers have seen last */
+ rpf_last = s->rep->flags;
- while (isspace((unsigned char)*line))
- line++;
+ /*
+ * Now forward all shutdown requests between both sides of the buffer
+ */
- arg = 0;
- args[arg] = line;
+ /*
+ * FIXME: this is probably where we should produce error responses.
+ */
- while (*line && arg < MAX_UXST_ARGS) {
- if (isspace((unsigned char)*line)) {
- *line++ = '\0';
+ /* first, let's check if the request buffer needs to shutdown(write) */
+ if (unlikely((s->rep->flags & (BF_SHUTW|BF_SHUTW_NOW|BF_EMPTY|BF_HIJACK|BF_WRITE_ENA|BF_SHUTR)) ==
+ (BF_EMPTY|BF_WRITE_ENA|BF_SHUTR)))
+ buffer_shutw_now(s->rep);
- while (isspace((unsigned char)*line))
- line++;
+ /* shutdown(write) pending */
+ if (unlikely((s->rep->flags & (BF_SHUTW|BF_SHUTW_NOW)) == BF_SHUTW_NOW))
+ s->rep->cons->shutw(s->rep->cons);
- args[++arg] = line;
- continue;
- }
+ /* shutdown(write) done on the client side, we must stop the server too */
+ if (unlikely((s->rep->flags & (BF_SHUTW|BF_SHUTR|BF_SHUTR_NOW)) == BF_SHUTW))
+ buffer_shutr_now(s->rep);
- line++;
- }
+ /* shutdown(read) pending */
+ if (unlikely((s->rep->flags & (BF_SHUTR|BF_SHUTR_NOW)) == BF_SHUTR_NOW))
+ s->rep->prod->shutr(s->rep->prod);
- while (++arg <= MAX_UXST_ARGS)
- args[arg] = line;
+ /*
+ * Here we want to check if we need to resync or not.
+ */
+ if ((s->rep->flags ^ rpf_last) & BF_MASK_STATIC)
+ resync = 1;
- if (!strcmp(args[0], "show")) {
- if (!strcmp(args[1], "stat")) {
- if (*args[2] && *args[3] && *args[4]) {
- s->data_ctx.stats.flags |= STAT_BOUND;
- s->data_ctx.stats.iid = atoi(args[2]);
- s->data_ctx.stats.type = atoi(args[3]);
- s->data_ctx.stats.sid = atoi(args[4]);
- }
+ s->rep->flags &= BF_CLEAR_READ & BF_CLEAR_WRITE & BF_CLEAR_TIMEOUT;
- s->data_ctx.stats.flags |= STAT_SHOW_STAT;
- s->data_ctx.stats.flags |= STAT_FMT_CSV;
- s->srv_state = SV_STDATA;
- fsm_resync |= 1;
- continue;
- }
+ if (resync)
+ goto resync_stream_interface;
- if (!strcmp(args[1], "info")) {
- s->data_ctx.stats.flags |= STAT_SHOW_INFO;
- s->data_ctx.stats.flags |= STAT_FMT_CSV;
- s->srv_state = SV_STDATA;
- fsm_resync |= 1;
- continue;
- }
- }
+ if (likely(s->rep->cons->state != SI_ST_CLO)) {
+ if (s->rep->cons->state == SI_ST_EST)
+ stream_sock_data_finish(s->rep->cons);
- s->srv_state = SV_STCLOSE;
- fsm_resync |= 1;
- continue;
+ s->req->flags &= BF_CLEAR_READ & BF_CLEAR_WRITE & BF_CLEAR_TIMEOUT;
+ s->rep->flags &= BF_CLEAR_READ & BF_CLEAR_WRITE & BF_CLEAR_TIMEOUT;
+ s->si[0].prev_state = s->si[0].state;
+ s->si[0].flags = SI_FL_NONE;
- case SV_STDATA:
- /* OK we have to process the request. Since it is possible
- * that we get there with the client output paused, we
- * will simply check that we have really sent some data
- * and wake the client up if needed.
- */
- last_rep_l = s->rep->l;
- if (stats_dump_raw(s, NULL) != 0) {
- s->srv_state = SV_STCLOSE;
- fsm_resync |= 1;
- }
- if (s->rep->l != last_rep_l)
- fsm_resync |= 1;
- break;
- }
- } while (fsm_resync);
+ /* Trick: if a request is being waiting for the server to respond,
+ * and if we know the server can timeout, we don't want the timeout
+ * to expire on the client side first, but we're still interested
+ * in passing data from the client to the server (eg: POST). Thus,
+ * we can cancel the client's request timeout if the server's
+ * request timeout is set and the server has not yet sent a response.
+ */
- if (likely(s->cli_state != CL_STCLOSE || s->srv_state != SV_STCLOSE)) {
- s->req->flags &= BF_CLEAR_READ & BF_CLEAR_WRITE;
- s->rep->flags &= BF_CLEAR_READ & BF_CLEAR_WRITE;
+ if ((s->rep->flags & (BF_WRITE_ENA|BF_SHUTR)) == 0 &&
+ (tick_isset(s->req->wex) || tick_isset(s->rep->rex)))
+ s->req->rex = TICK_ETERNITY;
t->expire = tick_first(tick_first(s->req->rex, s->req->wex),
tick_first(s->rep->rex, s->rep->wex));
+ if (s->req->analysers)
+ t->expire = tick_first(t->expire, s->req->analyse_exp);
+
+ if (s->si[0].exp)
+ t->expire = tick_first(t->expire, s->si[0].exp);
/* restore t to its place in the task list */
task_queue(t);