Skip to main content

wasm_sandbox/wrappers/
http_server_impl.rs

1//! HTTP Server wrapper generator implementation
2
3use 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/// HTTP Server wrapper configuration
14#[derive(Clone)]
15pub struct HttpServerConfig {
16    /// Server port
17    pub port: u16,
18    
19    /// Server host
20    pub host: String,
21    
22    /// Maximum request body size in bytes
23    pub max_body_size: usize,
24    
25    /// Request timeout in seconds
26    pub request_timeout: u64,
27    
28    /// CORS configuration
29    pub cors_enabled: bool,
30    
31    /// CORS allowed origins
32    pub cors_allowed_origins: Vec<String>,
33    
34    /// CORS allowed methods
35    pub cors_allowed_methods: Vec<String>,
36    
37    /// CORS allowed headers
38    pub cors_allowed_headers: Vec<String>,
39    
40    /// Static file serving configuration
41    pub serve_static: bool,
42    
43    /// Static file directory
44    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, // 10MB
53            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
64/// HTTP Server wrapper generator
65pub struct HttpServerGenerator {
66    /// Configuration
67    config: HttpServerConfig,
68    
69    /// Cargo compiler
70    compiler: CargoCompiler,
71}
72
73impl HttpServerGenerator {
74    /// Create a new HTTP Server wrapper generator with default configuration
75    pub fn new() -> Self {
76        Self {
77            config: HttpServerConfig::default(),
78            compiler: CargoCompiler::new(),
79        }
80    }
81    
82    /// Create a new HTTP Server wrapper generator with custom configuration
83    pub fn with_config(config: HttpServerConfig) -> Self {
84        Self {
85            config,
86            compiler: CargoCompiler::new(),
87        }
88    }
89    
90    /// Generate Cargo.toml for the wrapper project
91    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    /// Render the wrapper code using the template
139    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                // Generate the template context
148                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                // Render the template
161                let mut template = HTTP_SERVER_TEMPLATE.to_string();
162                
163                // Replace template variables
164                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        // Create a temporary directory for the project
186        let temp_dir = tempfile::tempdir()?;
187        let project_dir = temp_dir.path();
188        
189        // Create src directory
190        let src_dir = project_dir.join("src");
191        fs::create_dir_all(&src_dir)?;
192        
193        // Write the code to lib.rs
194        fs::write(src_dir.join("lib.rs"), code)?;
195        
196        // Write Cargo.toml
197        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        // Configure compiler options
202        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        // Compile the project
210        self.compiler.compile(project_dir, output_path, &compiler_options)?;
211        
212        Ok(())
213    }
214}