// Package wsconn wraps a gorilla/websocket.Conn into a net.Conn so that // stream multiplexers like hashicorp/yamux can run on top of a WebSocket. package wsconn import ( "io" "net" "sync" "time" "github.com/gorilla/websocket" ) // Conn adapts a *websocket.Conn to the net.Conn interface. // All messages are sent/received as Binary frames. type Conn struct { ws *websocket.Conn reader io.Reader rmu sync.Mutex wmu sync.Mutex } // New wraps a WebSocket connection as a net.Conn. func New(ws *websocket.Conn) *Conn { return &Conn{ws: ws} } func (c *Conn) Read(p []byte) (int, error) { c.rmu.Lock() defer c.rmu.Unlock() for { if c.reader != nil { n, err := c.reader.Read(p) if err == io.EOF { c.reader = nil if n > 0 { return n, nil } continue } return n, err } _, reader, err := c.ws.NextReader() if err != nil { return 0, err } c.reader = reader } } func (c *Conn) Write(p []byte) (int, error) { c.wmu.Lock() defer c.wmu.Unlock() err := c.ws.WriteMessage(websocket.BinaryMessage, p) if err != nil { return 0, err } return len(p), nil } func (c *Conn) Close() error { return c.ws.Close() } func (c *Conn) LocalAddr() net.Addr { return c.ws.LocalAddr() } func (c *Conn) RemoteAddr() net.Addr { return c.ws.RemoteAddr() } func (c *Conn) SetDeadline(t time.Time) error { if err := c.ws.SetReadDeadline(t); err != nil { return err } return c.ws.SetWriteDeadline(t) } func (c *Conn) SetReadDeadline(t time.Time) error { return c.ws.SetReadDeadline(t) } func (c *Conn) SetWriteDeadline(t time.Time) error { return c.ws.SetWriteDeadline(t) }