You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
170 lines
4.0 KiB
170 lines
4.0 KiB
// +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) |
|
|
|
return |
|
}) |
|
} |
|
|
|
// 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 |
|
}
|
|
|