[MAJOR] add a connection error state to the stream_interface
Tracking connection status changes was hard, and some code was
redundant. A new SI_ST_CER state was added to the stream interface
to indicate a past connection error, and an SI_FL_ERR flag was
added to report past I/O error. The stream_sock code does not set
the connection to SI_ST_CLO anymore in case of I/O error, it's
the upper layer which does it. This makes it possible to know
exactly when the file descriptors are allocated.
The new SI_ST_CER state permitted to split tcp_connection_status()
in two parts, one processing SI_ST_CON and the other one SI_ST_CER.
Synchronous connection errors now make use of this last state, hence
eliminating duplicate code.
Some ib<->ob copy paste errors were found and fixed, and all entities
setting SI_ST_CLO also shut the buffers down.
Some of these stream_interface specific functions and structures
have migrated to a new stream_interface.c file.
Some types of errors are still not detected by the buffers. For
instance, let's assume the following scenario in one single pass
of process_session: a connection sits in SI_ST_TAR state during
a retry. At TAR expiration, a new connection attempt is made, the
connection is obtained and srv->cur_sess is increased. Then the
buffer timeout is fires and everything is cleared, the new state
becomes SI_ST_CLO. The cleaning code checks that previous state
was either SI_ST_CON or SI_ST_EST to release the connection. But
that's wrong because last state is still SI_ST_TAR. So the
server's connection count does not get decreased.
This means that prev_state must not be used, and must be replaced
by some transition detection instead of level detection.
The following debugging line was useful to track state changes :
fprintf(stderr, "%s:%d: cs=%d ss=%d(%d) rqf=0x%08x rpf=0x%08x\n", __FUNCTION__, __LINE__,
s->si[0].state, s->si[1].state, s->si[1].err_type, s->req->flags, s-> rep->flags);
diff --git a/src/stream_sock.c b/src/stream_sock.c
index 3418e98..4de51ee 100644
--- a/src/stream_sock.c
+++ b/src/stream_sock.c
@@ -259,31 +259,22 @@
goto out_wakeup;
out_error:
- /* There was an error. we must wakeup the task. No need to clear
- * the events, the task will do it.
+ /* Read error on the file descriptor. We mark the FD as STERROR so
+ * that we don't use it anymore. The error is reported to the stream
+ * interface which will take proper action. We must not perturbate the
+ * buffer because the stream interface wants to ensure transparent
+ * connection retries.
*/
+
fdtab[fd].state = FD_STERROR;
fdtab[fd].ev &= ~FD_POLL_STICKY;
- b->rex = TICK_ETERNITY;
-
- /* Read error on the file descriptor. We close the FD and set
- * the error on both buffers.
- * Note: right now we only support connected sockets.
- */
- if (si->state != SI_ST_EST)
- goto out_wakeup;
-
- if (!si->err_type)
- si->err_type = SI_ET_DATA_ERR;
-
- buffer_shutr(b);
- b->flags |= BF_READ_ERROR;
- buffer_shutw(si->ob);
- si->ob->flags |= BF_WRITE_ERROR;
+ si->flags |= SI_FL_ERR;
+ goto wakeup_return;
do_close_and_return:
- fd_delete(fd);
si->state = SI_ST_CLO;
+ fd_delete(fd);
+ wakeup_return:
task_wakeup(si->owner, TASK_WOKEN_IO);
return 1;
}
@@ -457,29 +448,22 @@
return retval;
out_error:
- /* There was an error. we must wakeup the task. No need to clear
- * the events, the task will do it.
+ /* Write error on the file descriptor. We mark the FD as STERROR so
+ * that we don't use it anymore. The error is reported to the stream
+ * interface which will take proper action. We must not perturbate the
+ * buffer because the stream interface wants to ensure transparent
+ * connection retries.
*/
+
fdtab[fd].state = FD_STERROR;
fdtab[fd].ev &= ~FD_POLL_STICKY;
- b->wex = TICK_ETERNITY;
- /* Read error on the file descriptor. We close the FD and set
- * the error on both buffers.
- * Note: right now we only support connected sockets.
- */
- if (si->state != SI_ST_EST)
- goto out_wakeup;
-
- if (!si->err_type)
- si->err_type = SI_ET_DATA_ERR;
+ si->flags |= SI_FL_ERR;
+ goto wakeup_return;
- buffer_shutw(b);
- b->flags |= BF_WRITE_ERROR;
- buffer_shutr(si->ib);
- si->ib->flags |= BF_READ_ERROR;
do_close_and_return:
- fd_delete(fd);
si->state = SI_ST_CLO;
+ fd_delete(fd);
+ wakeup_return:
task_wakeup(si->owner, TASK_WOKEN_IO);
return 1;
}
@@ -524,7 +508,7 @@
if (si->state != SI_ST_EST && si->state != SI_ST_CON)
return 0;
- if (si->ib->flags & BF_SHUTW) {
+ if (si->ob->flags & BF_SHUTW) {
fd_delete(si->fd);
si->state = SI_ST_CLO;
return 1;
@@ -534,22 +518,6 @@
}
/*
- * This function only has to be called once after a wakeup event in case of
- * suspected timeout. It controls the stream interface timeouts and sets
- * si->flags accordingly. It does NOT close anything, as this timeout may
- * be used for any purpose. It returns 1 if the timeout fired, otherwise
- * zero.
- */
-int stream_sock_check_timeouts(struct stream_interface *si)
-{
- if (tick_is_expired(si->exp, now_ms)) {
- si->flags |= SI_FL_EXP;
- return 1;
- }
- return 0;
-}
-
-/*
* Manages a stream_sock connection during its data phase. The buffers are
* examined for various cases of shutdown, then file descriptor and buffers'
* flags are updated accordingly.