Mongoose is an embedded Webserver, MQTT, WebSocket, SNTP server and a whole lot more. You can find out more about Mongoose on the Mongoose GitHub Repository - cesanta/mongoose: Embedded Web Server (github.com)
WAMR is the Wasm Micro Runtime, it is a WebAssembly runtime designed to run on IoT and embedded devices. Running a WebServer in a WebAssembly sandbox allows us to:
- Provide some additional security, if the server is compromised the sandbox should limit the blast radius.
- Provide portable code; writing the same web interface for multiple devices can be a pain, this web server should be portable between devices.
- Run the webserver for a device on a development machine or other bit of hardware - super handy for debugging and development.
There are a few gotcha's when building network enabled applications for WAMR and for building Mongoose for use on WAMR. This repository will go through the basics of getting these two great open source projects working together.
This repository will show you how to build the sample Mongoose HTTP server example and get it running as an wasm application for WAMR. - If all goes well, then at the end of this readme, you should have a working HTTP server running in a portable .wasm
file.
There is a step by step guide on how to get up and running with WAMR and the WASI SDK on my personal blog. But, to save you time, here's the highlights, assuming we're using Ubuntu, or Ubuntu for WSL (Windows Subsystem for Linux):
-
Install the necessary build packages using
apt-get
:sudo apt-get install build-essential clang cmake ninja-build wget curl git
-
Install WASI SDK; we'll download the tar file extract it and place it in the
/opt
directory:cd ~ wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-21/wasi-sdk-21.0-linux.tar.gz tar -xf wasi-sdk-21.0-linux.tar.gz sudo mkdir /opt/wasi-sdk sudo mv ./wasi-sdk-21.0/* /opt/wasi-sdk/
-
Clone and build the WAMR Repository:
git clone https://github.com/bytecodealliance/wasm-micro-runtime.git cd wasm-micro-runtime mdkir build cd build cmake .. make
The iwasm
runtime for WAMR doesn't typically include thread or socket handling support. Instead you need to build iwasm
with both sockets and pthreads enabled. Doing this is rather straight forward as the WAMR project already includes a set of socket sample applications, and a cmake file which will rebuild iwasm
with all the additional functionality we need. You can find the example applications and the cmake file we need here - Socket API Sample. Simply follow the build instructions and you will end up with a socket enabled iwasm
runtime. But, to save you time, here's the highlights:
-
Find the folder with the socket handling example could in it, and use cmake to build an updated
iwasm
along with all the example applications:cd ~/wasm-micro-runtime/samples/socket-api/ mkdir build cd build cmake .. make
-
Copy the build
iwasm
command line tool, and place it somewhere handy for future reference:mkdir ~/bin cp ./iwasm ~/bin/iwasm
Let's just clone the Mongoose repository:
cd ~
git clone https://github.com/cesanta/mongoose.git
You'll need a customized mongoose_custom.h
which is part of this repository.
cd ~
git clone https://github.com/woodsmc/wamr_with_mongoose.git
In order to build a working example http server we're going to need to collect all the necessary parts together from the three projects we've cloned so far.
-
Let's do this and create a new working directory with our http server code:
cd ~ mkdir wasm_http_server cd wasm_http_server
-
Now let's copy in our custom header:
cp ~/wamr_with_mongoose/mongoose_custom.h .
-
The socket library files from WAMR:
cp ~/wasm-micro-runtime/core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c . cp ~/wasm-micro-runtime/core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h .
-
The Mongoose framework:
cp ~/mongoose/mongoose.h . cp ~/mongoose/mongoose.c .
The Mongoose project used to include an example http server in their repository, however a recent update has removed this. Instead the Mongoose project includes an example HTTP server as a code snippet in their readme.md
file. This is reproduce below for your reference:
#include "mongoose.h" // To build, run: cc main.c mongoose.c
// HTTP server event handler function
void ev_handler(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
struct mg_http_serve_opts opts = { .root_dir = "./web_root/" };
mg_http_serve_dir(c, hm, &opts);
}
}
int main(void) {
struct mg_mgr mgr; // Declare event manager
mg_mgr_init(&mgr); // Initialise event manager
mg_http_listen(&mgr, "http://0.0.0.0:8000", ev_handler, NULL); // Setup listener
for (;;) { // Run an infinite event loop
mg_mgr_poll(&mgr, 1000);
}
return 0;
}
Now that everything is ready, we need to build the http server this can be done with the following command:
/opt/wasi-sdk/bin/clang -I . -o httpserver.wasm ./mongoose.c ./wasi_socket_ext.c ./server.c
This should produce a httpserver.wasm
file in your local directory. If you examine this server it comes in at about 186Kb of code size. If you examine the compiled code you will see that it uses 2 WebAssembly pages of memory or 128kb of Linear memory. Not bad a for a small server.
-rwxr-xr-x 1 mad mad 186K Aug 18 16:01 httpserver.wasm
As you can see from the code above the example webserver needs a web_root
directory which some content to serve. Therefore we need to do the following steps:
-
Create the
web_root
folder with:mkdir web_root
-
Create an example file, called
example.html
which contains the following:<html> <head></head> <body> <h1>Hello</h1> <p>World</p> </body> </html>
When you execute WAMR you have to tell it at the command line what permissions you will grant the running WebAssembly code. For the example WebServer to work you will need to provide the following command line switches:
--dir=.
this tells WAMR to allow the running WASM code to see the current directory. NB: The Server actually provides a listing of the content of theweb_root
subdirectory.--addr-pool=0.0.0.0/15
tells WAMR to allow the running WASM code to use the IP address for local host
~/bin/iwasm --dir=. --addr-pool=0.0.0.0/15 httpserver.wasm
This should give you a working webserver available on localhost at port 8000.