For a while, some WASM runtimes implement the Socket specification. So, let’s see how to code an HTTP server directly with WASM. First, WebAssembly (WASM)…
First, WebAssembly (WASM) is a compilation target. Initially, designed for the browser (to augment JavaScript). This binary format is optimized for the size of the produced file and speed execution.
A WASM runtime executes the WAM module (the compiled file) in an isolated sandbox (with no access to the resources of the host computer unless it’s explicitly allowed).
From a browser perspective, the WASM runtime is the JavaScript VM.
Since 2019, WebAssembly is moving outside the browser. It’s the reason why a new standard was created (the specification is a work in progress): WASI (WebAssembly System Interface). WASI is an API for the WASM runtimes to define how to provide access to the host resources by the WASM modules.
Now runtimes can take several forms:
Applications that can load the WASM modules as plugins
CLI applications like WasmEdge, WasmTime, or Wasmer, (and the others). These Command line runtimes execute a wasm module regardless of the architecture.
As I said, the WASI specification is a work in progress. Every WASM runtime implements the most advanced WebAssembly proposal’s.
If there is no official specification for socket networking, WasmEdge and WasmTime already implement their own POSIX sockets.
So, what does this imply?. It means that we can now start to embed web servers inside the WebAssembly modules! (🖐 Disclaimer! It’s not ready for production).
I will show you two examples of WASM HTTP servers. One with WasmEdge and Rust, the other with WasmTime and the dotNet Core WASM support.
Create a WASM HTTP server with Rust and WasmEdge #
To use it, you need to install the Rust SDK and the WasmEdge Runtime.
Installing the WasmEdge Runtime:
curl -sSf https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install.sh | bash -s -- -v 0.11.1source /home/ubuntu/.wasmedge/env # the installer will give you the appropriate path)# checkwasmedge --version
Then, you can test the WASM service with a curl command:
curl -d 'Bob Morane' -X POST http://127.0.0.1:8080/hello
And, you should get 👋 Hello Bob Morane.
The drawback is that, it seems to work with only WasmEdge; if I try with WasmTime (wich implements the socket networking too), I get an error:
wasmtime target/wasm32-wasi/debug/http-service.wasm --tcplisten localhost:8080Error: failed to run main module `target/wasm32-wasi/debug/http-service.wasm`Caused by: 0: failed to instantiate "target/wasm32-wasi/debug/http-server.wasm" 1: unknown import: `wasi_snapshot_preview1::sock_setsockopt` has not been defined
At KubeCon NA 2022, Docker announced Docker+Wasm technical preview in partnership with WasmEdge. Docker developers can simply build and run a complete Wasm application, thanks to a containerd shim developed in collaboration with Docker and WasmEdge. This shim extracts the Wasm module from the OCI artifact and runs it using the WasmEdge runtime.
To run the service with Docker, type the below command:
docker run -dp 8080:8080 --name=wasmservice --runtime=io.containerd.wasmedge.v1 --platform=wasi/wasm32 wasmservice
You can choose the runtime with the --runtime option, but right now, the option exits only for the WasmEdge runtime.
And now you can call the service like this: curl -d 'Bob Morane' -X POST http://127.0.0.1:8080/hello, and of course you'll get 👋 Hello Bob Morane.
I said that WasmTime implements the socket API, so let’s see how to use it with the WASM support of the dotNet framework.
Create a WASM HTTP server with ASP.Net, CSharp and WasmTime #
I didn’t find clear and understandable (for me) documentation about the socket support with WasmTime, but the Mio project provides an example of a WASM TCP server runnable with WasmTime.
However, when I was at WasmDay at Kubecon Valencia in May 2022, I attended to an awesome presentation by Steve Sanderson from Microsoft: "Bringing WebAssembly to the .NET Mainstream". Steve was running an embedded ASP.Net HTTP server in a wasm module with WasmTime.
cd hellowasmtime bin/Debug/net7.0/hello.wasm --tcplisten localhost:8080
Call the service: curl http://localhost:8080 and you'll get something like this: 👋 Hello, World! 🌍 🖥️: Wasm ⏳: 14:09:12 (UTC). My only regret is that does not yet run with the Docker+Wasm preview.
I tried with WasmEdge: wasmedge bin/Debug/net7.0/hello.wasm, but unsurprisingly it didn't work:
[2022-11-06 14:11:54.358] [error] instantiation failed: incompatible import type, Code: 0x61[2022-11-06 14:11:54.358] [error] Mismatched function type. Expected: FuncType {params{i32 , i32 , i32} returns{i32}} , Got: FuncType {params{i32 , i32} returns{i32}}[2022-11-06 14:11:54.358] [error] When linking module: "wasi_snapshot_preview1" , function name: "sock_accept"[2022-11-06 14:11:54.358] [error] At AST node: import description[2022-11-06 14:11:54.358] [error] At AST node: import section[2022-11-06 14:11:54.358] [error] At AST node: module
It's only a start, but the WASI specification seems to be moving in the right direction (even if the implementations sometimes diverge). It is already possible to offer "nano services" with no dependency other than the runtime. And I find dotNet Core's WASI support particularly impressive (and even more ASP.Net).