FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
websocket/close.go at master · commitgo/websocket · GitHub
Uh oh!
There was an error while loading.
Please reload this page
.
commitgo
/
websocket
Public
forked from
coder/websocket
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Pull requests
0
Actions
Projects
Security and quality
0
Insights
Additional navigation options
Code
Pull requests
Actions
Projects
Security and quality
Insights
Expand file tree
Breadcrumbs
websocket
/
close.go
Copy path
More file actions
More file actions
Latest commit
History
History
History
335 lines (282 loc) · 7.67 KB
Breadcrumbs
websocket
/
close.go
Copy path
File metadata and controls
335 lines (282 loc) · 7.67 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
//go:build !js
package
websocket
import
(
"context"
"encoding/binary"
"errors"
"fmt"
"net"
"time"
"github.com/coder/websocket/internal/errd"
)
// StatusCode represents a WebSocket status code.
// https://tools.ietf.org/html/rfc6455#section-7.4
type
StatusCode
int
// https://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number
//
// These are only the status codes defined by the protocol.
//
// You can define custom codes in the 3000-4999 range.
// The 3000-3999 range is reserved for use by libraries, frameworks and applications.
// The 4000-4999 range is reserved for private use.
const
(
StatusNormalClosure
StatusCode
=
1000
StatusGoingAway
StatusCode
=
1001
StatusProtocolError
StatusCode
=
1002
StatusUnsupportedData
StatusCode
=
1003
// 1004 is reserved and so unexported.
statusReserved
StatusCode
=
1004
// StatusNoStatusRcvd cannot be sent in a close message.
// It is reserved for when a close message is received without
// a status code.
StatusNoStatusRcvd
StatusCode
=
1005
// StatusAbnormalClosure is exported for use only with Wasm.
// In non Wasm Go, the returned error will indicate whether the
// connection was closed abnormally.
StatusAbnormalClosure
StatusCode
=
1006
StatusInvalidFramePayloadData
StatusCode
=
1007
StatusPolicyViolation
StatusCode
=
1008
StatusMessageTooBig
StatusCode
=
1009
StatusMandatoryExtension
StatusCode
=
1010
StatusInternalError
StatusCode
=
1011
StatusServiceRestart
StatusCode
=
1012
StatusTryAgainLater
StatusCode
=
1013
StatusBadGateway
StatusCode
=
1014
// StatusTLSHandshake is only exported for use with Wasm.
// In non Wasm Go, the returned error will indicate whether there was
// a TLS handshake failure.
StatusTLSHandshake
StatusCode
=
1015
)
// CloseError is returned when the connection is closed with a status and reason.
//
// Use Go 1.13's errors.As to check for this error.
// Also see the CloseStatus helper.
type
CloseError
struct
{
Code
StatusCode
Reason
string
}
func
(
ce
CloseError
)
Error
()
string
{
return
fmt
.
Sprintf
(
"status = %v and reason = %q"
,
ce
.
Code
,
ce
.
Reason
)
}
// CloseStatus is a convenience wrapper around Go 1.13's errors.As to grab
// the status code from a CloseError.
//
// -1 will be returned if the passed error is nil or not a CloseError.
func
CloseStatus
(
err
error
)
StatusCode
{
var
ce
CloseError
if
errors
.
As
(
err
,
&
ce
) {
return
ce
.
Code
}
return
-
1
}
// Close performs the WebSocket close handshake with the given status code and reason.
//
// It will write a WebSocket close frame with a timeout of 5s and then wait 5s for
// the peer to send a close frame.
// All data messages received from the peer during the close handshake will be discarded.
//
// The connection can only be closed once. Additional calls to Close
// are no-ops.
//
// The maximum length of reason must be 125 bytes. Avoid sending a dynamic reason.
//
// Close will unblock all goroutines interacting with the connection once
// complete.
func
(
c
*
Conn
)
Close
(
code
StatusCode
,
reason
string
) (
err
error
) {
defer
errd
.
Wrap
(
&
err
,
"failed to close WebSocket"
)
if
c
.
casClosing
() {
err
=
c
.
waitGoroutines
()
if
err
!=
nil
{
return
err
}
return
net
.
ErrClosed
}
defer
func
() {
if
errors
.
Is
(
err
,
net
.
ErrClosed
) {
err
=
nil
}
}()
err
=
c
.
closeHandshake
(
code
,
reason
)
err2
:=
c
.
close
()
if
err
==
nil
&&
err2
!=
nil
{
err
=
err2
}
err2
=
c
.
waitGoroutines
()
if
err
==
nil
&&
err2
!=
nil
{
err
=
err2
}
return
err
}
// CloseNow closes the WebSocket connection without attempting a close handshake.
// Use when you do not want the overhead of the close handshake.
func
(
c
*
Conn
)
CloseNow
() (
err
error
) {
defer
errd
.
Wrap
(
&
err
,
"failed to immediately close WebSocket"
)
if
c
.
casClosing
() {
err
=
c
.
waitGoroutines
()
if
err
!=
nil
{
return
err
}
return
net
.
ErrClosed
}
defer
func
() {
if
errors
.
Is
(
err
,
net
.
ErrClosed
) {
err
=
nil
}
}()
err
=
c
.
close
()
err2
:=
c
.
waitGoroutines
()
if
err
==
nil
&&
err2
!=
nil
{
err
=
err2
}
return
err
}
func
(
c
*
Conn
)
closeHandshake
(
code
StatusCode
,
reason
string
)
error
{
err
:=
c
.
writeClose
(
code
,
reason
)
if
err
!=
nil
{
return
err
}
err
=
c
.
waitCloseHandshake
()
if
CloseStatus
(
err
)
!=
code
{
return
err
}
return
nil
}
func
(
c
*
Conn
)
writeClose
(
code
StatusCode
,
reason
string
)
error
{
ce
:=
CloseError
{
Code
:
code
,
Reason
:
reason
,
}
var
p
[]
byte
var
err
error
if
ce
.
Code
!=
StatusNoStatusRcvd
{
p
,
err
=
ce
.
bytes
()
if
err
!=
nil
{
return
err
}
}
ctx
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
time
.
Second
*
5
)
defer
cancel
()
err
=
c
.
writeControl
(
ctx
,
opClose
,
p
)
// If the connection closed as we're writing we ignore the error as we might
// have written the close frame, the peer responded and then someone else read it
// and closed the connection.
if
err
!=
nil
&&
!
errors
.
Is
(
err
,
net
.
ErrClosed
) {
return
err
}
return
nil
}
func
(
c
*
Conn
)
waitCloseHandshake
()
error
{
ctx
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
time
.
Second
*
5
)
defer
cancel
()
err
:=
c
.
readMu
.
lock
(
ctx
)
if
err
!=
nil
{
return
err
}
defer
c
.
readMu
.
unlock
()
for
i
:=
int64
(
0
);
i
<
c
.
msgReader
.
payloadLength
;
i
++
{
_
,
err
:=
c
.
br
.
ReadByte
()
if
err
!=
nil
{
return
err
}
}
for
{
h
,
err
:=
c
.
readLoop
(
ctx
)
if
err
!=
nil
{
return
err
}
for
i
:=
int64
(
0
);
i
<
h
.
payloadLength
;
i
++
{
_
,
err
:=
c
.
br
.
ReadByte
()
if
err
!=
nil
{
return
err
}
}
}
}
func
(
c
*
Conn
)
waitGoroutines
()
error
{
t
:=
time
.
NewTimer
(
time
.
Second
*
15
)
defer
t
.
Stop
()
c
.
closeReadMu
.
Lock
()
closeRead
:=
c
.
closeReadCtx
!=
nil
c
.
closeReadMu
.
Unlock
()
if
closeRead
{
select
{
case
<-
c
.
closeReadDone
:
case
<-
t
.
C
:
return
errors
.
New
(
"failed to wait for close read goroutine to exit"
)
}
}
select
{
case
<-
c
.
closed
:
case
<-
t
.
C
:
return
errors
.
New
(
"failed to wait for connection to be closed"
)
}
return
nil
}
func
parseClosePayload
(
p
[]
byte
) (
CloseError
,
error
) {
if
len
(
p
)
==
0
{
return
CloseError
{
Code
:
StatusNoStatusRcvd
,
},
nil
}
if
len
(
p
)
<
2
{
return
CloseError
{},
fmt
.
Errorf
(
"close payload %q too small, cannot even contain the 2 byte status code"
,
p
)
}
ce
:=
CloseError
{
Code
:
StatusCode
(
binary
.
BigEndian
.
Uint16
(
p
)),
Reason
:
string
(
p
[
2
:]),
}
if
!
validWireCloseCode
(
ce
.
Code
) {
return
CloseError
{},
fmt
.
Errorf
(
"invalid status code %v"
,
ce
.
Code
)
}
return
ce
,
nil
}
// See http://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number
// and https://tools.ietf.org/html/rfc6455#section-7.4.1
func
validWireCloseCode
(
code
StatusCode
)
bool
{
switch
code
{
case
statusReserved
,
StatusNoStatusRcvd
,
StatusAbnormalClosure
,
StatusTLSHandshake
:
return
false
}
if
code
>=
StatusNormalClosure
&&
code
<=
StatusBadGateway
{
return
true
}
if
code
>=
3000
&&
code
<=
4999
{
return
true
}
return
false
}
func
(
ce
CloseError
)
bytes
() ([]
byte
,
error
) {
p
,
err
:=
ce
.
bytesErr
()
if
err
!=
nil
{
err
=
fmt
.
Errorf
(
"failed to marshal close frame: %w"
,
err
)
ce
=
CloseError
{
Code
:
StatusInternalError
,
}
p
,
_
=
ce
.
bytesErr
()
}
return
p
,
err
}
const
maxCloseReason
=
maxControlPayload
-
2
func
(
ce
CloseError
)
bytesErr
() ([]
byte
,
error
) {
if
len
(
ce
.
Reason
)
>
maxCloseReason
{
return
nil
,
fmt
.
Errorf
(
"reason string max is %v but got %q with length %v"
,
maxCloseReason
,
ce
.
Reason
,
len
(
ce
.
Reason
))
}
if
!
validWireCloseCode
(
ce
.
Code
) {
return
nil
,
fmt
.
Errorf
(
"status code %v cannot be set"
,
ce
.
Code
)
}
buf
:=
make
([]
byte
,
2
+
len
(
ce
.
Reason
))
binary
.
BigEndian
.
PutUint16
(
buf
,
uint16
(
ce
.
Code
))
copy
(
buf
[
2
:],
ce
.
Reason
)
return
buf
,
nil
}
func
(
c
*
Conn
)
casClosing
()
bool
{
return
c
.
closing
.
Swap
(
true
)
}
func
(
c
*
Conn
)
isClosed
()
bool
{
select
{
case
<-
c
.
closed
:
return
true
default
:
return
false
}
}
Back
|
FazBrowse Home
|
New Git URL