|
@@ -131,6 +131,7 @@ func (s *Session) Open() (err error) {
|
|
|
// lock.
|
|
|
s.listening = make(chan interface{})
|
|
|
go s.listen(s.wsConn, s.listening)
|
|
|
+ s.LastHeartbeatAck = time.Now().UTC()
|
|
|
|
|
|
s.Unlock()
|
|
|
|
|
@@ -199,10 +200,13 @@ type helloOp struct {
|
|
|
Trace []string `json:"_trace"`
|
|
|
}
|
|
|
|
|
|
+// Number of heartbeat intervals to wait until forcing a connection restart.
|
|
|
+const FailedHeartbeatAcks time.Duration = 5 * time.Millisecond
|
|
|
+
|
|
|
// heartbeat sends regular heartbeats to Discord so it knows the client
|
|
|
// is still connected. If you do not send these heartbeats Discord will
|
|
|
// disconnect the websocket connection after a few seconds.
|
|
|
-func (s *Session) heartbeat(wsConn *websocket.Conn, listening <-chan interface{}, i time.Duration) {
|
|
|
+func (s *Session) heartbeat(wsConn *websocket.Conn, listening <-chan interface{}, heartbeatIntervalMsec time.Duration) {
|
|
|
|
|
|
s.log(LogInformational, "called")
|
|
|
|
|
@@ -211,20 +215,26 @@ func (s *Session) heartbeat(wsConn *websocket.Conn, listening <-chan interface{}
|
|
|
}
|
|
|
|
|
|
var err error
|
|
|
- ticker := time.NewTicker(i * time.Millisecond)
|
|
|
+ ticker := time.NewTicker(heartbeatIntervalMsec * time.Millisecond)
|
|
|
defer ticker.Stop()
|
|
|
|
|
|
for {
|
|
|
+ s.RLock()
|
|
|
+ last := s.LastHeartbeatAck
|
|
|
+ s.RUnlock()
|
|
|
sequence := atomic.LoadInt64(s.sequence)
|
|
|
s.log(LogInformational, "sending gateway websocket heartbeat seq %d", sequence)
|
|
|
s.wsMutex.Lock()
|
|
|
err = wsConn.WriteJSON(heartbeatOp{1, sequence})
|
|
|
s.wsMutex.Unlock()
|
|
|
- if err != nil {
|
|
|
- s.log(LogError, "error sending heartbeat to gateway %s, %s", s.gateway, err)
|
|
|
- s.Lock()
|
|
|
- s.DataReady = false
|
|
|
- s.Unlock()
|
|
|
+ if err != nil || time.Now().UTC().Sub(last) > (heartbeatIntervalMsec*FailedHeartbeatAcks) {
|
|
|
+ if err != nil {
|
|
|
+ s.log(LogError, "error sending heartbeat to gateway %s, %s", s.gateway, err)
|
|
|
+ } else {
|
|
|
+ s.log(LogError, "haven't gotten a heartbeat ACK in %v, triggering a reconnection", time.Now().UTC().Sub(last))
|
|
|
+ }
|
|
|
+ s.Close()
|
|
|
+ s.reconnect()
|
|
|
return
|
|
|
}
|
|
|
s.Lock()
|
|
@@ -398,7 +408,10 @@ func (s *Session) onEvent(messageType int, message []byte) {
|
|
|
// Reconnect
|
|
|
// Must immediately disconnect from gateway and reconnect to new gateway.
|
|
|
if e.Operation == 7 {
|
|
|
- // TODO
|
|
|
+ s.log(LogInformational, "Closing and reconnecting in response to Op7")
|
|
|
+ s.Close()
|
|
|
+ s.reconnect()
|
|
|
+ return
|
|
|
}
|
|
|
|
|
|
// Invalid Session
|
|
@@ -426,6 +439,14 @@ func (s *Session) onEvent(messageType int, message []byte) {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
+ if e.Operation == 11 {
|
|
|
+ s.Lock()
|
|
|
+ s.LastHeartbeatAck = time.Now().UTC()
|
|
|
+ s.Unlock()
|
|
|
+ s.log(LogInformational, "got heartbeat ACK")
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
// Do not try to Dispatch a non-Dispatch Message
|
|
|
if e.Operation != 0 {
|
|
|
// But we probably should be doing something with them.
|