Support ShouldReconnectOnError with exponential backoff up to 10 minutes.
This commit is contained in:
parent
94b087cd49
commit
e94e14e9d2
2 changed files with 72 additions and 47 deletions
|
@ -92,6 +92,8 @@ type Session struct {
|
||||||
|
|
||||||
// When nil, the session is not listening.
|
// When nil, the session is not listening.
|
||||||
listening chan interface{}
|
listening chan interface{}
|
||||||
|
|
||||||
|
ShouldReconnectOnError bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// A VoiceRegion stores data for a specific voice region server.
|
// A VoiceRegion stores data for a specific voice region server.
|
||||||
|
|
111
wsapi.go
111
wsapi.go
|
@ -116,9 +116,32 @@ func (s *Session) listen(wsConn *websocket.Conn, listening <-chan interface{}) {
|
||||||
for {
|
for {
|
||||||
messageType, message, err := wsConn.ReadMessage()
|
messageType, message, err := wsConn.ReadMessage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// Detect if we have been closed manually. If a Close() has already
|
||||||
|
// happened, the websocket we are listening on will be different to the
|
||||||
|
// current session.
|
||||||
|
s.RLock()
|
||||||
|
sameConnection := s.wsConn == wsConn
|
||||||
|
s.RUnlock()
|
||||||
|
if sameConnection {
|
||||||
// There has been an error reading, Close() the websocket so that
|
// There has been an error reading, Close() the websocket so that
|
||||||
// OnDisconnect is fired.
|
// OnDisconnect is fired.
|
||||||
s.Close()
|
s.Close()
|
||||||
|
|
||||||
|
// Attempt to reconnect, with expenonential backoff up to 10 minutes.
|
||||||
|
if s.ShouldReconnectOnError {
|
||||||
|
wait := time.Duration(1)
|
||||||
|
for {
|
||||||
|
if s.Open() == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
<-time.After(wait * time.Second)
|
||||||
|
wait *= 2
|
||||||
|
if wait > 600 {
|
||||||
|
wait = 600
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,6 +156,50 @@ func (s *Session) listen(wsConn *websocket.Conn, listening <-chan interface{}) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type heartbeatOp struct {
|
||||||
|
Op int `json:"op"`
|
||||||
|
Data int `json:"d"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Session) sendHeartbeat(wsConn *websocket.Conn) error {
|
||||||
|
return wsConn.WriteJSON(heartbeatOp{1, int(time.Now().Unix())})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
if listening == nil || wsConn == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
|
s.DataReady = true
|
||||||
|
s.Unlock()
|
||||||
|
|
||||||
|
// Send first heartbeat immediately because lag could put the
|
||||||
|
// first heartbeat outside the required heartbeat interval window.
|
||||||
|
err := s.sendHeartbeat(wsConn)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error sending initial heartbeat:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ticker := time.NewTicker(i * time.Millisecond)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
err := s.sendHeartbeat(wsConn)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error sending heartbeat:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case <-listening:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type updateStatusGame struct {
|
type updateStatusGame struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
@ -549,50 +616,6 @@ func (s *Session) event(messageType int, message []byte) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type heartbeatOp struct {
|
|
||||||
Op int `json:"op"`
|
|
||||||
Data int `json:"d"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Session) sendHeartbeat(wsConn *websocket.Conn) error {
|
|
||||||
return wsConn.WriteJSON(heartbeatOp{1, int(time.Now().Unix())})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
if listening == nil || wsConn == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
s.Lock()
|
|
||||||
s.DataReady = true
|
|
||||||
s.Unlock()
|
|
||||||
|
|
||||||
// Send first heartbeat immediately because lag could put the
|
|
||||||
// first heartbeat outside the required heartbeat interval window.
|
|
||||||
err := s.sendHeartbeat(wsConn)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Error sending initial heartbeat:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ticker := time.NewTicker(i * time.Millisecond)
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ticker.C:
|
|
||||||
err := s.sendHeartbeat(wsConn)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Error sending heartbeat:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case <-listening:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Code related to voice connections that initiate over the data websocket
|
// Code related to voice connections that initiate over the data websocket
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
Loading…
Reference in a new issue