Skip to content

Commit 0820148

Browse files
committed
Implement low level typed interface to the browser WebSocket API
Updates #121
1 parent 6448e27 commit 0820148

File tree

1 file changed

+150
-0
lines changed

1 file changed

+150
-0
lines changed

Diff for: internal/websocketjs/websocketjs.go

+150
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
// +build wasm
2+
3+
// websocketjs implements typed access to the javascript
4+
// WebSocket API.
5+
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
6+
package websocketjs
7+
8+
import (
9+
"context"
10+
"syscall/js"
11+
)
12+
13+
func handleJSError(err *error, onErr func()) {
14+
r := recover()
15+
16+
if jsErr, ok := r.(js.Error); ok {
17+
*err = jsErr
18+
19+
if onErr != nil {
20+
onErr()
21+
}
22+
return
23+
}
24+
25+
if r != nil {
26+
panic(r)
27+
}
28+
}
29+
30+
func New(ctx context.Context, url string, protocols []string) (c *WebSocket, err error) {
31+
defer handleJSError(&err, func() {
32+
c = nil
33+
})
34+
35+
jsProtocols := make([]interface{}, len(protocols))
36+
for i, p := range protocols {
37+
jsProtocols[i] = p
38+
}
39+
40+
c = &WebSocket{
41+
v: js.Global().Get("WebSocket").New(url, jsProtocols),
42+
}
43+
44+
c.setBinaryType("arrayBuffer")
45+
46+
c.Extensions = c.v.Get("extensions").String()
47+
c.Protocol = c.v.Get("protocol").String()
48+
c.URL = c.v.Get("url").String()
49+
50+
return c, nil
51+
}
52+
53+
type WebSocket struct {
54+
Extensions string
55+
Protocol string
56+
URL string
57+
58+
v js.Value
59+
}
60+
61+
func (c *WebSocket) setBinaryType(typ string) {
62+
c.v.Set("binaryType", string(typ))
63+
}
64+
65+
func (c *WebSocket) BufferedAmount() uint32 {
66+
return uint32(c.v.Get("bufferedAmount").Int())
67+
}
68+
69+
func (c *WebSocket) addEventListener(eventType string, fn func(e js.Value)) {
70+
c.v.Call("addEventListener", eventType, js.FuncOf(func(this js.Value, args []js.Value) interface{} {
71+
fn(args[0])
72+
return nil
73+
}))
74+
}
75+
76+
type CloseEvent struct {
77+
Code uint16
78+
Reason string
79+
WasClean bool
80+
}
81+
82+
func (c *WebSocket) OnClose(fn func(CloseEvent)) {
83+
c.addEventListener("close", func(e js.Value) {
84+
ce := CloseEvent{
85+
Code: uint16(e.Get("code").Int()),
86+
Reason: e.Get("reason").String(),
87+
WasClean: e.Get("wasClean").Bool(),
88+
}
89+
fn(ce)
90+
})
91+
}
92+
93+
func (c *WebSocket) OnError(fn func(e js.Value)) {
94+
c.addEventListener("error", fn)
95+
}
96+
97+
type MessageEvent struct {
98+
Data []byte
99+
// There are more types to the interface but we don't use them.
100+
// See https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent
101+
}
102+
103+
func (c *WebSocket) OnMessage(fn func(m MessageEvent)) {
104+
c.addEventListener("message", func(e js.Value) {
105+
arrayBuffer := e.Get("data")
106+
data := extractArrayBuffer(arrayBuffer)
107+
108+
me := MessageEvent{
109+
Data: data,
110+
}
111+
fn(me)
112+
113+
return
114+
})
115+
}
116+
117+
func (c *WebSocket) OnOpen(fn func(e js.Value)) {
118+
c.addEventListener("open", fn)
119+
}
120+
121+
func (c *WebSocket) Close(code int, reason string) (err error) {
122+
defer handleJSError(&err, nil)
123+
c.v.Call("close", code, reason)
124+
return err
125+
}
126+
127+
func (c *WebSocket) SendText(v string) (err error) {
128+
defer handleJSError(&err, nil)
129+
c.v.Call("send", v)
130+
return err
131+
}
132+
133+
func (c *WebSocket) SendBytes(v []byte) (err error) {
134+
defer handleJSError(&err, nil)
135+
c.v.Call("send", uint8Array(v))
136+
return err
137+
}
138+
139+
func extractArrayBuffer(arrayBuffer js.Value) []byte {
140+
uint8Array := js.Global().Get("Uint8Array").New(arrayBuffer)
141+
dst := make([]byte, uint8Array.Length())
142+
js.CopyBytesToGo(dst, uint8Array)
143+
return dst
144+
}
145+
146+
func uint8Array(src []byte) js.Value {
147+
uint8Array := js.Global().Get("Uint8Array").New(len(src))
148+
js.CopyBytesToJS(uint8Array, src)
149+
return uint8Array
150+
}

0 commit comments

Comments
 (0)