feat: add WASM client web server and improve service worker

- Implement serverWorker in main_js.go to start an HTTP server within the WASM client, serving a basic HTML page with server ID - Update service-worker.js to handle fetch events asynchronously, ensure WASM initialization, and provide better error responses - Increase page reload timeout in index.html from 500ms to 1000ms for improved stability after service worker registration This enables the web client to act as a server in the WASM environment, enhancing functionality for proxy and serving capabilities.

lemon-mint committed Oct 31, 2025 at 14:49 UTC 7293e53bdfa8809eeac3cb1b824da272c8f255eb
3 files changed +46 -9
cmd/webclient/index.html
+1 -1
@@ -28,7 +28,7 @@
28 setTimeout(() => {
29 showStatus('Reloading page...', 'loading');
30 location.reload();
31 - }, 500);
31 + }, 1000);
32 } catch (error) {
33 showStatus('Service Worker registration failed: ' + error.message, 'error');
34 }
cmd/webclient/main_js.go
+33 -1
@@ -118,9 +118,10 @@ func main() {
118 jsReq := args[0]
119 return httpjs.ServeHTTPAsyncWithStreaming(&Proxy{}, jsReq)
120 }))
121 -
121 log.Info().Msg("Portal proxy handler registered as __go_jshttp")
122
123 + go serverWorker(rdClient)
124 +
125 if runtime.Compiler == "tinygo" {
126 return
127 }
@@ -128,3 +129,34 @@ func main() {
129 ch := make(chan bool)
130 <-ch
131 }
132 +
133 +func serverWorker(client *sdk.RDClient) {
134 + time.Sleep(time.Second)
135 +
136 + cred := sdk.NewCredential()
137 + ln, err := client.Listen(cred, "WASM-Client-WebServer-"+cred.ID()[:8], []string{"http/1.1"})
138 + if err != nil {
139 + log.Error().Err(err).Msg("Failed to start listener")
140 + return
141 + }
142 + defer ln.Close()
143 +
144 + mux := http.NewServeMux()
145 + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
146 + w.WriteHeader(http.StatusOK)
147 + w.Write([]byte(`<!DOCTYPE html>
148 +<html>
149 +<head>
150 + <title>WASM Client Server</title>
151 +</head>
152 +<body>
153 + <h1>Hello, World! This server is running in a WASM client</h1>
154 + <p>Server ID: ` + cred.ID() + `</p>
155 +</body>
156 +</html>`))
157 + })
158 +
159 + if err := http.Serve(ln, mux); err != nil {
160 + log.Error().Err(err).Msg("Failed to start server")
161 + }
162 +}
cmd/webclient/service-worker.js
+12 -7
@@ -1,8 +1,10 @@
1 // const wasm_exec_URL = "https://cdn.jsdelivr.net/gh/golang/go@go1.25.3/misc/wasm/wasm_exec.js";
2 +global = {};
3 const wasm_exec_URL = "/wasm_exec.js";
4 const wasm_URL = "/main.wasm";
5 importScripts(wasm_exec_URL);
6
7 +
8 async function runWASM() {
9 const go = new Go();
10 const cache = await caches.open("WASM_Cache_v1");
@@ -36,16 +38,21 @@ self.addEventListener('activate', (e) => {
38 e.waitUntil(Activate());
39 });
40
39 -self.addEventListener('fetch', (e) => {
41 +self.addEventListener('fetch', async (e) => {
42 console.log(e.request);
41 - if (typeof __go_jshttp != 'undefined') {
42 - e.respondWith((async () => {
43 +
44 + if (typeof __go_jshttp == 'undefined') {
45 + await runWASM();
46 + }
47 +
48 + if (__go_jshttp) {
49 + e.respondWith((async () => {
50 try {
51 const resp = await __go_jshttp(e.request);
52 return resp;
53 } catch {
54 __go_jshttp = undefined;
48 - runWASM();
55 + await runWASM();
56 const resp = await __go_jshttp(e.request);
57 return resp;
58 }
@@ -53,7 +60,5 @@ self.addEventListener('fetch', (e) => {
60 return;
61 }
62
56 - console.log("__go_jshttp not found");
57 - runWASM();
58 - e.respondWith(fetch(e.request));
63 + e.respondWith(new Response("Sorry, Service Worker failed to process the request. Please refresh the page."));
64 });
\ No newline at end of file