Server-Side WebAssembly with NGINX Unit

by

in
Server-Side WebAssembly with NGINX Unit

WebAssembly (abbreviated to Wasm) has a lot to offer the world of web applications. In the browser, it provides a secure, sandboxed execution environment that enables frontend developers to work in a variety of high-level languages (not just JavaScript!) without compromising on performance. And at the backend (server-side), WebAssembly’s cross-platform support and multi-architecture portability promise to make development, deployment, and scalability easier than ever.

At NGINX, we envision a world where you can create a server-side WebAssembly module and run it anywhere – without modification and without multiple build pipelines. Instead, your WebAssembly module would start at local development and run all the way to mission-critical, multi-cloud environments.

With the release of NGINX Unit 1.31, we’re excited to deliver on this vision. NGINX Unit is a universal web app server where application code is executed alongside the other essential attributes of TLS, static files, and request routing. Moreover, NGINX Unit does all of this while providing a consistent developer experience for seven programming language runtimes, and now also WebAssembly.

Adding WebAssembly to NGINX Unit makes sense on many levels:

  • HTTP’s request-response pattern is a natural fit with the WebAssembly sandbox’s input/output (I/O) byte stream.
  • Developers can enjoy high-level language productivity without compromising on runtime performance.
  • NGINX Unit’s request router can facilitate construction of complex applications from multiple WebAssembly modules.
  • WebAssembly’s fast startup time makes it equally suitable for deploying single microservices and functions, or even full-featured applications.
  • Universal portability and cross-platform compatibility enables local development without complex build pipelines.
  • NGINX Unit already provides per-application isolation and the WebAssembly sandbox makes it even safer to run untrusted code.

Note: At the time of this writing, WebAssembly module is a Technology Preview – more details below.

How Does the NGINX Unit WebAssembly Module Work?

NGINX Unit’s architecture decouples networking protocols from the application runtime. The unitd: router process handles the incoming HTTP request, taking care of the TLS layer as required. After deciding what to do with this request, the “HTTP context” (URI, headers, and body) is then passed to the application runtime.

Many programming languages have a precise specification for how the HTTP context is made available to the application code, and how a developer can access URI, headers, and body. NGINX Unit provides several language modules that implement an interface layer between NGINX Unit’s router and the application runtime.

The WebAssembly language module for NGINX Unit provides a similar interface layer between the WebAssembly runtime and the router process. The WebAssembly sandbox’s linear memory is initialized with the HTTP context of the current request and the finalized response is sent back to the router for transmission to the client.

The sandboxed execution environment is provided by the Wasmtime runtime. The diagram below illustrates the flow of an HTTP request from client, through the router, to the WebAssembly module executed by Wasmtime.

Running WebAssembly Modules on NGINX Unit

Configuring NGINX Unit to execute a WebAssembly module is as straightforward as for any other language. In the configuration snippet below, there is an application called helloworld with these attributes:

  • type defines the language module to be loaded for this application
  • module points to a compiled WebAssembly bytecode
  • access is a feature of the Wasmtime runtime that enables the application to access resources outside of the sandbox
  • request_handler, malloc_handler, and free_handler relate to the SDK functions that transfer the HTTP context to Wasmtime (more on that in the next section)
{
“applications”:{
“helloworld”:{
“type”:”wasm”,
“module”:”/path/to/wasm_module.wasm”,
“access”:{
“filesystem”:[
“/tmp”,
“/var/tmp”
]
},
“request_handler”:”luw_request_handler”,
“malloc_handler”:”luw_malloc_handler”,
“free_handler”:”luw_free_handler”
}
}
}

Finding HTTP Context Inside the WebAssembly Sandbox

As mentioned above, NGINX Unit’s WebAssembly language module initializes the WebAssembly execution sandbox with the HTTP context of the current request. Where many programming language runtimes would provide native, direct access to the HTTP metadata, no such standard exists for WebAssembly.

We expect the WASI-HTTP standard to ultimately satisfy this need but, in the meantime, we provide a software development kit (SDK) for Rust and C. The Unit-Wasm SDK makes it easy to write web applications and APIs that compile to WebAssembly and run on NGINX Unit. In our how-to guide for WebAssembly, you can explore the development environment and build steps.

Despite our vision and desire to realize WebAssembly’s potential as a universal runtime, applications built with this SDK will only run on NGINX Unit. This is why we introduce WebAssembly support as a Technology Preview – we expect to replace it with WASI-HTTP support as soon as that is possible.

Try the Technology Preview

The Technology Preview is here to showcase the potential for server-side WebAssembly while providing a lightweight server for running web applications. Please approach it with a “kick the tires” mindset – experiment with it and provide us with feedback. We’d love to hear from you on the NGINX Community Slack or through the NGINX Unit GitHub repo.

To get started, install NGINX Unit and jump to the how-to guide for WebAssembly