feat: add WebSocket support for JavaScript/WebAssembly
Implement WebSocket dialer and stream for JS/WASM environment, enabling io.ReadWriteCloser interface for WebSocket connections with proper buffering and synchronization.
lemon-mint committed
Oct 30, 2025 at 16:16 UTC
725a0d8dcaea7fa5f5acac213294226e533ca2e1
2 files changed
+89
cmd/webclient/sdk_js.go
new
+22
@@ -0,0 +1,22 @@
1
+package main
2
+
3
+import (
4
+ "context"
5
+ "io"
6
+
7
+ "github.com/gosuda/portal/cmd/webclient/wsjs"
8
+)
9
+
10
+// WebSocketDialerJS creates a WebSocket dialer function for JavaScript/WebAssembly environment
11
+func WebSocketDialerJS() func(context.Context, string) (io.ReadWriteCloser, error) {
12
+ return func(ctx context.Context, url string) (io.ReadWriteCloser, error) {
13
+ // Use the wsjs package to create a WebSocket connection
14
+ conn, err := wsjs.Dial(url)
15
+ if err != nil {
16
+ return nil, err
17
+ }
18
+
19
+ // Wrap the WebSocket connection with WsStream for io.ReadWriteCloser interface
20
+ return wsjs.NewWsStream(conn), nil
21
+ }
22
+}
cmd/webclient/wsjs/wsstream_js.go
new
+67
@@ -0,0 +1,67 @@
1
+package wsjs
2
+
3
+import (
4
+ "sync"
5
+)
6
+
7
+// WsStream provides an io.Reader and io.Writer interface for WebSocket connections
8
+type WsStream struct {
9
+ conn *Conn
10
+ currentBuffer []byte
11
+ readMu sync.Mutex
12
+ writeMu sync.Mutex
13
+}
14
+
15
+// NewWsStream creates a new WsStream from a WebSocket connection
16
+func NewWsStream(conn *Conn) *WsStream {
17
+ return &WsStream{
18
+ conn: conn,
19
+ }
20
+}
21
+
22
+// Read implements io.Reader interface
23
+func (ws *WsStream) Read(p []byte) (n int, err error) {
24
+ ws.readMu.Lock()
25
+ defer ws.readMu.Unlock()
26
+
27
+ // If we have remaining data from previous message, use it first
28
+ if len(ws.currentBuffer) > 0 {
29
+ n = copy(p, ws.currentBuffer)
30
+ ws.currentBuffer = ws.currentBuffer[n:]
31
+ return n, nil
32
+ }
33
+
34
+ // Get next message from WebSocket
35
+ msg, err := ws.conn.NextMessage()
36
+ if err != nil {
37
+ return 0, err
38
+ }
39
+
40
+ // Copy message data to buffer
41
+ n = copy(p, msg)
42
+
43
+ // Store any remaining data for next read
44
+ if n < len(msg) {
45
+ ws.currentBuffer = msg[n:]
46
+ }
47
+
48
+ return n, nil
49
+}
50
+
51
+// Write implements io.Writer interface
52
+func (ws *WsStream) Write(p []byte) (n int, err error) {
53
+ ws.writeMu.Lock()
54
+ defer ws.writeMu.Unlock()
55
+
56
+ err = ws.conn.Send(p)
57
+ if err != nil {
58
+ return 0, err
59
+ }
60
+
61
+ return len(p), nil
62
+}
63
+
64
+// Close closes the WebSocket connection
65
+func (ws *WsStream) Close() error {
66
+ return ws.conn.Close()
67
+}