blob: e6963c475d350ee4f959dfae899701f41b5734fc [file] [log] [blame]
Shyam Jayaramandbae76b2014-11-17 12:51:29 -08001// +build !nacl
Nicolas LaCassefea49162014-11-17 15:41:03 -08002
Shyam Jayaramandbae76b2014-11-17 12:51:29 -08003package websocket
4
5import (
6 "fmt"
7 "github.com/gorilla/websocket"
8 "io"
9 "net"
10 "sync"
Shyam Jayaramandbae76b2014-11-17 12:51:29 -080011 "time"
12)
13
14// WebsocketConn provides a net.Conn interface for a websocket connection.
15func WebsocketConn(ws *websocket.Conn) net.Conn {
16 return &wrappedConn{ws: ws}
17}
18
19// wrappedConn provides a net.Conn interface to a websocket.
20// The underlying websocket connection needs regular calls to Read to make sure
21// websocket control messages (such as pings) are processed by the websocket
22// library.
23type wrappedConn struct {
24 ws *websocket.Conn
25 currReader io.Reader
26
27 // The gorilla docs aren't explicit about reading and writing from
28 // different goroutines. It is explicit that only one goroutine can
29 // do a write at any given time and only one goroutine can do a read
30 // at any given time. Based on inspection it seems that using a reader
31 // and writer simultaneously is safe, but this might change with
32 // future changes. We can't actually share the lock, because this means
33 // that we can't write while we are waiting for a message, causing some
34 // deadlocks where a write is need to unblock a read.
35 writeLock sync.Mutex
36 readLock sync.Mutex
37}
38
39func (c *wrappedConn) readFromCurrReader(b []byte) (int, error) {
40 n, err := c.currReader.Read(b)
41 if err == io.EOF {
42 err = nil
43 c.currReader = nil
44 }
45 return n, err
Shyam Jayaramandbae76b2014-11-17 12:51:29 -080046}
47
48func (c *wrappedConn) Read(b []byte) (int, error) {
49 c.readLock.Lock()
50 defer c.readLock.Unlock()
51 var n int
52 var err error
53
54 // TODO(bjornick): It would be nice to be able to read multiple messages at
55 // a time in case the first message is not big enough to fill b and another
56 // message is ready.
57 // Loop until we either get data or an error. This exists
58 // mostly to avoid return 0, nil.
59 for n == 0 && err == nil {
60 if c.currReader == nil {
61 t, r, err := c.ws.NextReader()
62
63 if t != websocket.BinaryMessage {
64 return 0, fmt.Errorf("Unexpected message type %d", t)
65 }
66 if err != nil {
67 return 0, err
68 }
69 c.currReader = r
70 }
71 n, err = c.readFromCurrReader(b)
72 }
73 return n, err
74}
75
76func (c *wrappedConn) Write(b []byte) (int, error) {
77 c.writeLock.Lock()
78 defer c.writeLock.Unlock()
79 if err := c.ws.WriteMessage(websocket.BinaryMessage, b); err != nil {
80 return 0, err
81 }
82 return len(b), nil
83}
84
85func (c *wrappedConn) Close() error {
86 c.writeLock.Lock()
87 defer c.writeLock.Unlock()
88 return c.ws.Close()
89}
90
91func (c *wrappedConn) LocalAddr() net.Addr {
92 return websocketAddr{s: c.ws.LocalAddr().String()}
93}
94
95func (c *wrappedConn) RemoteAddr() net.Addr {
96 return websocketAddr{s: c.ws.RemoteAddr().String()}
97}
98
99func (c *wrappedConn) SetDeadline(t time.Time) error {
100 if err := c.SetReadDeadline(t); err != nil {
101 return err
102 }
103 return c.SetWriteDeadline(t)
104}
105
106func (c *wrappedConn) SetReadDeadline(t time.Time) error {
107 return c.ws.SetReadDeadline(t)
108}
109
110func (c *wrappedConn) SetWriteDeadline(t time.Time) error {
111 return c.ws.SetWriteDeadline(t)
112}
113
114type websocketAddr struct {
115 s string
116}
117
118func (websocketAddr) Network() string {
119 return "ws"
120}
121
122func (w websocketAddr) String() string {
123 return w.s
124}