Wasm

Use Wasm middleware in your HTTP pipeline

WebAssembly is a way to safely run code compiled in other languages. Runtimes execute WebAssembly Modules (Wasm), which are most often binaries with a .wasm extension.

The Wasm HTTP middleware allows you to manipulate an incoming request or serve a response with custom logic compiled to a Wasm binary. In other words, you can extend Dapr using external files that are not pre-compiled into the daprd binary. Dapr embeds wazero to accomplish this without CGO.

Wasm binaries are loaded from a URL. For example, the URL file://rewrite.wasm loads rewrite.wasm from the current directory of the process. On Kubernetes, see How to: Mount Pod volumes to the Dapr sidecar to configure a filesystem mount that can contain Wasm modules. It is also possible to fetch the Wasm binary from a remote URL. In this case, the URL must point exactly to one Wasm binary. For example:

  • http://example.com/rewrite.wasm, or
  • https://example.com/rewrite.wasm.

Component format

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: wasm
spec:
  type: middleware.http.wasm
  version: v1
  metadata:
  - name: url
    value: "file://router.wasm"
  - guestConfig
    value: {"environment":"production"}

Spec metadata fields

Minimally, a user must specify a Wasm binary implements the http-handler. How to compile this is described later.

Field Details Required Example
url The URL of the resource including the Wasm binary to instantiate. The supported schemes include file://, http://, and https://. The path of a file:// URL is relative to the Dapr process unless it begins with /. true file://hello.wasm, https://example.com/hello.wasm
guestConfig An optional configuration passed to Wasm guests. Users can pass an arbitrary string to be parsed by the guest code. false environment=production,{"environment":"production"}

Dapr configuration

To be applied, the middleware must be referenced in configuration. See middleware pipelines.

apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
  name: appconfig
spec:
  httpPipeline:
    handlers:
    - name: wasm
      type: middleware.http.wasm

Note: WebAssembly middleware uses more resources than native middleware. This result in a resource constraint faster than the same logic in native code. Production usage should Control max concurrency.

Generating Wasm

This component lets you manipulate an incoming request or serve a response with custom logic compiled using the http-handler Application Binary Interface (ABI). The handle_request function receives an incoming request and can manipulate it or serve a response as necessary.

To compile your Wasm, you must compile source using a http-handler compliant guest SDK such as TinyGo.

Here’s an example in TinyGo:

package main

import (
	"strings"

	"github.com/http-wasm/http-wasm-guest-tinygo/handler"
	"github.com/http-wasm/http-wasm-guest-tinygo/handler/api"
)

func main() {
	handler.HandleRequestFn = handleRequest
}

// handleRequest implements a simple HTTP router.
func handleRequest(req api.Request, resp api.Response) (next bool, reqCtx uint32) {
	// If the URI starts with /host, trim it and dispatch to the next handler.
	if uri := req.GetURI(); strings.HasPrefix(uri, "/host") {
		req.SetURI(uri[5:])
		next = true // proceed to the next handler on the host.
		return
	}

	// Serve a static response
	resp.Headers().Set("Content-Type", "text/plain")
	resp.Body().WriteString("hello")
	return // skip the next handler, as we wrote a response.
}

If using TinyGo, compile as shown below and set the spec metadata field named “url” to the location of the output (for example, file://router.wasm):

tinygo build -o router.wasm -scheduler=none --no-debug -target=wasi router.go`

Wasm guestConfig example

Here is an example of how to use guestConfig to pass configurations to Wasm. In Wasm code, you can use the function handler.Host.GetConfig defined in guest SDK to get the configuration. In the following example, the Wasm middleware parses the executed environment from JSON config defined in the component.

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: wasm
spec:
  type: middleware.http.wasm
  version: v1
  metadata:
  - name: url
    value: "file://router.wasm"
  - guestConfig
    value: {"environment":"production"}

Here’s an example in TinyGo:

package main

import (
	"encoding/json"
	"github.com/http-wasm/http-wasm-guest-tinygo/handler"
	"github.com/http-wasm/http-wasm-guest-tinygo/handler/api"
)

type Config struct {
	Environment string `json:"environment"`
}

func main() {
	// get config bytes, which is the value of guestConfig defined in the component.
	configBytes := handler.Host.GetConfig()
	
	config := Config{}
	json.Unmarshal(configBytes, &config)
	handler.Host.Log(api.LogLevelInfo, "Config environment: "+config.Environment)
}

Last modified July 19, 2024: Fixed typo in links (#4267) (32fce1d)