[MEDIUM] stream_sock: don't close prematurely when nolinger is set
When the nolinger option is used, we must not close too fast because
some data might be left unsent. Instead we must proceed with a normal
shutdown first, then a close. Also, we want to avoid merging FIN with
the last segment if nolinger is set, because if that one gets lost,
there is no chance for it to be retransmitted.
diff --git a/include/types/fd.h b/include/types/fd.h
index 2bc258f..0c631b1 100644
--- a/include/types/fd.h
+++ b/include/types/fd.h
@@ -65,6 +65,7 @@
#define FD_FL_TCP 0x0001 /* socket is TCP */
#define FD_FL_TCP_NODELAY 0x0002
#define FD_FL_TCP_CORK 0x0004
+#define FD_FL_TCP_NOLING 0x0008 /* lingering disabled */
/* info about one given fd */
struct fdtab {
diff --git a/src/client.c b/src/client.c
index 346adc6..45c576d 100644
--- a/src/client.c
+++ b/src/client.c
@@ -418,6 +418,9 @@
fdtab[cfd].owner = &s->si[0];
fdtab[cfd].state = FD_STREADY;
fdtab[cfd].flags = FD_FL_TCP | FD_FL_TCP_NODELAY;
+ if (p->options & PR_O_TCP_NOLING)
+ fdtab[cfd].flags |= FD_FL_TCP_NOLING;
+
fdtab[cfd].cb[DIR_RD].f = l->proto->read;
fdtab[cfd].cb[DIR_RD].b = s->req;
fdtab[cfd].cb[DIR_WR].f = l->proto->write;
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index adf4e23..4a84186 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -289,6 +289,9 @@
fdtab[fd].owner = listener; /* reference the listener instead of a task */
fdtab[fd].state = FD_STLISTEN;
fdtab[fd].flags = FD_FL_TCP;
+ if (listener->options & LI_O_NOLINGER)
+ fdtab[fd].flags |= FD_FL_TCP_NOLING;
+
fdtab[fd].peeraddr = NULL;
fdtab[fd].peerlen = 0;
tcp_return:
diff --git a/src/stream_sock.c b/src/stream_sock.c
index a3ef269..52305c5 100644
--- a/src/stream_sock.c
+++ b/src/stream_sock.c
@@ -579,7 +579,7 @@
* buffer but we know we will close, so we try to merge the ongoing FIN
* with the last data segment.
*/
- if ((fdtab[si->fd].flags & (FD_FL_TCP|FD_FL_TCP_CORK)) == FD_FL_TCP) {
+ if ((fdtab[si->fd].flags & (FD_FL_TCP|FD_FL_TCP_NOLING|FD_FL_TCP_CORK)) == FD_FL_TCP) {
if (unlikely((b->send_max == b->l &&
(b->flags & (BF_SHUTW|BF_SHUTW_NOW|BF_HIJACK|BF_WRITE_ENA|BF_SHUTR)) ==
(BF_WRITE_ENA|BF_SHUTR)))) {
@@ -829,6 +829,12 @@
shutdown(si->fd, SHUT_WR);
return;
}
+
+ if (fdtab[si->fd].flags & FD_FL_TCP_NOLING) {
+ /* we have to shut before closing if we disable lingering */
+ EV_FD_CLR(si->fd, DIR_WR);
+ shutdown(si->fd, SHUT_WR);
+ }
/* fall through */
case SI_ST_CON:
/* we may have to close a pending connection, and mark the