Skip to content

Commit

Permalink
Improve gateway connection/resume logic (#3099)
Browse files Browse the repository at this point in the history
This commit refactors how the gateway connection being closed gets handled,
and also reworks how resuming is performed. If a resume fails, or if the
session id is invalid/doesn't exist, the shard will fall back to restart
+ reidentify after a 1 second delay. This behavior was only present in
some circumstances before.

Also, cleaned up the loop in `ShardRunner::run` by adding a
`ShardAction::Dispatch` variant, since event dispatch was already
mutually exclusive to hearbeating, identifying, and restarting. The
overall effect is less interleaving of control flow.

Plus, removed the `Shard::{reconnect, reset}` functions as they were
unused.

A notable change is that 4006 is no longer considered a valid close code
as it is undocumented, and neither is 1000, which tungstenite assigns as
`Normal` or "clean". We should stick to the [table of close
codes](https://discord.com/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes)
provided by Discord.
  • Loading branch information
mkrasnitski committed Jan 31, 2025
1 parent 2621db2 commit 872e19c
Show file tree
Hide file tree
Showing 5 changed files with 261 additions and 363 deletions.
88 changes: 39 additions & 49 deletions src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub const USER_AGENT: &str = concat!(
);

enum_number! {
/// An enum representing the [gateway opcodes].
/// An enum representing the gateway opcodes.
///
/// [Discord docs](https://discord.com/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-opcodes).
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
Expand Down Expand Up @@ -70,53 +70,43 @@ enum_number! {
}
}

pub mod close_codes {
/// Unknown error; try reconnecting?
///
/// Can reconnect.
pub const UNKNOWN_ERROR: u16 = 4000;
/// Invalid Gateway OP Code.
///
/// Can resume.
pub const UNKNOWN_OPCODE: u16 = 4001;
/// An invalid payload was sent.
///
/// Can resume.
pub const DECODE_ERROR: u16 = 4002;
/// A payload was sent prior to identifying.
///
/// Cannot reconnect.
pub const NOT_AUTHENTICATED: u16 = 4003;
/// The account token sent with the identify payload was incorrect.
///
/// Cannot reconnect.
pub const AUTHENTICATION_FAILED: u16 = 4004;
/// More than one identify payload was sent.
///
/// Can reconnect.
pub const ALREADY_AUTHENTICATED: u16 = 4005;
/// The sequence sent when resuming the session was invalid.
///
/// Can reconnect.
pub const INVALID_SEQUENCE: u16 = 4007;
/// Payloads were being sent too quickly.
///
/// Can resume.
pub const RATE_LIMITED: u16 = 4008;
/// A session timed out.
///
/// Can reconnect.
pub const SESSION_TIMEOUT: u16 = 4009;
/// An invalid shard when identifying was sent.
///
/// Cannot reconnect.
pub const INVALID_SHARD: u16 = 4010;
/// The session would have handled too many guilds.
enum_number! {
/// An enum representing the gateway close codes.
///
/// Cannot reconnect.
pub const SHARDING_REQUIRED: u16 = 4011;
/// Undocumented gateway intents have been provided.
pub const INVALID_GATEWAY_INTENTS: u16 = 4013;
/// Disallowed gateway intents have been provided.
pub const DISALLOWED_GATEWAY_INTENTS: u16 = 4014;
/// [Discord docs](https://discord.com/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes)
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
#[non_exhaustive]
pub enum CloseCode {
/// Unknown error; try reconnecting.
UnknownError = 4000,
/// Invalid gateway opcode.
UnknownOpcode = 4001,
/// An invalid payload was sent.
DecodeError = 4002,
/// A payload was sent prior to identifying, or the session was invalidated.
NotAuthenticated = 4003,
/// The account token sent with the identify payload was incorrect.
AuthenticationFailed = 4004,
/// More than one identify payload was sent.
AlreadyAuthenticated = 4005,
/// The sequence sent when resuming the session was invalid.
InvalidSequence = 4007,
/// Payloads were being sent too quickly.
RateLimited = 4008,
/// The gateway session timed out, and a new one must be started.
SessionTimeout = 4009,
/// An invalid shard was sent when identifying.
InvalidShard = 4010,
/// The session would have handled too many guilds; you must use sharding to connect.
ShardingRequired = 4011,
/// An invalid gateway API version was sent.
InvalidApiVersion = 4012,
/// An invalid gateway intent was sent.
InvalidGatewayIntents = 4013,
/// A disallowed gateway intent was sent; you may have it disabled or may not be approved
/// to use it.
DisallowedGatewayIntents = 4014,

_ => Unknown(u16),
}
}
3 changes: 3 additions & 0 deletions src/gateway/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub enum Error {
HeartbeatFailed,
/// When invalid authentication (a bad token) was sent in the IDENTIFY.
InvalidAuthentication,
/// When an invalid API version was sent to the gateway.
InvalidApiVersion,
/// Expected a Ready or an InvalidateSession
InvalidHandshake,
/// When invalid sharding data was sent in the IDENTIFY.
Expand Down Expand Up @@ -71,6 +73,7 @@ impl fmt::Display for Error {
Self::ExpectedHello => f.write_str("Expected a Hello"),
Self::HeartbeatFailed => f.write_str("Failed sending a heartbeat"),
Self::InvalidAuthentication => f.write_str("Sent invalid authentication"),
Self::InvalidApiVersion => f.write_str("Sent invalid API version"),
Self::InvalidHandshake => f.write_str("Expected a valid Handshake"),
Self::InvalidShardData => f.write_str("Sent invalid shard data"),
Self::NoAuthentication => f.write_str("Sent no authentication"),
Expand Down
Loading

0 comments on commit 872e19c

Please sign in to comment.