Description
Problem: If I have an application that waits for data over the websocket, how do I cleanly shut down the websocket? Here, cleanly means:
- stop waiting for new data
- perform the closing handshake
- verify that these things happened with no errors
Our first attempt at this calls (*Conn).Read(ctx)
in a loop on one goroutine, and then when it was time to shut down, calls (*Conn).Close()
from a different goroutine.
However, this would often result in close errors reading the response packet. Investigating, I found that calling Close()
concurrently with Read()
from different goroutines exposed a race condition where Read()
would read the frame header, and then Close()
would read the packet payload as if it were a header, and throw an error.
Would you consider this a bug? That is to say, are you meant to be able to call Read()
and Close()
from different goroutines concurrently?
My next attempt cancels the Context
passed to Read()
, and then calls Close()
from the same goroutine after Read()
returns. There are a couple problems with this approach. Firstly, canceling the Read()
context seems to trigger an opClose with status PolicyViolation. Ideally I'd be sending a normal status on disconnect. Secondly, Close()
returns a couple different error messages that seem to depend on some races in the library, including "already wrote close" and "WebSocket closed", so it's really hard to verify that the close handshake completed correctly.