Skip to content

Commit 8a1efed

Browse files
committed
fix(ws_js): use close codes browsers accept
WebSocket.close throws for any code other than 1000 and 3000-4999, so closing with the codes this package uses itself (1001 from CloseNow, 1008, 1009, 1011) failed and left the socket open, and under TinyGo the throw is a fatal panic. Send 1000 for those codes while keeping the requested code in the local close error, and stop the error handler from closing, since a close event always follows and waiting for it inside the callback deadlocks.
1 parent 9c8faad commit 8a1efed

2 files changed

Lines changed: 33 additions & 1 deletion

File tree

‎ws_js.go‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,9 @@ func (c *Conn) init() {
101101
})
102102

103103
c.releaseOnError = c.ws.OnError(func(v js.Value) {
104+
// A close event always follows. Closing here would wait for it
105+
// inside this callback and deadlock.
104106
c.setCloseErr(errors.New(v.Get("message").String()))
105-
c.closeWithInternal()
106107
})
107108

108109
c.releaseOnMessage = c.ws.OnMessage(func(e wsjs.MessageEvent) {
@@ -259,6 +260,9 @@ func (c *Conn) exportedClose(code StatusCode, reason string) error {
259260
})
260261

261262
c.setCloseErr(ce)
263+
if code != StatusNormalClosure && (code < 3000 || code > 4999) {
264+
code = StatusNormalClosure // Browsers throw on any other code.
265+
}
262266
err := c.ws.Close(int(code), reason)
263267
if err != nil {
264268
return err

‎ws_js_test.go‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,31 @@ func TestWasmDialTimeout(t *testing.T) {
5252
t.Fatal("wasm context dial timeout is not working", time.Since(beforeDial))
5353
}
5454
}
55+
56+
func TestWasmCloseStatus(t *testing.T) {
57+
t.Parallel()
58+
59+
// The library closes with these itself, but browsers only accept
60+
// 1000 and 3000-4999.
61+
for _, code := range []websocket.StatusCode{
62+
websocket.StatusGoingAway,
63+
websocket.StatusPolicyViolation,
64+
websocket.StatusMessageTooBig,
65+
websocket.StatusInternalError,
66+
} {
67+
t.Run(code.String(), func(t *testing.T) {
68+
t.Parallel()
69+
70+
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
71+
defer cancel()
72+
73+
c, _, err := websocket.Dial(ctx, os.Getenv("WS_ECHO_SERVER_URL"), &websocket.DialOptions{
74+
Subprotocols: []string{"echo"},
75+
})
76+
assert.Success(t, err)
77+
78+
err = c.Close(code, "")
79+
assert.Success(t, err)
80+
})
81+
}
82+
}

0 commit comments

Comments
 (0)