From 5d707e1aaa4498d6eef9c0fd174240c3a7bdf020 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sun, 28 Jun 2009 11:09:07 +0200 Subject: [PATCH] [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. --- include/types/fd.h | 1 + src/client.c | 3 +++ src/proto_tcp.c | 3 +++ src/stream_sock.c | 8 +++++++- 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/include/types/fd.h b/include/types/fd.h index 2bc258fa71..0c631b1d62 100644 --- a/include/types/fd.h +++ b/include/types/fd.h @@ -65,6 +65,7 @@ enum { #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 346adc6734..45c576dd60 100644 --- a/src/client.c +++ b/src/client.c @@ -418,6 +418,9 @@ int event_accept(int fd) { 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 adf4e230c0..4a841866f6 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -289,6 +289,9 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen) 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 a3ef269926..52305c5e34 100644 --- a/src/stream_sock.c +++ b/src/stream_sock.c @@ -579,7 +579,7 @@ static int stream_sock_write_loop(struct stream_interface *si, struct buffer *b) * 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 @@ void stream_sock_shutw(struct stream_interface *si) 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