From 971c162d33164d21b9881fa3f3882f904cd8d30c Mon Sep 17 00:00:00 2001 From: "diamondburned (Forefront)" Date: Fri, 17 Jan 2020 23:40:44 -0800 Subject: [PATCH] Fixed bug where reconnect would fail --- gateway/gateway.go | 10 ++++++++-- gateway/integration_test.go | 14 ++++++-------- gateway/op.go | 5 +++++ gateway/pacemaker.go | 8 +++++++- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/gateway/gateway.go b/gateway/gateway.go index 42f6cc6..bb841b3 100644 --- a/gateway/gateway.go +++ b/gateway/gateway.go @@ -150,7 +150,10 @@ func (g *Gateway) Close() error { g.Pacemaker.Stop() // Stop the event handler - defer close(g.handler) + if g.handler != nil { + close(g.handler) + g.handler = nil + } // Stop the Websocket return g.WS.Close(nil) @@ -190,7 +193,10 @@ func (g *Gateway) Open() error { // If the connection is rate limited (documented behavior): // https://discordapp.com/developers/docs/topics/gateway#rate-limiting if err == ErrInvalidSession { - continue // retry + // Close the connection + g.Close() + + continue // then retry } // Else, fatal diff --git a/gateway/integration_test.go b/gateway/integration_test.go index e814c25..d4973f2 100644 --- a/gateway/integration_test.go +++ b/gateway/integration_test.go @@ -32,9 +32,10 @@ func TestIntegration(t *testing.T) { t.Fatal("Failed to authenticate with Discord:", err) } - ready, ok := wait(t, gateway.Events).(*ReadyEvent) + ev := wait(t, gateway.Events) + ready, ok := ev.(*ReadyEvent) if !ok { - t.Fatal("Event received is not of type Ready:", ready) + t.Fatal("Event received is not of type Ready:", ev) } if gateway.SessionID == "" { @@ -48,18 +49,15 @@ func TestIntegration(t *testing.T) { t.Fatal("Failed to reconnect:", err) } - /* TODO: this isn't true, as Discord would keep sending Invalid Sessions. + /* TODO: We're not testing this, as Discord will replay events before it + * sends the Resumed event. + resumed, ok := (<-gateway.Events).(*ResumedEvent) if !ok { t.Fatal("Event received is not of type Resumed:", resumed) } */ - ready, ok = wait(t, gateway.Events).(*ReadyEvent) - if !ok { - t.Fatal("Event received is not of type Ready:", ready) - } - if err := g.Close(); err != nil { t.Fatal("Failed to close Gateway:", err) } diff --git a/gateway/op.go b/gateway/op.go index 61e3198..89688c5 100644 --- a/gateway/op.go +++ b/gateway/op.go @@ -2,6 +2,8 @@ package gateway import ( "fmt" + "math/rand" + "time" "github.com/diamondburned/arikawa/internal/json" "github.com/diamondburned/arikawa/internal/wsutil" @@ -122,6 +124,9 @@ func HandleOP(g *Gateway, op *OP) error { return g.Reconnect() case InvalidSessionOP: + // Discord expects us to sleep for no reason + time.Sleep(time.Duration(rand.Intn(5)+1) * time.Second) + // Invalid session, respond with Identify. return g.Identify() diff --git a/gateway/pacemaker.go b/gateway/pacemaker.go index c8607e4..ee8d05e 100644 --- a/gateway/pacemaker.go +++ b/gateway/pacemaker.go @@ -39,7 +39,10 @@ func (p *Pacemaker) Dead() bool { } func (p *Pacemaker) Stop() { - close(p.stop) + if p.stop != nil { + close(p.stop) + p.stop = nil + } } // Start beats until it's dead. @@ -57,10 +60,13 @@ func (p *Pacemaker) start(stop chan struct{}) error { // Echo at least once p.Echo() + // TODO: what happens if the heartbeat fails? + for { select { case <-stop: return nil + case <-tick.C: if err := p.Pace(); err != nil { return err