| // +build !nacl |
| |
| package websocket |
| |
| import ( |
| "fmt" |
| "io" |
| "net" |
| "sync" |
| "time" |
| |
| "github.com/gorilla/websocket" |
| ) |
| |
| // WebsocketConn provides a net.Conn interface for a websocket connection. |
| func WebsocketConn(ws *websocket.Conn) net.Conn { |
| return &wrappedConn{ws: ws} |
| } |
| |
| // wrappedConn provides a net.Conn interface to a websocket. |
| // The underlying websocket connection needs regular calls to Read to make sure |
| // websocket control messages (such as pings) are processed by the websocket |
| // library. |
| type wrappedConn struct { |
| ws *websocket.Conn |
| currReader io.Reader |
| |
| // The gorilla docs aren't explicit about reading and writing from |
| // different goroutines. It is explicit that only one goroutine can |
| // do a write at any given time and only one goroutine can do a read |
| // at any given time. Based on inspection it seems that using a reader |
| // and writer simultaneously is safe, but this might change with |
| // future changes. We can't actually share the lock, because this means |
| // that we can't write while we are waiting for a message, causing some |
| // deadlocks where a write is need to unblock a read. |
| writeLock sync.Mutex |
| readLock sync.Mutex |
| } |
| |
| func (c *wrappedConn) readFromCurrReader(b []byte) (int, error) { |
| n, err := c.currReader.Read(b) |
| if err == io.EOF { |
| err = nil |
| c.currReader = nil |
| } |
| return n, err |
| } |
| |
| func (c *wrappedConn) Read(b []byte) (int, error) { |
| c.readLock.Lock() |
| defer c.readLock.Unlock() |
| var n int |
| var err error |
| |
| // TODO(bjornick): It would be nice to be able to read multiple messages at |
| // a time in case the first message is not big enough to fill b and another |
| // message is ready. |
| // Loop until we either get data or an error. This exists |
| // mostly to avoid return 0, nil. |
| for n == 0 && err == nil { |
| if c.currReader == nil { |
| t, r, err := c.ws.NextReader() |
| |
| if t != websocket.BinaryMessage { |
| return 0, fmt.Errorf("Unexpected message type %d", t) |
| } |
| if err != nil { |
| return 0, err |
| } |
| c.currReader = r |
| } |
| n, err = c.readFromCurrReader(b) |
| } |
| return n, err |
| } |
| |
| func (c *wrappedConn) Write(b []byte) (int, error) { |
| c.writeLock.Lock() |
| defer c.writeLock.Unlock() |
| if err := c.ws.WriteMessage(websocket.BinaryMessage, b); err != nil { |
| return 0, err |
| } |
| return len(b), nil |
| } |
| |
| func (c *wrappedConn) Close() error { |
| c.writeLock.Lock() |
| defer c.writeLock.Unlock() |
| return c.ws.Close() |
| } |
| |
| func (c *wrappedConn) LocalAddr() net.Addr { |
| return websocketAddr{s: c.ws.LocalAddr().String()} |
| } |
| |
| func (c *wrappedConn) RemoteAddr() net.Addr { |
| return websocketAddr{s: c.ws.RemoteAddr().String()} |
| } |
| |
| func (c *wrappedConn) SetDeadline(t time.Time) error { |
| if err := c.SetReadDeadline(t); err != nil { |
| return err |
| } |
| return c.SetWriteDeadline(t) |
| } |
| |
| func (c *wrappedConn) SetReadDeadline(t time.Time) error { |
| return c.ws.SetReadDeadline(t) |
| } |
| |
| func (c *wrappedConn) SetWriteDeadline(t time.Time) error { |
| return c.ws.SetWriteDeadline(t) |
| } |
| |
| type websocketAddr struct { |
| s string |
| } |
| |
| func (websocketAddr) Network() string { |
| return "ws" |
| } |
| |
| func (w websocketAddr) String() string { |
| return w.s |
| } |