Skip to content

Commit

Permalink
Merge pull request #94 from iopred/login
Browse files Browse the repository at this point in the history
Support ShouldReconnectOnError with exponential backoff up to 10 minutes.
  • Loading branch information
bwmarrin committed Jan 22, 2016
2 parents 94b087c + e94e14e commit 54e14ba
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 47 deletions.
2 changes: 2 additions & 0 deletions structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ type Session struct {

// When nil, the session is not listening.
listening chan interface{}

ShouldReconnectOnError bool
}

// A VoiceRegion stores data for a specific voice region server.
Expand Down
117 changes: 70 additions & 47 deletions wsapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,32 @@ func (s *Session) listen(wsConn *websocket.Conn, listening <-chan interface{}) {
for {
messageType, message, err := wsConn.ReadMessage()
if err != nil {
// There has been an error reading, Close() the websocket so that
// OnDisconnect is fired.
s.Close()
// 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
// OnDisconnect is fired.
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
}

Expand All @@ -133,6 +156,50 @@ func (s *Session) listen(wsConn *websocket.Conn, listening <-chan interface{}) {
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 {
Name string `json:"name"`
}
Expand Down Expand Up @@ -549,50 +616,6 @@ func (s *Session) event(messageType int, message []byte) (err error) {
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
// ------------------------------------------------------------------------------------------------
Expand Down

0 comments on commit 54e14ba

Please sign in to comment.