BUG/MINOR: session: ensure that we don't retry connection if some data were sent
With extra-large buffers, it is possible that a lot of data are sent upon
connection establishment before the session is notified. The issue is how
to handle a send() error after some data were actually sent.
At the moment, only a connection error is reported, causing a new connection
attempt and send() to restart after the last data. We absolutely don't want
to retry the connect() if at least one byte was sent, because those data are
lost.
The solution consists in reporting exactly what happens, which is :
- a successful connection attempt
- a read/write error on the channel
That way we go on with sess_establish(), the response analysers are called
and report the appropriate connection state for the error (typically a server
abort while waiting for a response). This mechanism also guarantees that we
won't retry since it's a success. The logs also report the correct connect
time.
Note that 1.4 is not directly affected because it only attempts one send(),
so it cannot detect a send() failure here and distinguish it form a failed
connection attempt. So no backport is needed. Also, this is just a safe belt
we're taking, since this issue should not happen anymore since previous commit.
diff --git a/src/session.c b/src/session.c
index 840ad29..9d72703 100644
--- a/src/session.c
+++ b/src/session.c
@@ -762,6 +762,19 @@
* attempts and error reports.
*/
if (unlikely(si->flags & (SI_FL_EXP|SI_FL_ERR))) {
+ if (unlikely(si->ob->flags & CF_WRITE_PARTIAL)) {
+ /* Some data were sent past the connection establishment,
+ * so we need to pretend we're established to log correctly
+ * and let later states handle the failure.
+ */
+ s->logs.t_connect = tv_ms_elapsed(&s->logs.tv_accept, &now);
+ si->exp = TICK_ETERNITY;
+ si->state = SI_ST_EST;
+ si->err_type = SI_ET_DATA_ERR;
+ si->ib->flags |= CF_READ_ERROR | CF_WRITE_ERROR;
+ si->err_loc = target_srv(&s->target);
+ return 1;
+ }
si->exp = TICK_ETERNITY;
si->state = SI_ST_CER;
fd_delete(si_fd(si));