MWSE/internal/ws/conn.go

44 lines
1.7 KiB
Go

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])
}