Skip to content

Commit c226e7b

Browse files
committed
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.
1 parent 056502d commit c226e7b

File tree

3 files changed

+21
-0
lines changed

3 files changed

+21
-0
lines changed

include/discord-internal.h

+5
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,11 @@ struct discord_gateway {
749749
* @note obtained at `HELLO`
750750
*/
751751
int64_t hbeat_interval;
752+
/**
753+
* boolean that indicates if the last heartbeat was answered
754+
* @note used to detect zombie connections
755+
*/
756+
bool hbeat_acknowledged;
752757
/**
753758
* Gateway's concept of "now"
754759
* @note updated at discord_gateway_perform()

src/discord-gateway.c

+4
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ _discord_on_heartbeat_ack(struct discord_gateway *gw)
319319
/* get request / response interval in milliseconds */
320320
pthread_rwlock_wrlock(&gw->timer->rwlock);
321321
gw->timer->ping_ms = (int)(gw->timer->now - gw->timer->hbeat_last);
322+
gw->timer->hbeat_acknowledged = true;
322323
pthread_rwlock_unlock(&gw->timer->rwlock);
323324

324325
logconf_trace(&gw->conf, "PING: %d ms", gw->timer->ping_ms);
@@ -551,6 +552,9 @@ discord_gateway_init(struct discord_gateway *gw,
551552
ASSERT_S(!pthread_rwlock_init(&gw->timer->rwlock, NULL),
552553
"Couldn't initialize Gateway's rwlock");
553554

555+
/* mark true to not get reconnected each reconnect */
556+
gw->timer->hbeat_acknowledged = true;
557+
554558
/* client connection status */
555559
gw->session = calloc(1, sizeof *gw->session);
556560
gw->session->retry.enable = true;

src/discord-gateway_dispatch.c

+12
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,16 @@ discord_gateway_send_heartbeat(struct discord_gateway *gw, int seq)
264264
jsonb_object_pop(&b, buf, sizeof(buf));
265265
}
266266

267+
if (!gw->timer->hbeat_acknowledged) {
268+
logconf_warn(&gw->conf, "Heartbeat ACK not received, marked as zombie");
269+
270+
gw->timer->hbeat_acknowledged = true;
271+
272+
discord_gateway_reconnect(gw, false);
273+
274+
return;
275+
}
276+
267277
if (ws_send_text(gw->ws, &info, buf, b.pos)) {
268278
io_poller_curlm_enable_perform(CLIENT(gw, gw)->io_poller, gw->mhandle);
269279
logconf_info(
@@ -273,6 +283,8 @@ discord_gateway_send_heartbeat(struct discord_gateway *gw, int seq)
273283
ANSI_FG_BRIGHT_GREEN) " HEARTBEAT (%d bytes) [@@@_%zu_@@@]",
274284
b.pos, info.loginfo.counter + 1);
275285

286+
gw->timer->hbeat_acknowledged = false;
287+
276288
/* update heartbeat timestamp */
277289
gw->timer->hbeat_last = gw->timer->now;
278290
if (!gw->timer->hbeat_timer)

0 commit comments

Comments
 (0)