diff --git a/include/types/stream_interface.h b/include/types/stream_interface.h index fb89f50d4..dde7b3fe5 100644 --- a/include/types/stream_interface.h +++ b/include/types/stream_interface.h @@ -72,6 +72,7 @@ enum { SI_FL_DONT_WAKE = 0x0020, /* resync in progress, don't wake up */ SI_FL_INDEP_STR = 0x0040, /* independant streams = don't update rex on write */ SI_FL_NOLINGER = 0x0080, /* may close without lingering. One-shot. */ + SI_FL_NOHALF = 0x0100, /* no half close, close both sides at once */ SI_FL_SRC_ADDR = 0x1000, /* get the source ip/port with getsockname */ SI_FL_TO_SET = 0x2000, /* addr.to is set */ SI_FL_FROM_SET = 0x4000, /* addr.from is set */ diff --git a/src/proto_http.c b/src/proto_http.c index 18db42f74..f18ae724d 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -3424,6 +3424,11 @@ int http_process_request(struct session *s, struct buffer *req, int an_bit) req->analyse_exp = TICK_ETERNITY; req->analysers &= ~an_bit; + /* if the server closes the connection, we want to immediately react + * and close the socket to save packets and syscalls. + */ + req->cons->flags |= SI_FL_NOHALF; + s->logs.tv_request = now; /* OK let's go on with the BODY now */ return 1; @@ -3670,7 +3675,7 @@ void http_end_txn_clean_session(struct session *s) */ http_silent_debug(__LINE__, s); - s->req->cons->flags |= SI_FL_NOLINGER; + s->req->cons->flags |= SI_FL_NOLINGER | SI_FL_NOHALF; s->req->cons->sock.shutr(s->req->cons); s->req->cons->sock.shutw(s->req->cons); diff --git a/src/session.c b/src/session.c index 036480c83..a3becfebd 100644 --- a/src/session.c +++ b/src/session.c @@ -1359,8 +1359,11 @@ struct task *process_session(struct task *t) s->req->cons->sock.shutw(s->req->cons); } - if (unlikely((s->req->flags & (BF_SHUTR|BF_READ_TIMEOUT)) == BF_READ_TIMEOUT)) + if (unlikely((s->req->flags & (BF_SHUTR|BF_READ_TIMEOUT)) == BF_READ_TIMEOUT)) { + if (s->req->prod->flags & SI_FL_NOHALF) + s->req->prod->flags |= SI_FL_NOLINGER; s->req->prod->sock.shutr(s->req->prod); + } buffer_check_timeouts(s->rep); @@ -1369,8 +1372,11 @@ struct task *process_session(struct task *t) s->rep->cons->sock.shutw(s->rep->cons); } - if (unlikely((s->rep->flags & (BF_SHUTR|BF_READ_TIMEOUT)) == BF_READ_TIMEOUT)) + if (unlikely((s->rep->flags & (BF_SHUTR|BF_READ_TIMEOUT)) == BF_READ_TIMEOUT)) { + if (s->rep->prod->flags & SI_FL_NOHALF) + s->rep->prod->flags |= SI_FL_NOLINGER; s->rep->prod->sock.shutr(s->rep->prod); + } } /* 1b: check for low-level errors reported at the stream interface. @@ -1907,8 +1913,11 @@ struct task *process_session(struct task *t) buffer_shutr_now(s->req); /* shutdown(read) pending */ - if (unlikely((s->req->flags & (BF_SHUTR|BF_SHUTR_NOW)) == BF_SHUTR_NOW)) + if (unlikely((s->req->flags & (BF_SHUTR|BF_SHUTR_NOW)) == BF_SHUTR_NOW)) { + if (s->req->prod->flags & SI_FL_NOHALF) + s->req->prod->flags |= SI_FL_NOLINGER; s->req->prod->sock.shutr(s->req->prod); + } /* it's possible that an upper layer has requested a connection setup or abort. * There are 2 situations where we decide to establish a new connection : @@ -2049,8 +2058,11 @@ struct task *process_session(struct task *t) buffer_shutr_now(s->rep); /* shutdown(read) pending */ - if (unlikely((s->rep->flags & (BF_SHUTR|BF_SHUTR_NOW)) == BF_SHUTR_NOW)) + if (unlikely((s->rep->flags & (BF_SHUTR|BF_SHUTR_NOW)) == BF_SHUTR_NOW)) { + if (s->rep->prod->flags & SI_FL_NOHALF) + s->rep->prod->flags |= SI_FL_NOLINGER; s->rep->prod->sock.shutr(s->rep->prod); + } if (s->req->prod->state == SI_ST_DIS || s->req->cons->state == SI_ST_DIS) goto resync_stream_interface; diff --git a/src/sock_raw.c b/src/sock_raw.c index 7fba926f0..b7edbfc97 100644 --- a/src/sock_raw.c +++ b/src/sock_raw.c @@ -819,11 +819,13 @@ static void sock_raw_shutw(struct stream_interface *si) if (si->flags & SI_FL_ERR) { /* quick close, the socket is already shut. Remove pending flags. */ si->flags &= ~SI_FL_NOLINGER; - } else if (si->flags & SI_FL_NOLINGER) { + } + else if (si->flags & SI_FL_NOLINGER) { si->flags &= ~SI_FL_NOLINGER; setsockopt(si->fd, SOL_SOCKET, SO_LINGER, (struct linger *) &nolinger, sizeof(struct linger)); - } else { + } + else if (!(si->flags & SI_FL_NOHALF)) { EV_FD_CLR(si->fd, DIR_WR); shutdown(si->fd, SHUT_WR); @@ -857,7 +859,8 @@ static void sock_raw_shutw(struct stream_interface *si) * This function performs a shutdown-read on a stream interface in a connected or * init state (it does nothing for other states). It either shuts the read side * or closes the file descriptor and marks itself as closed. The buffer flags are - * updated to reflect the new state. + * updated to reflect the new state. If the stream interface has SI_FL_NOHALF, + * we also forward the close to the write side. */ static void sock_raw_shutr(struct stream_interface *si) { @@ -880,6 +883,10 @@ static void sock_raw_shutr(struct stream_interface *si) si->release(si); return; } + else if (si->flags & SI_FL_NOHALF) { + /* we want to immediately forward this close to the write side */ + return sock_raw_shutw(si); + } EV_FD_CLR(si->fd, DIR_RD); return; }