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:
Josh Rickmar 2014-03-20 16:10:42 -05:00
parent 4f1d2e7121
commit 19fd6406e8

View file

@ -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)