| 
1 | 1 | package wsport  | 
2 | 2 | 
 
  | 
3 | 3 | import (  | 
4 |  | -	"io"  | 
5 |  | -	"net"  | 
6 |  | -	"sync"  | 
7 |  | -	"time"  | 
8 |  | - | 
9 | 4 | 	"github.com/libp2p/go-libp2p/core/network"  | 
10 | 5 | 	"github.com/libp2p/go-libp2p/core/transport"  | 
11 |  | - | 
12 |  | -	ws "github.com/gorilla/websocket"  | 
13 | 6 | )  | 
14 | 7 | 
 
  | 
15 |  | -// GracefulCloseTimeout is the time to wait trying to gracefully close a  | 
16 |  | -// connection before simply cutting it.  | 
17 |  | -var GracefulCloseTimeout = 100 * time.Millisecond  | 
18 |  | - | 
19 |  | -// Conn implements net.Conn interface for gorilla/websocket.  | 
20 |  | -type Conn struct {  | 
21 |  | -	*ws.Conn  | 
22 |  | -	secure             bool  | 
23 |  | -	DefaultMessageType int  | 
24 |  | -	reader             io.Reader  | 
25 |  | -	closeOnce          sync.Once  | 
26 |  | - | 
27 |  | -	readLock, writeLock sync.Mutex  | 
28 |  | -}  | 
29 |  | - | 
30 |  | -var _ net.Conn = (*Conn)(nil)  | 
31 |  | - | 
32 |  | -// NewConn creates a Conn given a regular gorilla/websocket Conn.  | 
33 |  | -func NewConn(raw *ws.Conn, secure bool) *Conn {  | 
34 |  | -	return &Conn{  | 
35 |  | -		Conn:               raw,  | 
36 |  | -		secure:             secure,  | 
37 |  | -		DefaultMessageType: ws.BinaryMessage,  | 
38 |  | -	}  | 
39 |  | -}  | 
40 |  | - | 
41 |  | -func (c *Conn) Read(b []byte) (int, error) {  | 
42 |  | -	c.readLock.Lock()  | 
43 |  | -	defer c.readLock.Unlock()  | 
44 |  | - | 
45 |  | -	if c.reader == nil {  | 
46 |  | -		if err := c.prepNextReader(); err != nil {  | 
47 |  | -			return 0, err  | 
48 |  | -		}  | 
49 |  | -	}  | 
50 |  | - | 
51 |  | -	for {  | 
52 |  | -		n, err := c.reader.Read(b)  | 
53 |  | -		switch err {  | 
54 |  | -		case io.EOF:  | 
55 |  | -			c.reader = nil  | 
56 |  | - | 
57 |  | -			if n > 0 {  | 
58 |  | -				return n, nil  | 
59 |  | -			}  | 
60 |  | - | 
61 |  | -			if err := c.prepNextReader(); err != nil {  | 
62 |  | -				return 0, err  | 
63 |  | -			}  | 
64 |  | - | 
65 |  | -			// explicitly looping  | 
66 |  | -		default:  | 
67 |  | -			return n, err  | 
68 |  | -		}  | 
69 |  | -	}  | 
70 |  | -}  | 
71 |  | - | 
72 |  | -func (c *Conn) prepNextReader() error {  | 
73 |  | -	t, r, err := c.Conn.NextReader()  | 
74 |  | -	if err != nil {  | 
75 |  | -		if wserr, ok := err.(*ws.CloseError); ok {  | 
76 |  | -			if wserr.Code == 1000 || wserr.Code == 1005 {  | 
77 |  | -				return io.EOF  | 
78 |  | -			}  | 
79 |  | -		}  | 
80 |  | -		return err  | 
81 |  | -	}  | 
82 |  | - | 
83 |  | -	if t == ws.CloseMessage {  | 
84 |  | -		return io.EOF  | 
85 |  | -	}  | 
86 |  | - | 
87 |  | -	c.reader = r  | 
88 |  | -	return nil  | 
89 |  | -}  | 
90 |  | - | 
91 |  | -func (c *Conn) Write(b []byte) (n int, err error) {  | 
92 |  | -	c.writeLock.Lock()  | 
93 |  | -	defer c.writeLock.Unlock()  | 
94 |  | - | 
95 |  | -	if err := c.Conn.WriteMessage(c.DefaultMessageType, b); err != nil {  | 
96 |  | -		return 0, err  | 
97 |  | -	}  | 
98 |  | - | 
99 |  | -	return len(b), nil  | 
100 |  | -}  | 
101 |  | - | 
102 |  | -// Close closes the connection. Only the first call to Close will receive the  | 
103 |  | -// close error, subsequent and concurrent calls will return nil.  | 
104 |  | -// This method is thread-safe.  | 
105 |  | -func (c *Conn) Close() error {  | 
106 |  | -	var err error  | 
107 |  | -	c.closeOnce.Do(func() {  | 
108 |  | -		err1 := c.Conn.WriteControl(  | 
109 |  | -			ws.CloseMessage,  | 
110 |  | -			ws.FormatCloseMessage(ws.CloseNormalClosure, "closed"),  | 
111 |  | -			time.Now().Add(GracefulCloseTimeout),  | 
112 |  | -		)  | 
113 |  | -		err2 := c.Conn.Close()  | 
114 |  | -		switch {  | 
115 |  | -		case err1 != nil:  | 
116 |  | -			err = err1  | 
117 |  | -		case err2 != nil:  | 
118 |  | -			err = err2  | 
119 |  | -		}  | 
120 |  | -	})  | 
121 |  | -	return err  | 
122 |  | -}  | 
123 |  | - | 
124 |  | -func (c *Conn) LocalAddr() net.Addr {  | 
125 |  | -	return NewAddrWithScheme(c.Conn.LocalAddr().String(), c.secure)  | 
126 |  | -}  | 
127 |  | - | 
128 |  | -func (c *Conn) RemoteAddr() net.Addr {  | 
129 |  | -	return NewAddrWithScheme(c.Conn.RemoteAddr().String(), c.secure)  | 
130 |  | -}  | 
131 |  | - | 
132 |  | -func (c *Conn) SetDeadline(t time.Time) error {  | 
133 |  | -	if err := c.SetReadDeadline(t); err != nil {  | 
134 |  | -		return err  | 
135 |  | -	}  | 
136 |  | - | 
137 |  | -	return c.SetWriteDeadline(t)  | 
138 |  | -}  | 
139 |  | - | 
140 |  | -func (c *Conn) SetReadDeadline(t time.Time) error {  | 
141 |  | -	// Don't lock when setting the read deadline. That would prevent us from  | 
142 |  | -	// interrupting an in-progress read.  | 
143 |  | -	return c.Conn.SetReadDeadline(t)  | 
144 |  | -}  | 
145 |  | - | 
146 |  | -func (c *Conn) SetWriteDeadline(t time.Time) error {  | 
147 |  | -	// Unlike the read deadline, we need to lock when setting the write  | 
148 |  | -	// deadline.  | 
149 |  | - | 
150 |  | -	c.writeLock.Lock()  | 
151 |  | -	defer c.writeLock.Unlock()  | 
152 |  | - | 
153 |  | -	return c.Conn.SetWriteDeadline(t)  | 
154 |  | -}  | 
155 |  | - | 
156 | 8 | type capableConn struct {  | 
157 | 9 | 	transport.CapableConn  | 
158 | 10 | }  | 
 | 
0 commit comments