package ws import ( "crypto/rand" "fmt" "time" ) // Conn is the minimal slice of *gorilla/websocket.Conn that the engine relies on. // Depending on an interface (rather than the concrete type) lets the tests drive // a Client with an in-memory fake connection, so the concurrency tests in #26 do // not need real sockets. // // gorilla's contract: at most one goroutine may call the write methods at a time // and at most one may call the read methods at a time, BUT Close and WriteControl // may be called concurrently with everything else. The Client honours this by // funnelling all WriteMessage calls through a single writer goroutine, while the // ping loop uses WriteControl and shutdown uses Close. type Conn interface { ReadMessage() (messageType int, p []byte, err error) WriteMessage(messageType int, data []byte) error WriteControl(messageType int, data []byte, deadline time.Time) error SetReadLimit(limit int64) SetReadDeadline(t time.Time) error SetWriteDeadline(t time.Time) error SetPongHandler(h func(appData string) error) Close() error } // newUUID returns a random RFC 4122 version 4 UUID. The original server used // Node's crypto.randomUUID(); this keeps client ids in the same shape without a // third-party dependency. func newUUID() string { var b [16]byte if _, err := rand.Read(b[:]); err != nil { // crypto/rand failing is catastrophic and not something a relay can recover // from sensibly; surface it loudly rather than handing out a zero id. panic(fmt.Sprintf("ws: cannot read random bytes for uuid: %v", err)) } b[6] = (b[6] & 0x0f) | 0x40 // version 4 b[8] = (b[8] & 0x3f) | 0x80 // variant 10 return fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:16]) }