3
3
package websocket_test
4
4
5
5
import (
6
- "bufio"
7
6
"context"
8
- "crypto/rand"
9
7
"io"
10
- "math/big"
11
- "net"
12
- "net/http"
13
- "net/http/httptest"
14
8
"testing"
15
9
"time"
16
10
17
- "cdr.dev/slog/sloggers/slogtest/assert "
11
+ "golang.org/x/xerrors "
18
12
19
13
"nhooyr.io/websocket"
14
+ "nhooyr.io/websocket/internal/test/cmp"
15
+ "nhooyr.io/websocket/internal/test/wstest"
16
+ "nhooyr.io/websocket/internal/test/xrand"
17
+ "nhooyr.io/websocket/wsjson"
20
18
)
21
19
22
- func goFn (fn func ()) func () {
23
- done := make (chan struct {} )
20
+ func goFn (fn func () error ) chan error {
21
+ errs := make (chan error )
24
22
go func () {
25
- defer close (done )
26
- fn ()
23
+ defer close (errs )
24
+ errs <- fn ()
27
25
}()
28
26
29
- return func () {
30
- <- done
31
- }
27
+ return errs
32
28
}
33
29
34
30
func TestConn (t * testing.T ) {
35
31
t .Parallel ()
36
32
37
- t .Run ("json " , func (t * testing.T ) {
33
+ t .Run ("data " , func (t * testing.T ) {
38
34
t .Parallel ()
39
35
40
- for i := 0 ; i < 1 ; i ++ {
36
+ for i := 0 ; i < 10 ; i ++ {
41
37
t .Run ("" , func (t * testing.T ) {
42
- ctx , cancel := context .WithTimeout (context .Background (), time .Second * 5 )
38
+ t .Parallel ()
39
+
40
+ ctx , cancel := context .WithTimeout (context .Background (), time .Second * 10 )
43
41
defer cancel ()
44
42
45
- c1 , c2 := websocketPipe (t )
43
+ copts := websocket.CompressionOptions {
44
+ Mode : websocket .CompressionMode (xrand .Int (int (websocket .CompressionDisabled ))),
45
+ Threshold : xrand .Int (9999 ),
46
+ }
47
+
48
+ c1 , c2 , err := wstest .Pipe (& websocket.DialOptions {
49
+ CompressionOptions : copts ,
50
+ }, & websocket.AcceptOptions {
51
+ CompressionOptions : copts ,
52
+ })
53
+ if err != nil {
54
+ t .Fatal (err )
55
+ }
56
+ defer c1 .Close (websocket .StatusInternalError , "" )
57
+ defer c2 .Close (websocket .StatusInternalError , "" )
46
58
47
- wait := goFn (func () {
59
+ echoLoopErr := goFn (func () error {
48
60
err := echoLoop (ctx , c1 )
49
- assertCloseStatus (t , websocket .StatusNormalClosure , err )
61
+ return assertCloseStatus (websocket .StatusNormalClosure , err )
50
62
})
51
- defer wait ()
63
+ defer func () {
64
+ err := <- echoLoopErr
65
+ if err != nil {
66
+ t .Errorf ("echo loop error: %v" , err )
67
+ }
68
+ }()
52
69
defer cancel ()
53
70
54
71
c2 .SetReadLimit (1 << 30 )
55
72
56
73
for i := 0 ; i < 10 ; i ++ {
57
- n := randInt (t , 131_072 )
58
- echoJSON (t , c2 , n )
74
+ n := xrand .Int (131_072 )
75
+
76
+ msg := xrand .String (n )
77
+
78
+ writeErr := goFn (func () error {
79
+ return wsjson .Write (ctx , c2 , msg )
80
+ })
81
+
82
+ var act interface {}
83
+ err := wsjson .Read (ctx , c2 , & act )
84
+ if err != nil {
85
+ t .Fatal (err )
86
+ }
87
+
88
+ err = <- writeErr
89
+ if err != nil {
90
+ t .Fatal (err )
91
+ }
92
+
93
+ if ! cmp .Equal (msg , act ) {
94
+ t .Fatalf ("unexpected msg read: %v" , cmp .Diff (msg , act ))
95
+ }
59
96
}
60
97
61
98
c2 .Close (websocket .StatusNormalClosure , "" )
@@ -64,6 +101,16 @@ func TestConn(t *testing.T) {
64
101
})
65
102
}
66
103
104
+ func assertCloseStatus (exp websocket.StatusCode , err error ) error {
105
+ if websocket .CloseStatus (err ) == - 1 {
106
+ return xerrors .Errorf ("expected websocket.CloseError: %T %v" , err , err )
107
+ }
108
+ if websocket .CloseStatus (err ) != exp {
109
+ return xerrors .Errorf ("unexpected close status (%v):%v" , exp , err )
110
+ }
111
+ return nil
112
+ }
113
+
67
114
// echoLoop echos every msg received from c until an error
68
115
// occurs or the context expires.
69
116
// The read limit is set to 1 << 30.
@@ -98,75 +145,3 @@ func echoLoop(ctx context.Context, c *websocket.Conn) error {
98
145
}
99
146
}
100
147
}
101
-
102
- func randBool (t testing.TB ) bool {
103
- return randInt (t , 2 ) == 1
104
- }
105
-
106
- func randInt (t testing.TB , max int ) int {
107
- x , err := rand .Int (rand .Reader , big .NewInt (int64 (max )))
108
- assert .Success (t , "rand.Int" , err )
109
- return int (x .Int64 ())
110
- }
111
-
112
- type testHijacker struct {
113
- * httptest.ResponseRecorder
114
- serverConn net.Conn
115
- hijacked chan struct {}
116
- }
117
-
118
- var _ http.Hijacker = testHijacker {}
119
-
120
- func (hj testHijacker ) Hijack () (net.Conn , * bufio.ReadWriter , error ) {
121
- close (hj .hijacked )
122
- return hj .serverConn , bufio .NewReadWriter (bufio .NewReader (hj .serverConn ), bufio .NewWriter (hj .serverConn )), nil
123
- }
124
-
125
- func websocketPipe (t * testing.T ) (* websocket.Conn , * websocket.Conn ) {
126
- var serverConn * websocket.Conn
127
- tt := testTransport {
128
- h : func (w http.ResponseWriter , r * http.Request ) {
129
- serverConn = acceptWebSocket (t , r , w , nil )
130
- },
131
- }
132
-
133
- dialOpts := & websocket.DialOptions {
134
- HTTPClient : & http.Client {
135
- Transport : tt ,
136
- },
137
- }
138
-
139
- clientConn , _ , err := websocket .Dial (context .Background (), "ws://example.com" , dialOpts )
140
- assert .Success (t , "websocket.Dial" , err )
141
-
142
- if randBool (t ) {
143
- return serverConn , clientConn
144
- }
145
- return clientConn , serverConn
146
- }
147
-
148
- type testTransport struct {
149
- h http.HandlerFunc
150
- }
151
-
152
- func (t testTransport ) RoundTrip (r * http.Request ) (* http.Response , error ) {
153
- clientConn , serverConn := net .Pipe ()
154
-
155
- hj := testHijacker {
156
- ResponseRecorder : httptest .NewRecorder (),
157
- serverConn : serverConn ,
158
- hijacked : make (chan struct {}),
159
- }
160
-
161
- done := make (chan struct {})
162
- t .h .ServeHTTP (hj , r )
163
-
164
- select {
165
- case <- hj .hijacked :
166
- resp := hj .ResponseRecorder .Result ()
167
- resp .Body = clientConn
168
- return resp , nil
169
- case <- done :
170
- return hj .ResponseRecorder .Result (), nil
171
- }
172
- }
0 commit comments