From fc0e1ee311e2bd1d0d2f3e00bae39310029bd985 Mon Sep 17 00:00:00 2001 From: Sergey Lyubka Date: Tue, 14 Jan 2025 14:44:38 +0000 Subject: [PATCH 1/2] Close connection fast if both sides sent FIN --- mongoose.c | 16 +++++++++++++--- src/net_builtin.c | 16 +++++++++++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/mongoose.c b/mongoose.c index 4e89210a40..5ed433341b 100644 --- a/mongoose.c +++ b/mongoose.c @@ -4108,6 +4108,8 @@ struct connstate { #define MIP_TTYPE_FIN 4 // FIN sent, waiting until terminating the connection uint8_t tmiss; // Number of keep-alive misses struct mg_iobuf raw; // For TLS only. Incoming raw data + bool fin_sent; // We have sent FIN to the peer + bool fin_rcvd; // We have received FIN from the peer }; #pragma pack(push, 1) @@ -4722,7 +4724,7 @@ long mg_io_send(struct mg_connection *c, const void *buf, size_t len) { } static void handle_tls_recv(struct mg_connection *c) { - size_t avail = mg_tls_pending(c); + size_t avail = mg_tls_pending(c); size_t min = avail > MG_MAX_RECV_SIZE ? MG_MAX_RECV_SIZE : avail; struct mg_iobuf *io = &c->recv; if (io->size - io->len < min && !mg_iobuf_resize(io, io->len + min)) { @@ -4736,7 +4738,7 @@ static void handle_tls_recv(struct mg_connection *c) { // Decrypted successfully - trigger MG_EV_READ io->len += (size_t) n; mg_call(c, MG_EV_READ, &n); - } // else n < 0: outstanding data to be moved to c->recv + } // else n < 0: outstanding data to be moved to c->recv } } @@ -4752,21 +4754,28 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) { // closure process uint8_t flags = TH_ACK; s->ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len + 1); + s->fin_rcvd = true; if (c->is_draining && s->ttype == MIP_TTYPE_FIN) { if (s->seq == mg_htonl(pkt->tcp->ack)) { // Simultaneous closure ? s->seq++; // Yes. Increment our SEQ } else { // Otherwise, s->seq = mg_htonl(pkt->tcp->ack); // Set to peer's ACK } + if (s->fin_sent) c->is_closing = 1; // Both sides closed, we're done } else { flags |= TH_FIN; c->is_draining = 1; + s->fin_sent = true; settmout(c, MIP_TTYPE_FIN); } tx_tcp(c->mgr->ifp, s->mac, rem_ip, flags, c->loc.port, c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), "", 0); } else if (pkt->pay.len == 0) { - // TODO(cpq): handle this peer's ACK + // If we receive a final ACK in TCP closure, close immediately, + // don't wait until MIP_TTYPE_FIN timeout expires + if (s->fin_rcvd && s->fin_sent) { + c->is_closing = true; + } } else if (seq != s->ack) { uint32_t ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len); if (s->ack == ack) { @@ -5213,6 +5222,7 @@ static void init_closure(struct mg_connection *c) { tx_tcp(c->mgr->ifp, s->mac, rem_ip, TH_FIN | TH_ACK, c->loc.port, c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); settmout(c, MIP_TTYPE_FIN); + s->fin_sent = true; } } diff --git a/src/net_builtin.c b/src/net_builtin.c index 57b4017675..94201a830f 100644 --- a/src/net_builtin.c +++ b/src/net_builtin.c @@ -28,6 +28,8 @@ struct connstate { #define MIP_TTYPE_FIN 4 // FIN sent, waiting until terminating the connection uint8_t tmiss; // Number of keep-alive misses struct mg_iobuf raw; // For TLS only. Incoming raw data + bool fin_sent; // We have sent FIN to the peer + bool fin_rcvd; // We have received FIN from the peer }; #pragma pack(push, 1) @@ -642,7 +644,7 @@ long mg_io_send(struct mg_connection *c, const void *buf, size_t len) { } static void handle_tls_recv(struct mg_connection *c) { - size_t avail = mg_tls_pending(c); + size_t avail = mg_tls_pending(c); size_t min = avail > MG_MAX_RECV_SIZE ? MG_MAX_RECV_SIZE : avail; struct mg_iobuf *io = &c->recv; if (io->size - io->len < min && !mg_iobuf_resize(io, io->len + min)) { @@ -656,7 +658,7 @@ static void handle_tls_recv(struct mg_connection *c) { // Decrypted successfully - trigger MG_EV_READ io->len += (size_t) n; mg_call(c, MG_EV_READ, &n); - } // else n < 0: outstanding data to be moved to c->recv + } // else n < 0: outstanding data to be moved to c->recv } } @@ -672,21 +674,28 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) { // closure process uint8_t flags = TH_ACK; s->ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len + 1); + s->fin_rcvd = true; if (c->is_draining && s->ttype == MIP_TTYPE_FIN) { if (s->seq == mg_htonl(pkt->tcp->ack)) { // Simultaneous closure ? s->seq++; // Yes. Increment our SEQ } else { // Otherwise, s->seq = mg_htonl(pkt->tcp->ack); // Set to peer's ACK } + if (s->fin_sent) c->is_closing = 1; // Both sides closed, we're done } else { flags |= TH_FIN; c->is_draining = 1; + s->fin_sent = true; settmout(c, MIP_TTYPE_FIN); } tx_tcp(c->mgr->ifp, s->mac, rem_ip, flags, c->loc.port, c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), "", 0); } else if (pkt->pay.len == 0) { - // TODO(cpq): handle this peer's ACK + // If we receive a final ACK in TCP closure, close immediately, + // don't wait until MIP_TTYPE_FIN timeout expires + if (s->fin_rcvd && s->fin_sent) { + c->is_closing = true; + } } else if (seq != s->ack) { uint32_t ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len); if (s->ack == ack) { @@ -1133,6 +1142,7 @@ static void init_closure(struct mg_connection *c) { tx_tcp(c->mgr->ifp, s->mac, rem_ip, TH_FIN | TH_ACK, c->loc.port, c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); settmout(c, MIP_TTYPE_FIN); + s->fin_sent = true; } } From 091356ed59658319363f974c733531ace90527b9 Mon Sep 17 00:00:00 2001 From: "Sergio R. Caprile" Date: Fri, 17 Jan 2025 19:51:12 -0300 Subject: [PATCH 2/2] give TLS a chance --- mongoose.c | 22 +++++++++++----------- src/net_builtin.c | 22 +++++++++++----------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/mongoose.c b/mongoose.c index 5ed433341b..6d674991a7 100644 --- a/mongoose.c +++ b/mongoose.c @@ -4108,8 +4108,8 @@ struct connstate { #define MIP_TTYPE_FIN 4 // FIN sent, waiting until terminating the connection uint8_t tmiss; // Number of keep-alive misses struct mg_iobuf raw; // For TLS only. Incoming raw data - bool fin_sent; // We have sent FIN to the peer bool fin_rcvd; // We have received FIN from the peer + bool twclosure; // 3-way closure done }; #pragma pack(push, 1) @@ -4761,21 +4761,17 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) { } else { // Otherwise, s->seq = mg_htonl(pkt->tcp->ack); // Set to peer's ACK } - if (s->fin_sent) c->is_closing = 1; // Both sides closed, we're done + s->twclosure = true; } else { flags |= TH_FIN; c->is_draining = 1; - s->fin_sent = true; settmout(c, MIP_TTYPE_FIN); } tx_tcp(c->mgr->ifp, s->mac, rem_ip, flags, c->loc.port, c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), "", 0); - } else if (pkt->pay.len == 0) { - // If we receive a final ACK in TCP closure, close immediately, - // don't wait until MIP_TTYPE_FIN timeout expires - if (s->fin_rcvd && s->fin_sent) { - c->is_closing = true; - } + } else if (pkt->pay.len == 0) { // this is an ACK + if (s->fin_rcvd && s->ttype == MIP_TTYPE_FIN) + s->twclosure = true; } else if (seq != s->ack) { uint32_t ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len); if (s->ack == ack) { @@ -5061,7 +5057,7 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { struct connstate *s = (struct connstate *) (c + 1); uint32_t rem_ip; memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); - if (now > s->timer) { + if (ifp->now > s->timer) { if (s->ttype == MIP_TTYPE_ARP) { mg_error(c, "ARP timeout"); } else if (c->is_udp) { @@ -5222,7 +5218,6 @@ static void init_closure(struct mg_connection *c) { tx_tcp(c->mgr->ifp, s->mac, rem_ip, TH_FIN | TH_ACK, c->loc.port, c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); settmout(c, MIP_TTYPE_FIN); - s->fin_sent = true; } } @@ -5250,10 +5245,15 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) { MG_VERBOSE(("%lu .. %c%c%c%c%c", c->id, c->is_tls ? 'T' : 't', c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h', c->is_resolving ? 'R' : 'r', c->is_closing ? 'C' : 'c')); + // order is important, TLS conn close with > 1 record in buffer if (c->is_tls && mg_tls_pending(c) > 0) handle_tls_recv(c); if (can_write(c)) write_conn(c); if (c->is_draining && c->send.len == 0 && s->ttype != MIP_TTYPE_FIN) init_closure(c); + // For non-TLS, close immediately upon completing the 3-way closure + // For TLS, process any pending data until MIP_TTYPE_FIN timeout expires + if (s->twclosure && (!c->is_tls || mg_tls_pending(c) == 0)) + c->is_closing = 1; if (c->is_closing) close_conn(c); } (void) ms; diff --git a/src/net_builtin.c b/src/net_builtin.c index 94201a830f..9e85f2c961 100644 --- a/src/net_builtin.c +++ b/src/net_builtin.c @@ -28,8 +28,8 @@ struct connstate { #define MIP_TTYPE_FIN 4 // FIN sent, waiting until terminating the connection uint8_t tmiss; // Number of keep-alive misses struct mg_iobuf raw; // For TLS only. Incoming raw data - bool fin_sent; // We have sent FIN to the peer bool fin_rcvd; // We have received FIN from the peer + bool twclosure; // 3-way closure done }; #pragma pack(push, 1) @@ -681,21 +681,17 @@ static void read_conn(struct mg_connection *c, struct pkt *pkt) { } else { // Otherwise, s->seq = mg_htonl(pkt->tcp->ack); // Set to peer's ACK } - if (s->fin_sent) c->is_closing = 1; // Both sides closed, we're done + s->twclosure = true; } else { flags |= TH_FIN; c->is_draining = 1; - s->fin_sent = true; settmout(c, MIP_TTYPE_FIN); } tx_tcp(c->mgr->ifp, s->mac, rem_ip, flags, c->loc.port, c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), "", 0); - } else if (pkt->pay.len == 0) { - // If we receive a final ACK in TCP closure, close immediately, - // don't wait until MIP_TTYPE_FIN timeout expires - if (s->fin_rcvd && s->fin_sent) { - c->is_closing = true; - } + } else if (pkt->pay.len == 0) { // this is an ACK + if (s->fin_rcvd && s->ttype == MIP_TTYPE_FIN) + s->twclosure = true; } else if (seq != s->ack) { uint32_t ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len); if (s->ack == ack) { @@ -981,7 +977,7 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { struct connstate *s = (struct connstate *) (c + 1); uint32_t rem_ip; memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); - if (now > s->timer) { + if (ifp->now > s->timer) { if (s->ttype == MIP_TTYPE_ARP) { mg_error(c, "ARP timeout"); } else if (c->is_udp) { @@ -1142,7 +1138,6 @@ static void init_closure(struct mg_connection *c) { tx_tcp(c->mgr->ifp, s->mac, rem_ip, TH_FIN | TH_ACK, c->loc.port, c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); settmout(c, MIP_TTYPE_FIN); - s->fin_sent = true; } } @@ -1170,10 +1165,15 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) { MG_VERBOSE(("%lu .. %c%c%c%c%c", c->id, c->is_tls ? 'T' : 't', c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h', c->is_resolving ? 'R' : 'r', c->is_closing ? 'C' : 'c')); + // order is important, TLS conn close with > 1 record in buffer if (c->is_tls && mg_tls_pending(c) > 0) handle_tls_recv(c); if (can_write(c)) write_conn(c); if (c->is_draining && c->send.len == 0 && s->ttype != MIP_TTYPE_FIN) init_closure(c); + // For non-TLS, close immediately upon completing the 3-way closure + // For TLS, process any pending data until MIP_TTYPE_FIN timeout expires + if (s->twclosure && (!c->is_tls || mg_tls_pending(c) == 0)) + c->is_closing = 1; if (c->is_closing) close_conn(c); } (void) ms;