Can Actix-Web Serve a .Wasm File with the Logic?
Yes, Actix-web can serve a .wasm file, but there are a few nuances when it comes to integrating the logic within the WebAssembly file.
While serving static files like images and scripts is straightforward, serving and running a .wasm file containing logic requires a bit more setup. Here’s how you can serve and execute logic from a WebAssembly (.wasm) file using Actix-web.
Overview:
- Serving the .wasm File: You can use Actix-web to serve the .wasm file as a static file.
- Executing Logic in .wasm: To run the logic embedded in the .wasm file, you will need a WebAssembly runtime on the server side. Popular runtimes include
wasmer
orwasmtime
.
This guide demonstrates how to set up an Actix-web server to serve a .wasm file and execute a function within that file, using wasmer
to run the WebAssembly logic.
https://docs.rs/wasmer/5.0.4/wasmer/index.html
Step-by-Step Setup
- Add Dependencies in
Cargo.toml
:
[dependencies]
actix-web = "4.0"
tokio = { version = "1", features = ["full"] }
wasmer = "2.0"
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
- Create the Actix-web Server (
main.rs
):
use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use actix_files::Files;
use wasmer::{Store, Module, Instance, imports};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
#[derive(Deserialize)]
struct Property {
property_value: f64,
}
// A simple struct to wrap the store and module for Wasmer
struct WasmEnv {
store: Store,
module: Module,
}
// Serve the Wasm file
async fn serve_wasm() -> impl Responder {
HttpResponse::Ok()
.content_type("application/wasm")
.body(include_bytes!("sdlt_tool.wasm").to_vec()) // Serve the wasm file
}
// Handle POST request for SDLT calculation
async fn calculate(property: web::Json<Property>, wasm_env: web::Data<Arc<WasmEnv>>) -> impl Responder {
let result = run_wasm_calculation(wasm_env, property.property_value);
match result {
Ok(sdlt) => HttpResponse::Ok().json(sdlt),
Err(_) => HttpResponse::InternalServerError().body("Error calculating SDLT"),
}
}
// Function to execute Wasm calculation
fn run_wasm_calculation(wasm_env: web::Data<Arc<WasmEnv>>, property_value: f64) -> Result<f64, String> {
let import_object = imports! {}; // Can be extended if there are required imports
let mut instance = match Instance::new(&wasm_env.store, &wasm_env.module, &import_object) {
Ok(inst) => inst,
Err(_) => return Err("Failed to instantiate Wasm module".to_string()),
};
let calculate_sdlt = match instance.exports.get_function("calculate_sdlt") {
Ok(func) => func,
Err(_) => return Err("Failed to find the calculate_sdlt function".to_string()),
};
let result = match calculate_sdlt.call(&mut instance, &[property_value.into()]) {
Ok(res) => res[0].to_f64().unwrap_or(0.0),
Err(_) => return Err("Error executing Wasm function".to_string()),
};
Ok(result)
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
// Load the Wasm module
let wasm_bytes = std::fs::read("sdlt_tool.wasm").expect("Unable to read .wasm file");
let store = Store::default();
let module = Module::new(&store, wasm_bytes).expect("Failed to load Wasm module");
// Wrap Wasm env in Arc for sharing between handlers
let wasm_env = Arc::new(WasmEnv { store, module });
// Start the Actix web server
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(wasm_env.clone())) // Pass the Wasm environment
.route("/calculate", web::post().to(calculate))
.route("/sdlt_tool.wasm", web::get().to(serve_wasm)) // Serve the .wasm file
})
.bind("127.0.0.1:8080")?
.run()
.await
}
Explanation:
- Serving the .wasm File:
- The
serve_wasm
handler serves the WebAssembly file when you access the/sdlt_tool.wasm
endpoint. This file can be fetched by a client-side application for execution in the browser or another WebAssembly runtime.
- The
- Handling the SDLT Calculation:
- The
calculate
handler receives a POST request with the property value, executes the SDLT calculation using the WebAssembly logic inside the .wasm file, and returns the result.
- The
- Running the Wasm Module:
- The
wasmer
library is used to load and execute the WebAssembly module on the server. Thecalculate_sdlt
function inside the Wasm module is called with the provided property value, and the result is returned to the client.
- The
How to Test:
- Start the Server:
Run the Rust project usingcargo run
. - Access the .wasm File: Open your browser and navigate to
http://localhost:8080/sdlt_tool.wasm
to fetch the .wasm file. - Send a POST Request: Send a POST request to
http://localhost:8080/calculate
with a JSON body like:{ "property_value": 1000000.0 }
You will receive the result as a JSON response, for example:{ "result": 25000.0 }
Advantages of This Approach:
- Self-contained Server Logic:
The server can now run the WebAssembly logic directly without needing an external JavaScript runtime. This keeps everything on the backend. - Serving .wasm Files and Handling Execution:
The server can both serve the .wasm file to clients and execute it on the server side usingwasmer
.
This approach provides a neat integration of WebAssembly with Actix-web, allowing you to serve and execute WebAssembly logic directly within your Rust server.