mod_wasm
mod_wasm is an Apache Server (httpd) extension module able to run and serve WebAssembly binaries as endpoints.
The first version (v0.1.0) was unveiled at the ApacheCon North America on Oct 3rd, 2022 (see the slides). In addition, a full-detailed article was published at VMware's Wasm Labs page.
β Features
mod_wasm can be useful in the different scenarios:
- Run existing applications from a variety of languages without modification.
- Execute untrusted third-party code in a secure environment without using containers.
- The Wasm capabilities model allows to enable/disable capabilites per HTTP request.
βΆοΈ Quick Demo
- Run the container:
docker run -p 8080:8080 ghcr.io/vmware-labs/httpd-mod-wasm:latest
- Open browser at:
Demo | Wasm Module | URL |
---|---|---|
Drupal | PHP 8.2.0 | http://localhost:8080/drupal |
Drupal (from setup) | PHP 8.2.0 | http://localhost:8080/drupal-10-zero |
WordPress | PHP 7.3.33 | http://localhost:8080/wordpress |
HTTP Request Viewer | Python 3.11 | http://localhost:8080/http-request-viewer |
π Contents
π Overview
The mod_wasm project is composed by two different libraries:
mod_wasm.so
(written in C) acts as the extension module for the Apache Server (httpd).libwasm_runtime.so
(written in Rust) offers a very high-level C-API to manage WebAssembly modules via Wasmtime.
Apache Configuration
To enable mod_wasm in Apache, simply define your <Location>
with the wasm-handler
and the file path to the Wasm binary in httpd.conf
:
LoadModule wasm_module modules/mod_wasm.so
<Location /hello-wasm>
SetHandler wasm-handler
WasmModule /var/www/modules/hello_wasm.wasm
</Location>
mod_wasm supports multiple <Location>
definitions, each of them with its own configuration. In addition, multiple configurations can share the same .wasm file. mod_wasm will automatically cache Wasm modules and use only one instance on memory.
New Directives
To setup and manage WebAssembly binaries and their WASI contexts, mod_wasm offers new directives to the httpd.conf
configuration file:
Directive | Description |
---|---|
WasmModule <path> |
Specifies the Wasm module file path. |
WasmDir <dir> |
Pre-open a host directory for the Wasm context. |
WasmMapDir <map> <dir> |
Pre-open a host directory for the Wasm context and mount into a mapping directory. |
WasmArg <arg> |
Set an argument to be passed to the Wasm module context. |
WasmEnv <env> <value> |
Set an environment variable to be passed to the Wasm module context. |
WasmEnableCGI {On|Off} |
Enable/Disable CGI emulation mode. Default is Off . |
WasmMapCGIFileNames {On|Off} |
Enable/Disable mapping SCRIPT_FILENAME based on WasmMapDirs when WasmEnableCGI is enabled. Default is Off . |
Workflow
mod_wasm plays a role in two different stages of the Apache Server workflow:
- On the boot up sequence, the different
WasmXXX
directives are read fromhttpd.conf
:- When a
WasmModule
directive is found, the Wasm runtime tries to load the given Wasm binary from disk into memory. This is an expensive operation so that is why it is executed only once during the Apache boot up sequence. In addition, a cache is used to store the different Wasm modules, so a specific Wasm module can be shared among different configurations with only one instance loaded into memory. - The remaining
WasmXXX
directives define different configuration aspects. A new configuration instance is created for each<Location>
and it will be later used during execution as a template.
- When a
- On each incoming HTTP request, mod_wasm builds a new WASI context for the already-loaded Wasm binary. Next, the Wasm module is instantiated and the entry point is executed. The
stdout
from the Wasm module is redirected to the HTTP response, and thestderr
is appended to Apache Server's trace (usually at<httpd_dir>/dist/logs/error_log
).
mod_wasm also offers the ability to build a specific execution context per HTTP request. When setting up WasmEnableCGI On
, mod_wasm will pass the HTTP headers as environtment variables to the Wasm module (they will be prefixed as HTTP_
). Also, URL parameters are passed in the environment variable QUERY_STRING
. And finally, the HTTP request body is passed as the stdin to the module.
πΉοΈ Examples
This repo cointains several pre-built WebAssembly examples to play with.
Feel free to explore, modify and crash them!
ποΈ Building mod_wasm
As introduced in the overview, there are two main libraries in the mod_wasm project, being libwasm_runtime.so
a dependency for mod_wasm.so
. So, you might want to build libwasm_runtime.so
first:
- To build
libwasm_runtime.so
, the Wasm management and runtime library, go to wasm_runtime for detailed instructions. - For
mod_wasm.so
, the Apache Server module extension, go to mod_wasm.
π¦ Building the container image
This repository contains all you need to build a local container image. Go to image for detailed instructions.
β οΈ Troubleshooting
modules/mod_wasm.so
into server
Cannot load This is a common error related to LD_LIBRARY_PATH
:
$ httpd
httpd: Syntax error on line XXX of <...>/httpd/dist/conf/httpd.conf:
Cannot load modules/mod_wasm.so into server: libwasm_runtime.so: cannot open shared object file: No such file or directory
Apache is loading modules/mod_wasm.so
but during the process it cannot find libwasm_runtime.so
. Either run Apache with LD_LIBRARY_PATH
pointing to the directory where libwasm_runtime.so
is located, or copy libwasm_runtime.so
to a directory such as /usr/lib
.
π Debugging
To get detailed debugging information about the Wasm execution, run the Apache Server with the following environment variables:
WASMTIME_BACKTRACE_DETAILS=1
RUST_BACKTRACE=full
Also, it is recommended to run Apache in debug mode (-X
option), this means only one process, only one worker, and without detaching from the terminal.
WASMTIME_BACKTRACE_DETAILS=1 RUST_BACKTRACE=full ./httpd -X