This is a follow-up on a previous post that described how to run a really simple
server-side WASM application with the Docker Desktop+WASM Tech Preview.
This was your typical Hello, World!
example. This time I'm going for a simple echoing TCP server.
Building the web server in Rustโ
Since I'm quite new to Rust, I followed this nice tutorial on how to build a REST API in Rust. It is based on Actix Web and it offers an intuitive way of constructing a REST API.
After finishing the tutorial and some local testing to demonstrate it 'worked on my machine' (you can probably guess now what comes next),
I tried to compile it to the wasm32-wasi
target:
rwwilden@LAPTOP-FMQ1F4IR:~/projects/rust_wasm_webserver$ cargo build --target wasm32-wasi --release
Downloaded wasm-bindgen-shared v0.2.83
...
Downloaded 8 crates (420.5 KB) in 0.55s
Compiling autocfg v1.1.0
Compiling cfg-if v1.0.0
...
Compiling socket2 v0.4.7
error[E0583]: file not found for module `sys`
--> /home/rwwilden/.cargo/registry/src/github.com-1ecc6299db9ec823/socket2-0.4.7/src/lib.rs:124:1
|
124 | mod sys;
| ^^^^^^^^
|
= help: to create the module `sys`, create file "/home/rwwilden/.cargo/registry/src/github.com-1ecc6299db9ec823/socket2-0.4.7/src/sys.rs" or "/home/rwwilden/.cargo/registry/src/github.com-1ecc6299db9ec823/socket2-0.4.7/src/sys/mod.rs"
error: Socket2 doesn't support the compile target
--> /home/rwwilden/.cargo/registry/src/github.com-1ecc6299db9ec823/socket2-0.4.7/src/lib.rs:127:1
|
127 | compile_error!("Socket2 doesn't support the compile target");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
As you can see, this fails miserably. After a bit of Googling, it turns out that the socket2
crate fails to compile
because the sys
module is only imported for a unix
or windows
target. Because of my still limited knowledge of Rust
I'm not 100% sure what that means but anything that depends on socket2
won't compile to WASM. These
two GitHub issues provide further details.
So we need another way to open a socket via WASI in WASM.
wasmedge_wasi_socket
to the rescueโ
The creators of WasmEdge also built a crate called wasmedge_wasi_socket
that allows binding to a socket.
I'm not sure how that works though. WASI sockets is still in the
WASI Feature Proposal state so
by no means standardized. But let's give it a try ๐
There's a nice tutorial for implementing a
simple TCP server that echoes back the request you send it. I just followed along, the source code can be found
here. Note that wasmedge_wasi_socket
exposes
the same types as std::net
.
Compiling it to the wasm32-wasi
target and a subsequent AOT compile work fine now:
cargo build --target wasm32-wasi --release
wasmedgec target/wasm32-wasi/release/rust_wasm_webserver.wasm rust_wasm_webserver.cwasm
Dockerize and run itโ
Let's define the Dockerfile:
FROM scratch
ENV PORT=8000
ENV RUST_BACKTRACE=full
ENTRYPOINT [ "/rust_wasm_webserver.cwasm" ]
COPY rust_wasm_webserver.cwasm /rust_wasm_webserver.cwasm
and build an image:
docker build -t rust-wasm-webserver .
We should now be able to run the image:
docker run --name rust-wasm-webserver \
--runtime=io.containerd.wasmedge.v1 \
--platform=wasi/wasm32 \
--publish 8000:8000 \
rust-wasm-webserver
Unfortunately, we get an error message:
Error: Os { code: 6, kind: WouldBlock, message: "Resource temporarily unavailable" }
The reason for this is my attempt to set the non-blocking flag to true
when accepting connections.
The reason for this is my (until now) limited understanding of non-blocking sockets.
This is explained in more detail here and
here.
A non-blocking accept returns immediately with an error EAGAIN
or EWOULDBLOCK
in case there is no client
connecting at that time. You actually see a kind: WouldBlock
in the error message. You typically handle that
by checking for these errors and trying again a little bit later, which is a little bit out-of-scope for
this blog post.
When we run the WASM application with the fix, we get the expected output:
Going to bind to port 8000
Bound to port 8000
Accepted client
Accepted client
Accepted client
Requests can be sent via curl:
C:\Users\rwwil>curl -d "Server-side WASM" -X POST http://127.0.0.1:8000
echo: Server-side WASM