blob: 3a291ea1e1c8f02abfbb5be4bfd6d809a4095a12 [file] [log] [blame]
// +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
}