github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/nhooyr.io/websocket/internal/wsjs/wsjs_js.go (about) 1 // +build js 2 3 // Package wsjs implements typed access to the browser javascript WebSocket API. 4 // 5 // https://developer.mozilla.org/en-US/docs/Web/API/WebSocket 6 package wsjs 7 8 import ( 9 "syscall/js" 10 ) 11 12 func handleJSError(err *error, onErr func()) { 13 r := recover() 14 15 if jsErr, ok := r.(js.Error); ok { 16 *err = jsErr 17 18 if onErr != nil { 19 onErr() 20 } 21 return 22 } 23 24 if r != nil { 25 panic(r) 26 } 27 } 28 29 // New is a wrapper around the javascript WebSocket constructor. 30 func New(url string, protocols []string) (c WebSocket, err error) { 31 defer handleJSError(&err, func() { 32 c = WebSocket{} 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 return c, nil 47 } 48 49 // WebSocket is a wrapper around a javascript WebSocket object. 50 type WebSocket struct { 51 v js.Value 52 } 53 54 func (c WebSocket) setBinaryType(typ string) { 55 c.v.Set("binaryType", string(typ)) 56 } 57 58 func (c WebSocket) addEventListener(eventType string, fn func(e js.Value)) func() { 59 f := js.FuncOf(func(this js.Value, args []js.Value) interface{} { 60 fn(args[0]) 61 return nil 62 }) 63 c.v.Call("addEventListener", eventType, f) 64 65 return func() { 66 c.v.Call("removeEventListener", eventType, f) 67 f.Release() 68 } 69 } 70 71 // CloseEvent is the type passed to a WebSocket close handler. 72 type CloseEvent struct { 73 Code uint16 74 Reason string 75 WasClean bool 76 } 77 78 // OnClose registers a function to be called when the WebSocket is closed. 79 func (c WebSocket) OnClose(fn func(CloseEvent)) (remove func()) { 80 return c.addEventListener("close", func(e js.Value) { 81 ce := CloseEvent{ 82 Code: uint16(e.Get("code").Int()), 83 Reason: e.Get("reason").String(), 84 WasClean: e.Get("wasClean").Bool(), 85 } 86 fn(ce) 87 }) 88 } 89 90 // OnError registers a function to be called when there is an error 91 // with the WebSocket. 92 func (c WebSocket) OnError(fn func(e js.Value)) (remove func()) { 93 return c.addEventListener("error", fn) 94 } 95 96 // MessageEvent is the type passed to a message handler. 97 type MessageEvent struct { 98 // string or []byte. 99 Data interface{} 100 101 // There are more fields to the interface but we don't use them. 102 // See https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent 103 } 104 105 // OnMessage registers a function to be called when the WebSocket receives a message. 106 func (c WebSocket) OnMessage(fn func(m MessageEvent)) (remove func()) { 107 return c.addEventListener("message", func(e js.Value) { 108 var data interface{} 109 110 arrayBuffer := e.Get("data") 111 if arrayBuffer.Type() == js.TypeString { 112 data = arrayBuffer.String() 113 } else { 114 data = extractArrayBuffer(arrayBuffer) 115 } 116 117 me := MessageEvent{ 118 Data: data, 119 } 120 fn(me) 121 122 return 123 }) 124 } 125 126 // Subprotocol returns the WebSocket subprotocol in use. 127 func (c WebSocket) Subprotocol() string { 128 return c.v.Get("protocol").String() 129 } 130 131 // OnOpen registers a function to be called when the WebSocket is opened. 132 func (c WebSocket) OnOpen(fn func(e js.Value)) (remove func()) { 133 return c.addEventListener("open", fn) 134 } 135 136 // Close closes the WebSocket with the given code and reason. 137 func (c WebSocket) Close(code int, reason string) (err error) { 138 defer handleJSError(&err, nil) 139 c.v.Call("close", code, reason) 140 return err 141 } 142 143 // SendText sends the given string as a text message 144 // on the WebSocket. 145 func (c WebSocket) SendText(v string) (err error) { 146 defer handleJSError(&err, nil) 147 c.v.Call("send", v) 148 return err 149 } 150 151 // SendBytes sends the given message as a binary message 152 // on the WebSocket. 153 func (c WebSocket) SendBytes(v []byte) (err error) { 154 defer handleJSError(&err, nil) 155 c.v.Call("send", uint8Array(v)) 156 return err 157 } 158 159 func extractArrayBuffer(arrayBuffer js.Value) []byte { 160 uint8Array := js.Global().Get("Uint8Array").New(arrayBuffer) 161 dst := make([]byte, uint8Array.Length()) 162 js.CopyBytesToGo(dst, uint8Array) 163 return dst 164 } 165 166 func uint8Array(src []byte) js.Value { 167 uint8Array := js.Global().Get("Uint8Array").New(len(src)) 168 js.CopyBytesToJS(uint8Array, src) 169 return uint8Array 170 }