Skip to content

Latest commit

 

History

History

hello-wagi

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 

Running WASI binaries as HTTP handlers with WAGI

WebAssembly Gateway Interface (WAGI) allows you to run WebAssembly WASI binaries as HTTP handlers. Write a command line application that prints a few headers, and compile it to wasm32-wasi. Add an entry to a modules.toml file matching URL to Wasm module. That's it. You can use any programming language that can compile to wasm32-wasi.

Headers are placed in environment variables. Query parameters, when present, are sent in as command line options. Incoming HTTP payloads are sent in via STDIN. And the response is simply written to STDOUT.

Wagi

Prerequisites

Install WAGI

Get the latest from here: https://github.com/deislabs/wagi/releases

Untar:

tar -zxf wagi-v0.8.1-macos-amd64.tar.gz

Move to /usr/local/bin:

sudo mv ./wagi /usr/local/bin/wagi

Create a .NET app for WAGI

For this sample, let's use a .NET console app.

dotnet new console -n HelloWagi

Add Wasi.Sdk package:

cd HelloWagi
dotnet add package Wasi.Sdk --prerelease

Change Program.cs to print the following:

Console.WriteLine("Content-Type: text/plain");
Console.WriteLine("Status: 200");
Console.WriteLine();
Console.WriteLine("Hello WAGI from C#!");

// Headers are placed in environment variables
var envVars = Environment.GetEnvironmentVariables();
Console.WriteLine($"### Environment variables: {envVars.Keys.Count} ###");
foreach (var variable in envVars.Keys)
{
    Console.WriteLine($"{variable} = {envVars[variable]}");
}

Build:

dotnet build

  HelloWagi -> /Users/atamel/dev/github/meteatamel/wasm-basics/samples/hello-wagi/HelloWagi/bin/Debug/net8.0/HelloWagi.wasm

Run in a Wasm runtime:

wasmtime ./bin/Debug/net8.0/HelloWagi.wasm

Content-Type: text/plain
Status: 200

Hello WAGI from C#!
### Environment variables: 1 ###
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT = true

Create a Go app for WAGI

For the next sample, let's use a Go console app.

Create hello-wagi.go to print the following:

package main

import (
  "fmt"
  "io/ioutil"
  "os"
)

func main() {

  fmt.Println("Content-Type: text/plain")
  fmt.Println("Status: 200")
  fmt.Println()
  fmt.Println("Hello WAGI from Go!")

  // Headers are placed in environment variables
  envVars := os.Environ()
  fmt.Printf("### Environment variables: %d ###\n", len(envVars))
  for _, envVar := range envVars {
    fmt.Println(envVar)
  }

  // Query parameters are sent in as command line options
  args := os.Args[1:]
  fmt.Printf("### Query parameters: %d ###\n", len(args))
  for _, arg := range args {
    fmt.Printf("Argument=%s\n", arg)
  }

  // Incoming HTTP payloads are sent in via STDIN
  fmt.Println("### HTTP payload ###")
  payload, err := ioutil.ReadAll(os.Stdin)
  if err != nil {
    fmt.Println("Error reading payload:", err)
    return
  }
  fmt.Println(string(payload))
}

Build:

tinygo build -target=wasi hello-wagi.go

Run in a Wasm runtime:

wasmtime ./hello-wagi.wasm

Content-Type: text/plain

Hello WAGI from Go!

Run as a WAGI module

To run these samples with WAGI, create a simple modules.toml file that maps paths to Wasm modules:

[[module]]
route = "/csharp"
module = "HelloWagi/bin/Debug/net8.0/HelloWagi.wasm"

[[module]]
route = "/go"
module = "hello-wagi.wasm"

Run as a WAGI module:

wagi -c modules.toml

Ready: serving on http://127.0.0.1:3000

In a separate terminal, you can curl different paths to hit different modules:

curl http://localhost:3000/csharp

Hello WAGI from C#!
### Environment variables: 23 ###
SERVER_PORT = 3000
REQUEST_METHOD = GET
X_MATCHED_ROUTE = /csharp
...
curl -X POST http://127.0.0.1:3000/go\?arg1=value1 -d 'Hello World'

Hello WAGI from Go!
### Environment variables: 24 ###
HTTP_CONTENT_LENGTH=11
X_RAW_PATH_INFO=
REMOTE_USER=
PATH_INFO=
PATH_TRANSLATED=
HTTP_USER_AGENT=curl/7.88.1
SCRIPT_NAME=/go
...
### Query parameters: 1 ###
Argument=arg1=value1
### HTTP payload ###
Hello World

References