go-wasm-http-server

repository·master·Indexed 19 days ago

https://github.com/nlepage/go-wasm-http-server

A Go library that allows developers to embed Go HTTP handlers into a browser-based ServiceWorker using WebAssembly. It enables running a Go-based web server entirely within the client's browser, providing tools to convert JavaScript Requests to Go *http.Request and a Response interface that implements http.ResponseWriter. Supports compilation via the standard Go compiler or TinyGo for smaller WASM binaries.

Tokens
3.3K
Snippets
15
Records
17
Agent score
64%

What's inside go-wasm-http-server

  1. Requirements for using go-wasm-http-server

    master

    To use go-wasm-http-server, your Go application must be compatible with WebAssembly (WASM). Ensure your code meets the following criteria:

    • No C bindings: The code must be pure Go or compatible with WASM targets.
    • No System dependencies: Your code cannot rely on the local file system or network (e.g., it cannot connect to a local database server).
    • TinyGo Compatibility (Optional): For smaller WASM binaries, you may want to use TinyGo.
  2. Create a ServiceWorker file (sw.js)

    master

    Your ServiceWorker must import the wasm_exec.js file corresponding to the version of Go or TinyGo used to compile your WASM binary, followed by the go-wasm-http-server library and a call to registerWasmHTTPListener().

    Important: The version in the importScripts URL must match your compiler version.

    // Note the 'go1.23.4' below, which must match your 'go version' output:
    importScripts('https://cdn.jsdelivr.net/gh/golang/go@go1.23.4/misc/wasm/wasm_exec.js')
    
    // OR if using TinyGo:
    // importScripts('https://cdn.jsdelivr.net/gh/tinygo-org/tinygo@0.35.0/targets/wasm_exec.js')
    
    importScripts('https://cdn.jsdelivr.net/gh/nlepage/go-wasm-http-server@v2.2.1/sw.js')
    
    registerWasmHTTPListener('path/to/server.wasm')
  3. Register the ServiceWorker in your web page

    master

    Register the ServiceWorker in your HTML/JavaScript to enable the emulated server. By default, the server is scoped to the ServiceWorker's directory.

    <script>
      // If your sw.js is in a 'server/' directory:
      navigator.serviceWorker.register('server/sw.js')
    </script>

    Once registered, you can fetch resources from the emulated server:

    // The server will receive a request for "/path/to/resource"
    fetch('server/path/to/resource').then(res => {
      // use response...
    })
  4. Configure wasm_exec.js for TinyGo ServiceWorker

    master

    When using TinyGo, your ServiceWorker (sw.js) must use the wasm_exec.js file that matches your specific TinyGo version. If the versions do not match, the WASM execution will fail.

    To ensure compatibility, check your TinyGo version using tinygo version and then import the corresponding script via a CDN like JSDelivr. For example, if your version is 0.35.0, your sw.js should include:

    importScripts('https://cdn.jsdelivr.net/gh/tinygo-org/tinygo@0.35.0/targets/wasm_exec.js')
  5. Replace http.ListenAndServe with wasmhttp.Serve

    master

    To run your Go HTTP server inside a ServiceWorker, you must replace the standard net/http listener with wasmhttp.Serve(). It is recommended to use Go build tags to maintain compatibility with both standard Go environments and WebAssembly environments.

    //go:build js && wasm
    
    package main
    
    import (
        wasmhttp "github.com/nlepage/go-wasm-http-server/v2"
    )
    
    func main() {
        // Define handlers...
    
        wasmhttp.Serve(nil)
    }
  6. Build your Go application to WebAssembly

    master

    Compile your Go code to a .wasm file using the js/wasm target. You can use either the standard Go compiler or TinyGo.

    # To compile with Go
    GOOS=js GOARCH=wasm go build -o server.wasm .
    
    # To compile with TinyGo, if your code is compatible
    GOOS=js GOARCH=wasm tinygo build -o server.wasm  .
  7. Compile to WASM using TinyGo

    master

    You can use TinyGo to compile go-wasm-http-server to produce significantly smaller WASM blobs compared to the standard Go compiler. Note that this comes with a reduced standard library and potential bugs (see TinyGo issue #1140).

    To compile the project, set GOOS to js and GOARCH to wasm, then run the tinygo build command targeting an output file (e.g., api.wasm).

    GOOS=js GOARCH=wasm tinygo build -o api.wasm  .
  8. Troubleshooting: WebSockets and SSE

    master

    WebSockets

    WebSockets are not supported because Service Workers cannot intercept WebSocket connections.

    Server-Sent Events (SSE)

    Server-Sent Events (SSE) are supported as an alternative to WebSockets for streaming data from the WASM server to the client.

  9. Use registerWasmHTTPListener in JavaScript

    master

    The registerWasmHTTPListener(wasmUrl, options) function instantiates and runs the WebAssembly module at wasmUrl and registers a fetch listener to forward requests to the WASM server.

    Note: This function must be called only once per ServiceWorker. To run multiple servers, use multiple ServiceWorkers.

    // Example usage:
    registerWasmHTTPListener('path/to/server.wasm', {
      base: '/api',
      cacheName: 'wasm-cache',
      args: ['arg1', 'arg2'],
      passthrough: (request) => false
    });
  10. Reference: registerWasmHTTPListener options

    master

    The options object for registerWasmHTTPListener supports the following keys:

    {
      /** @type {string} Base path of the server, relative to the ServiceWorker's scope. */
      base: 'string',
    
      /** @type {string} Name of the Cache to store the WebAssembly binary. */
      cacheName: 'string',
    
      /** @type {string[]} Arguments for the WebAssembly module. */
      args: ['string'],
    
      /** @type {(request: Request): boolean} Optional callback to allow passing the request through to network. */
      passthrough: '(request: Request) => boolean'
    }
  11. Use the Response interface to write HTTP responses

    master

    The Response interface is the primary way to construct and send HTTP responses from a WebAssembly module. It implements standard Go interfaces including http.ResponseWriter, io.StringWriter, http.Flusher, and io.Closer.

    Key capabilities:

    • Standard HTTP methods: Use Header(), Write([]byte), and WriteHeader(int) to control the response.
    • Automatic Content-Type: If you don't set a Content-Type header, the response will attempt to detect it based on the first 512 bytes written.
    • Error Handling: Use WriteError(string) to log an error and send a 500 Internal Server Error response if a header hasn't been sent yet.
    • JavaScript Integration: Call JSValue() to retrieve a js.Value (a Promise) that resolves when the response is finalized, allowing the JavaScript side to react to the completed response.
    // Initialize a new response
    resp, err := wasmhttp.NewResponse()
    if err != nil {
        return err
    }
    
    // Set headers
    resp.Header().Set("Content-Type", "application/json")
    
    // Write status and body
    resp.WriteHeader(200)
    resp.WriteString(`{"status": "ok"}`)
    
    // Finalize the response
    resp.Close()