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
|
//go:build js
// +build js
// Package wsjs implements typed access to the browser javascript WebSocket API.
//
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
package wsjs
import (
"syscall/js"
)
func handleJSError(err *error, onErr func()) {
r := recover()
if jsErr, ok := r.(js.Error); ok {
*err = jsErr
if onErr != nil {
onErr()
}
return
}
if r != nil {
panic(r)
}
}
// New is a wrapper around the javascript WebSocket constructor.
func New(url string, protocols []string) (c WebSocket, err error) {
defer handleJSError(&err, func() {
c = WebSocket{}
})
jsProtocols := make([]interface{}, len(protocols))
for i, p := range protocols {
jsProtocols[i] = p
}
c = WebSocket{
v: js.Global().Get("WebSocket").New(url, jsProtocols),
}
c.setBinaryType("arraybuffer")
return c, nil
}
// WebSocket is a wrapper around a javascript WebSocket object.
type WebSocket struct {
v js.Value
}
func (c WebSocket) setBinaryType(typ string) {
c.v.Set("binaryType", string(typ))
}
func (c WebSocket) addEventListener(eventType string, fn func(e js.Value)) func() {
f := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
fn(args[0])
return nil
})
c.v.Call("addEventListener", eventType, f)
return func() {
c.v.Call("removeEventListener", eventType, f)
f.Release()
}
}
// CloseEvent is the type passed to a WebSocket close handler.
type CloseEvent struct {
Code uint16
Reason string
WasClean bool
}
// OnClose registers a function to be called when the WebSocket is closed.
func (c WebSocket) OnClose(fn func(CloseEvent)) (remove func()) {
return c.addEventListener("close", func(e js.Value) {
ce := CloseEvent{
Code: uint16(e.Get("code").Int()),
Reason: e.Get("reason").String(),
WasClean: e.Get("wasClean").Bool(),
}
fn(ce)
})
}
// OnError registers a function to be called when there is an error
// with the WebSocket.
func (c WebSocket) OnError(fn func(e js.Value)) (remove func()) {
return c.addEventListener("error", fn)
}
// MessageEvent is the type passed to a message handler.
type MessageEvent struct {
// string or []byte.
Data interface{}
// There are more fields to the interface but we don't use them.
// See https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent
}
// OnMessage registers a function to be called when the WebSocket receives a message.
func (c WebSocket) OnMessage(fn func(m MessageEvent)) (remove func()) {
return c.addEventListener("message", func(e js.Value) {
var data interface{}
arrayBuffer := e.Get("data")
if arrayBuffer.Type() == js.TypeString {
data = arrayBuffer.String()
} else {
data = extractArrayBuffer(arrayBuffer)
}
me := MessageEvent{
Data: data,
}
fn(me)
})
}
// Subprotocol returns the WebSocket subprotocol in use.
func (c WebSocket) Subprotocol() string {
return c.v.Get("protocol").String()
}
// OnOpen registers a function to be called when the WebSocket is opened.
func (c WebSocket) OnOpen(fn func(e js.Value)) (remove func()) {
return c.addEventListener("open", fn)
}
// Close closes the WebSocket with the given code and reason.
func (c WebSocket) Close(code int, reason string) (err error) {
defer handleJSError(&err, nil)
c.v.Call("close", code, reason)
return err
}
// SendText sends the given string as a text message
// on the WebSocket.
func (c WebSocket) SendText(v string) (err error) {
defer handleJSError(&err, nil)
c.v.Call("send", v)
return err
}
// SendBytes sends the given message as a binary message
// on the WebSocket.
func (c WebSocket) SendBytes(v []byte) (err error) {
defer handleJSError(&err, nil)
c.v.Call("send", uint8Array(v))
return err
}
func extractArrayBuffer(arrayBuffer js.Value) []byte {
uint8Array := js.Global().Get("Uint8Array").New(arrayBuffer)
dst := make([]byte, uint8Array.Length())
js.CopyBytesToGo(dst, uint8Array)
return dst
}
func uint8Array(src []byte) js.Value {
uint8Array := js.Global().Get("Uint8Array").New(len(src))
js.CopyBytesToJS(uint8Array, src)
return uint8Array
}
|