From bc83587cae0aebc5f3720aa2e59af7e44e53fc8c Mon Sep 17 00:00:00 2001 From: ThePedroo Date: Tue, 27 Feb 2024 18:10:06 -0300 Subject: [PATCH] feat: zombie connection check This commit adds the check that verifies if the connection is dead, AKA zombie. Without this, once internet was unavailable for a certain time, Discord would stop responding to the heartbeats and wouldn't send any events, causing to the bot be offline, while the process would be still alive. --- include/discord-internal.h | 5 +++++ src/discord-gateway.c | 4 ++++ src/discord-gateway_dispatch.c | 12 ++++++++++++ 3 files changed, 21 insertions(+) diff --git a/include/discord-internal.h b/include/discord-internal.h index 17eb2a59..7f37fb8b 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -749,6 +749,11 @@ struct discord_gateway { * @note obtained at `HELLO` */ int64_t hbeat_interval; + /** + * boolean that indicates if the last heartbeat was answered + * @note used to detect zombie connections + */ + bool hbeat_acknowledged; /** * Gateway's concept of "now" * @note updated at discord_gateway_perform() diff --git a/src/discord-gateway.c b/src/discord-gateway.c index dd272c83..c2b47174 100644 --- a/src/discord-gateway.c +++ b/src/discord-gateway.c @@ -319,6 +319,7 @@ _discord_on_heartbeat_ack(struct discord_gateway *gw) /* get request / response interval in milliseconds */ pthread_rwlock_wrlock(&gw->timer->rwlock); gw->timer->ping_ms = (int)(gw->timer->now - gw->timer->hbeat_last); + gw->timer->hbeat_acknowledged = true; pthread_rwlock_unlock(&gw->timer->rwlock); logconf_trace(&gw->conf, "PING: %d ms", gw->timer->ping_ms); @@ -551,6 +552,9 @@ discord_gateway_init(struct discord_gateway *gw, ASSERT_S(!pthread_rwlock_init(&gw->timer->rwlock, NULL), "Couldn't initialize Gateway's rwlock"); + /* mark true to not get reconnected each reconnect */ + gw->timer->hbeat_acknowledged = true; + /* client connection status */ gw->session = calloc(1, sizeof *gw->session); gw->session->retry.enable = true; diff --git a/src/discord-gateway_dispatch.c b/src/discord-gateway_dispatch.c index b8df9e29..0bddeb45 100644 --- a/src/discord-gateway_dispatch.c +++ b/src/discord-gateway_dispatch.c @@ -264,6 +264,16 @@ discord_gateway_send_heartbeat(struct discord_gateway *gw, int seq) jsonb_object_pop(&b, buf, sizeof(buf)); } + if (!gw->timer->hbeat_acknowledged) { + logconf_warn(&gw->conf, "Heartbeat ACK not received, marked as zombie"); + + gw->timer->hbeat_acknowledged = true; + + discord_gateway_reconnect(gw, false); + + return; + } + if (ws_send_text(gw->ws, &info, buf, b.pos)) { io_poller_curlm_enable_perform(CLIENT(gw, gw)->io_poller, gw->mhandle); logconf_info( @@ -273,6 +283,8 @@ discord_gateway_send_heartbeat(struct discord_gateway *gw, int seq) ANSI_FG_BRIGHT_GREEN) " HEARTBEAT (%d bytes) [@@@_%zu_@@@]", b.pos, info.loginfo.counter + 1); + gw->timer->hbeat_acknowledged = false; + /* update heartbeat timestamp */ gw->timer->hbeat_last = gw->timer->now; if (!gw->timer->hbeat_timer)