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

  1. apiVersion: dapr.io/v1alpha1
  2. kind: Component
  3. metadata:
  4. name: wasm
  5. spec:
  6. type: middleware.http.wasm
  7. version: v1
  8. metadata:
  9. - name: url
  10. value: "file://router.wasm"
  11. - guestConfig
  12. 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.

FieldDetailsRequiredExample
urlThe 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 /.truefile://hello.wasm, https://example.com/hello.wasm
guestConfigAn optional configuration passed to Wasm guests. Users can pass an arbitrary string to be parsed by the guest code.falseenvironment=production,{“environment”:”production”}

Dapr configuration

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

  1. apiVersion: dapr.io/v1alpha1
  2. kind: Configuration
  3. metadata:
  4. name: appconfig
  5. spec:
  6. httpPipeline:
  7. handlers:
  8. - name: wasm
  9. 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:

  1. package main
  2. import (
  3. "strings"
  4. "github.com/http-wasm/http-wasm-guest-tinygo/handler"
  5. "github.com/http-wasm/http-wasm-guest-tinygo/handler/api"
  6. )
  7. func main() {
  8. handler.HandleRequestFn = handleRequest
  9. }
  10. // handleRequest implements a simple HTTP router.
  11. func handleRequest(req api.Request, resp api.Response) (next bool, reqCtx uint32) {
  12. // If the URI starts with /host, trim it and dispatch to the next handler.
  13. if uri := req.GetURI(); strings.HasPrefix(uri, "/host") {
  14. req.SetURI(uri[5:])
  15. next = true // proceed to the next handler on the host.
  16. return
  17. }
  18. // Serve a static response
  19. resp.Headers().Set("Content-Type", "text/plain")
  20. resp.Body().WriteString("hello")
  21. return // skip the next handler, as we wrote a response.
  22. }

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):

  1. 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.

  1. apiVersion: dapr.io/v1alpha1
  2. kind: Component
  3. metadata:
  4. name: wasm
  5. spec:
  6. type: middleware.http.wasm
  7. version: v1
  8. metadata:
  9. - name: url
  10. value: "file://router.wasm"
  11. - guestConfig
  12. value: {"environment":"production"}

Here’s an example in TinyGo:

  1. package main
  2. import (
  3. "encoding/json"
  4. "github.com/http-wasm/http-wasm-guest-tinygo/handler"
  5. "github.com/http-wasm/http-wasm-guest-tinygo/handler/api"
  6. )
  7. type Config struct {
  8. Environment string `json:"environment"`
  9. }
  10. func main() {
  11. // get config bytes, which is the value of guestConfig defined in the component.
  12. configBytes := handler.Host.GetConfig()
  13. config := Config{}
  14. json.Unmarshal(configBytes, &config)
  15. handler.Host.Log(api.LogLevelInfo, "Config environment: "+config.Environment)
  16. }

Last modified March 21, 2024: Merge pull request #4082 from newbe36524/v1.13 (f4b0938)