wasm_sandbox/wrappers/
http_server_impl.rs1use std::path::{Path, PathBuf};
4use std::fs;
5use std::collections::HashMap;
6
7use crate::error::{Error, Result};
8use crate::wrappers::{WrapperGenerator, WrapperSpec, ApplicationType};
9use crate::templates::HTTP_SERVER_TEMPLATE;
10use crate::compiler::{CompilerOptions, BuildProfile, OptimizationLevel};
11use crate::compiler::{CargoCompiler, Compiler};
12
13#[derive(Clone)]
15pub struct HttpServerConfig {
16 pub port: u16,
18
19 pub host: String,
21
22 pub max_body_size: usize,
24
25 pub request_timeout: u64,
27
28 pub cors_enabled: bool,
30
31 pub cors_allowed_origins: Vec<String>,
33
34 pub cors_allowed_methods: Vec<String>,
36
37 pub cors_allowed_headers: Vec<String>,
39
40 pub serve_static: bool,
42
43 pub static_dir: Option<PathBuf>,
45}
46
47impl Default for HttpServerConfig {
48 fn default() -> Self {
49 Self {
50 port: 8080,
51 host: "127.0.0.1".to_string(),
52 max_body_size: 10 * 1024 * 1024, request_timeout: 30,
54 cors_enabled: false,
55 cors_allowed_origins: vec!["*".to_string()],
56 cors_allowed_methods: vec!["GET".to_string(), "POST".to_string(), "PUT".to_string(), "DELETE".to_string()],
57 cors_allowed_headers: vec!["Content-Type".to_string(), "Authorization".to_string()],
58 serve_static: false,
59 static_dir: None,
60 }
61 }
62}
63
64pub struct HttpServerGenerator {
66 config: HttpServerConfig,
68
69 compiler: CargoCompiler,
71}
72
73impl HttpServerGenerator {
74 pub fn new() -> Self {
76 Self {
77 config: HttpServerConfig::default(),
78 compiler: CargoCompiler::new(),
79 }
80 }
81
82 pub fn with_config(config: HttpServerConfig) -> Self {
84 Self {
85 config,
86 compiler: CargoCompiler::new(),
87 }
88 }
89
90 fn generate_cargo_toml(&self, app_name: &str) -> String {
92 format!(
93 r#"[package]
94name = "{}-http-server-wrapper"
95version = "0.1.0"
96edition = "2021"
97
98[lib]
99crate-type = ["cdylib"]
100
101[dependencies]
102wasm-bindgen = "0.2.84"
103wasm-bindgen-futures = "0.4.34"
104js-sys = "0.3.61"
105console_error_panic_hook = "0.1.7"
106serde = {{ version = "1.0.152", features = ["derive"] }}
107serde_json = "1.0.93"
108rmp-serde = "1.1.1"
109log = "0.4.17"
110futures = "0.3.28"
111once_cell = "1.17.1"
112anyhow = "1.0.70"
113hyper = {{ version = "0.14.25", features = ["full"] }}
114tokio = {{ version = "1.27.0", features = ["full"] }}
115http = "0.2.9"
116bytes = "1.4.0"
117base64 = "0.21.0"
118
119[dependencies.web-sys]
120version = "0.3.61"
121features = [
122 "console",
123 "Document",
124 "Element",
125 "HtmlElement",
126 "Window",
127]
128
129[profile.release]
130opt-level = 3
131lto = true
132codegen-units = 1
133"#,
134 app_name
135 )
136 }
137
138 fn render_wrapper_code(&self, spec: &WrapperSpec) -> Result<String> {
140 match &spec.app_type {
141 ApplicationType::HttpServer { port } => {
142 let config = HttpServerConfig {
143 port: *port,
144 ..self.config.clone()
145 };
146
147 let mut context = HashMap::new();
149 context.insert("app_path".to_string(), spec.app_path.to_string_lossy().to_string());
150 context.insert("app_args".to_string(), serde_json::to_string(&spec.arguments)?);
151 context.insert("port".to_string(), port.to_string());
152 context.insert("host".to_string(), config.host);
153 context.insert("max_body_size".to_string(), config.max_body_size.to_string());
154 context.insert("request_timeout".to_string(), config.request_timeout.to_string());
155 context.insert("cors_enabled".to_string(), config.cors_enabled.to_string());
156 context.insert("cors_allowed_origins".to_string(), serde_json::to_string(&config.cors_allowed_origins)?);
157 context.insert("cors_allowed_methods".to_string(), serde_json::to_string(&config.cors_allowed_methods)?);
158 context.insert("cors_allowed_headers".to_string(), serde_json::to_string(&config.cors_allowed_headers)?);
159
160 let mut template = HTTP_SERVER_TEMPLATE.to_string();
162
163 for (key, value) in context {
165 let placeholder = format!("{{{{ {} }}}}", key);
166 template = template.replace(&placeholder, &value);
167 }
168
169 Ok(template)
170 }
171 _ => Err(Error::WrapperGeneration {
172 reason: "Not an HTTP server application".to_string(),
173 wrapper_type: Some("http_server".to_string()),
174 }),
175 }
176 }
177}
178
179impl WrapperGenerator for HttpServerGenerator {
180 fn generate_wrapper(&self, spec: &WrapperSpec) -> Result<String> {
181 self.render_wrapper_code(spec)
182 }
183
184 fn compile_wrapper(&self, code: &str, output_path: &Path) -> Result<()> {
185 let temp_dir = tempfile::tempdir()?;
187 let project_dir = temp_dir.path();
188
189 let src_dir = project_dir.join("src");
191 fs::create_dir_all(&src_dir)?;
192
193 fs::write(src_dir.join("lib.rs"), code)?;
195
196 let app_name = "http_server_app";
198 let cargo_toml = self.generate_cargo_toml(app_name);
199 fs::write(project_dir.join("Cargo.toml"), cargo_toml)?;
200
201 let compiler_options = CompilerOptions {
203 target: "wasm32-wasi".to_string(),
204 opt_level: OptimizationLevel::Speed,
205 profile: BuildProfile::Release,
206 ..CompilerOptions::default()
207 };
208
209 self.compiler.compile(project_dir, output_path, &compiler_options)?;
211
212 Ok(())
213 }
214}