Prevent a send on closed chan panic.
The select statement does not guarantee selecting a better case if one might panic for sending to a closed channel. This case was hit during client disconnect due to having multiple senders on a single channel with one of the senders closing the chan to notify the next goroutine to finish. This change gives each writes its own unique channel to prevent this error.
This commit is contained in:
parent
4f1d2e7121
commit
19fd6406e8
1 changed files with 21 additions and 14 deletions
35
sockets.go
35
sockets.go
|
@ -451,7 +451,9 @@ func (s *server) WSSendRecv(ws *websocket.Conn, authenticated bool) {
|
||||||
go stringQueue(recvQueueIn, recvQueueOut, cc.quit)
|
go stringQueue(recvQueueIn, recvQueueOut, cc.quit)
|
||||||
|
|
||||||
badAuth := make(chan struct{})
|
badAuth := make(chan struct{})
|
||||||
|
sendResp := make(chan []byte)
|
||||||
go func() {
|
go func() {
|
||||||
|
out:
|
||||||
for m := range recvQueueOut {
|
for m := range recvQueueOut {
|
||||||
resp, err := s.ReplyToFrontend([]byte(m), true, authenticated)
|
resp, err := s.ReplyToFrontend([]byte(m), true, authenticated)
|
||||||
if err == ErrBadAuth {
|
if err == ErrBadAuth {
|
||||||
|
@ -459,24 +461,28 @@ func (s *server) WSSendRecv(ws *websocket.Conn, authenticated bool) {
|
||||||
case badAuth <- struct{}{}:
|
case badAuth <- struct{}{}:
|
||||||
case <-cc.quit:
|
case <-cc.quit:
|
||||||
}
|
}
|
||||||
return
|
break out
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authentication passed.
|
// Authentication passed.
|
||||||
authenticated = true
|
authenticated = true
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case cc.send <- resp:
|
case sendResp <- resp:
|
||||||
case <-cc.quit:
|
case <-cc.quit:
|
||||||
|
break out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
close(cc.send)
|
close(sendResp)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
const deadline time.Duration = 2 * time.Second
|
const deadline time.Duration = 2 * time.Second
|
||||||
|
|
||||||
out:
|
out:
|
||||||
for {
|
for {
|
||||||
|
var m []byte
|
||||||
|
var ok bool
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-badAuth:
|
case <-badAuth:
|
||||||
// Bad auth. Disconnect.
|
// Bad auth. Disconnect.
|
||||||
|
@ -484,22 +490,23 @@ out:
|
||||||
ws.Close()
|
ws.Close()
|
||||||
break out
|
break out
|
||||||
|
|
||||||
case m, ok := <-cc.send:
|
case m = <-cc.send: // sends from external writers. never closes.
|
||||||
|
case m, ok = <-sendResp:
|
||||||
if !ok {
|
if !ok {
|
||||||
// Nothing left to send. Return so the handler exits.
|
// Nothing left to send. Return so the handler exits.
|
||||||
break out
|
break out
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err := ws.SetWriteDeadline(time.Now().Add(deadline))
|
err := ws.SetWriteDeadline(time.Now().Add(deadline))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Cannot set write deadline: %v", err)
|
log.Errorf("Cannot set write deadline: %v", err)
|
||||||
break out
|
break out
|
||||||
}
|
}
|
||||||
err = websocket.Message.Send(ws, string(m))
|
err = websocket.Message.Send(ws, string(m))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Infof("Cannot complete client websocket send: %v", err)
|
log.Infof("Cannot complete client websocket send: %v", err)
|
||||||
break out
|
break out
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
close(sendQuit)
|
close(sendQuit)
|
||||||
|
|
Loading…
Reference in a new issue