Created
July 18, 2019 08:14
-
-
Save ear7h/fc535f6af869cea9d8d42300ceb6c2e8 to your computer and use it in GitHub Desktop.
client side WebSocket as a net.Conn (wasm)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// +build js | |
package websocket | |
import ( | |
"fmt" | |
"io" | |
"net" | |
"syscall/js" | |
"time" | |
) | |
const blobToArr = `function blobToArr (b, cb) { | |
new Response(b).arrayBuffer().then((arr) => cb(new Uint8Array(arr))) | |
}` | |
func init() { | |
js.Global().Call("eval", blobToArr) | |
} | |
func Dial(addr string) (net.Conn, error) { | |
ret := &conn{ | |
in: make(chan []byte), | |
more: nil, | |
isOpen: true, | |
addr: wsAddr{addr}, | |
} | |
ws := js.Global().Get("WebSocket").New(addr) | |
ws.Call("addEventListener", | |
"message", | |
js.FuncOf(ret.onMessage)) | |
ws.Call("addEventListener", | |
"error", | |
js.FuncOf(ret.onError)) | |
start := make(chan struct{}) | |
ws.Call("addEventListener", | |
"open", | |
js.FuncOf(func(_ js.Value, _ []js.Value) interface{} { | |
start <- struct{}{} | |
return nil | |
})) | |
ret.ws = ws | |
<-start | |
return ret, nil | |
} | |
type conn struct { | |
ws js.Value | |
in chan []byte | |
more []byte | |
isOpen bool | |
addr wsAddr | |
} | |
type wsAddr struct { | |
addr string | |
} | |
func (wsAddr) Network() string { | |
return "ws" | |
} | |
func (a wsAddr) String() string { | |
return a.addr | |
} | |
func (c *conn) LocalAddr() net.Addr { | |
return c.addr | |
} | |
func (c *conn) RemoteAddr() net.Addr { | |
return c.addr | |
} | |
func (c *conn) SetDeadline(t time.Time) error {return nil} | |
func (c *conn) SetReadDeadline(t time.Time) error {return nil} | |
func (c *conn) SetWriteDeadline(t time.Time) error {return nil} | |
func (c *conn) Write(byt []byte) (int, error) { | |
fmt.Println("Write: ", len(byt)) | |
uint8Array := js.Global().Get("Uint8Array").New(len(byt)) | |
js.CopyBytesToJS(uint8Array, byt) | |
c.ws.Call("send", "") | |
c.ws.Call("send", uint8Array) | |
return len(byt), nil | |
} | |
func (c *conn) onError(_ js.Value, args []js.Value) interface{} { | |
err := args[0] | |
fmt.Println(err) | |
panic("ws error") | |
} | |
func (c *conn) onMessage(_ js.Value, args []js.Value) interface{} { | |
msg := args[0].Get("data") | |
switch { | |
case msg.InstanceOf(js.Global().Get("Blob")): | |
// new Uint8Array(await new Response(b).arrayBuffer()) | |
cb := func(_ js.Value, args []js.Value) interface{} { | |
msg := args[0] | |
arr := make([]byte, msg.Length()) | |
js.CopyBytesToGo(arr, msg) | |
fmt.Println("onMessage: ", len(arr)) | |
if c.isOpen { | |
c.in <- arr | |
} | |
return nil | |
} | |
js.Global().Call("blobToArr", | |
msg, | |
js.FuncOf(cb)) | |
default: | |
fmt.Println("unknown message type: ", msg) | |
fmt.Println(msg.Get("constructor").Get("name")) | |
panic("unknown message type") | |
} | |
return nil | |
} | |
func (c *conn) Read(byt []byte) (n int, err error) { | |
defer func() { fmt.Println("n: ", n) }() | |
fmt.Println("in read: ") | |
fmt.Println("len(byt): ", len(byt)) | |
fmt.Println("len(c.more): ", len(c.more)) | |
if len(c.more) != 0 { | |
//fmt.Println("c.more: ", c.more) | |
n = copy(byt, c.more) | |
c.more = c.more[n:] | |
if n == len(byt) { | |
// we filled the buffer | |
return n, nil | |
} | |
byt = byt[:n] | |
} | |
c.more, c.isOpen = <-c.in | |
fmt.Println("read from channel: ", len(c.more), c.isOpen) | |
n = copy(byt, c.more) | |
c.more = c.more[n:] | |
if !c.isOpen { | |
// the channel is closed and we | |
// wrote all that we needed into the | |
// givem buffer | |
return len(byt), io.EOF | |
} | |
return n, nil | |
} | |
func (c *conn) Close() error { | |
if c.isOpen { | |
c.isOpen = false | |
close(c.in) | |
} | |
return nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I think the code for
onMessage
causes a race condition...