WASM + WASI + WAGI + Web Assembly Modules in Rust

B Shyam Sundar
5 min readJul 3, 2022

--

Title Image

Introduction

This is a 3 part series, in this article we will look at how we can simple program in rust that can respond to CGI invocations.

In Part 2 we will look at concepts a bit more advanced such how an external web assembly module can directly using wasm-to-oci.

In Part 3 we will look at how we can deploy and execute our web assembly modules in an Kubernetes cluster using krustlet.

Prerequisites (For this part)

  1. Rust (1.60+)

Let us begin:

WASM — Web Assembly

What is Web Assembly? To draw a quote from WebAssembly’s creator Luke Wagner, he says:

WebAssembly… defines a portable, size- and load-time-efficient format and execution model specifically designed to serve as a compilation target for the Web.

Original goal of WebAssembly amongst a variety of use cases including cloud applications. WebAssembly can be executed in

Command lineWasmEdge, Wamr, WasmTime

Embeddedwasm3

Programming Languageswasmer

A comparison view of WebAssembly can be pictured below:

Pic courtesy: wasmCloud from Kubecon 2021 WasmDay WebAssembly: The Future of Distributed Computing by Liam Randall.

By default, WebAssembly modules cannot access external resources which means no access to files, no environment variables and no access to network connections. So your default WebAssembly is quite limited in what it can do.

A group of specifications developed for non-browser web assembly modules to securely communicate to a select few system resources. And that is:

WASI — Web Assembly System Interface

WASI provides the means for a WebAssembly module to access the files / folders of the system , environment variables and system’s random generator amongst other things.

In the future WASI would support a wide variety of features which are implemented through proposals. In the future WASM will support threading, which would enable us to write low level socket code and in turn networking.

But lack of full fledged networking support doesn’t stop us from creating modules that respond to HTTP requests. Enter…

WAGI — Web Assembly Gateway Interface

Gateway Interfaces are actually one of the oldest web development techniques. The WAGI can respond to HTTP requests with existing WASI feature by use of Common Gateway Interface (CGI).

WAGI conforms to the CGI 1.1 spec (RFC 3875).

A brief view of how CGI works:

Basic CGI Flow

After web server receives a request, it parses the headers and body and invokes the mapped CGI program. The headers are stored to environment variables. The body content is passed to the CGI program via STDIN. The CGI program runs and prints to STDOUT. The contents printed to STDOUT is sent back to the browser by the server. In case of error, the contents written to STDERR file handle is sent to error log.

The key point to note is that the CGI did not require access to network for responding to a HTTP request.

Example

Before we can create a web assembly module, we need to install a few dependencies. First, add wasm32-wasi target to rust using the following command. We also will use a tool called “just” to simplify the build process.

#!/bin/bashrustup target add wasm32-wasi

To install just run the following command

#!/bin/bashcargo install just

We need to install WAGI server executable to run our web assembly module.

You can download the latest version of wagi here.

For wagi to execute a module file is required. Run the following commands

#!/bin/bashmkdir hello-wagi
cd hello-wagi
touch Cargo.toml modules.toml justfile

Paste the following contents to Cargo.toml

./Cargo.toml[workspace]
members = [
"level1",
]

Paste the following contents to modules.toml

./modules.toml[[module]]
route = "/level1-ping"
module = "./target/wasm32-wasi/debug/level1.wasm"

Create a new rust program using

#!/bin/bashcargo new --name level1 --bin

Replace the contents of the main.rs file with

./main.rsfn main() {
println!("Content-Type: text/plain");
println!("");
println!("pong");
}

Copy the following contents to “justfile

./justfileset dotenv-loadrun sample: (build sample "--target wasm32-wasi")
wagi -c modules.toml --log-dir ./logs

Now to run the program with the following commands

#!/bin/bashjust run level1

If all goes well then you should see an output similar to

run command output

This command actually builds the project using cargo build command targetting wasm32-wasi. Then it uses the wagi command to run the web assembly.

Now go to your browser and navigate to the URL “127.0.0.1:3000/level1-ping” and voila, you should see

level 1 output

Wow! The simplicity of this and yet the wide implications of the output cannot be justified within a single article.

Let us have a peek at the network tab

network tab

Notice that the content-type is the one which we mentioned in the code.

In our next part will see how we can serve requests directly by loading web assembly module code stored in external references using an exceptional tool called wasm-to-oci.

References

Introduction to WAGI | Slides + Coverage (fettblog.eu)

https://youtu.be/9NDwHBjLlhQ — Very useful video and the main influence for this article

deislabs/wagi: Write HTTP handlers in WebAssembly with a minimal amount of work (github.com)

Wasm, WASI, Wagi: What are they? | Fermyon Technologies (@FermyonTech)

thangchung/webassembly-tour: ⚙️ Take you through a tour of WebAssembly (WASM targets on WASI) with wasmCloud, Krustlet, WAGI, etc. 🌟 Give it a star if you like it. (github.com)

--

--