Skip to main content

wasm_sandbox/wrappers/
http_server.rs

1//! HTTP server wrapper implementation
2
3use std::net::{IpAddr, SocketAddr};
4use std::path::PathBuf;
5
6use serde::{Serialize, Deserialize};
7
8use crate::error::Result;
9use crate::wrappers::{WrapperGenerator, WrapperSpec};
10
11/// HTTP request method
12#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13pub enum HttpMethod {
14    /// GET request
15    GET,
16    
17    /// POST request
18    POST,
19    
20    /// PUT request
21    PUT,
22    
23    /// DELETE request
24    DELETE,
25    
26    /// PATCH request
27    PATCH,
28    
29    /// HEAD request
30    HEAD,
31    
32    /// OPTIONS request
33    OPTIONS,
34}
35
36impl std::fmt::Display for HttpMethod {
37    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38        match self {
39            Self::GET => write!(f, "GET"),
40            Self::POST => write!(f, "POST"),
41            Self::PUT => write!(f, "PUT"),
42            Self::DELETE => write!(f, "DELETE"),
43            Self::PATCH => write!(f, "PATCH"),
44            Self::HEAD => write!(f, "HEAD"),
45            Self::OPTIONS => write!(f, "OPTIONS"),
46        }
47    }
48}
49
50/// HTTP request
51#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct HttpRequest {
53    /// Request method
54    pub method: HttpMethod,
55    
56    /// Request path
57    pub path: String,
58    
59    /// Query parameters
60    pub query: Option<String>,
61    
62    /// Request headers
63    pub headers: Vec<(String, String)>,
64    
65    /// Request body
66    pub body: Option<Vec<u8>>,
67    
68    /// Remote address
69    pub remote_addr: Option<SocketAddr>,
70}
71
72/// HTTP response
73#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct HttpResponse {
75    /// Status code
76    pub status: u16,
77    
78    /// Response headers
79    pub headers: Vec<(String, String)>,
80    
81    /// Response body
82    pub body: Vec<u8>,
83}
84
85impl HttpResponse {
86    /// Create a new HTTP response
87    pub fn new(status: u16, body: Vec<u8>) -> Self {
88        Self {
89            status,
90            headers: Vec::new(),
91            body,
92        }
93    }
94    
95    /// Create a new HTTP response with headers
96    pub fn with_headers(status: u16, headers: Vec<(String, String)>, body: Vec<u8>) -> Self {
97        Self {
98            status,
99            headers,
100            body,
101        }
102    }
103    
104    /// Create an OK response with text
105    pub fn text(body: &str) -> Self {
106        Self {
107            status: 200,
108            headers: vec![
109                ("Content-Type".to_string(), "text/plain; charset=utf-8".to_string()),
110                ("Content-Length".to_string(), body.len().to_string()),
111            ],
112            body: body.as_bytes().to_vec(),
113        }
114    }
115    
116    /// Create an OK response with JSON
117    pub fn json<T: Serialize>(body: &T) -> Self {
118        let json = serde_json::to_vec(body).unwrap_or_default();
119        Self {
120            status: 200,
121            headers: vec![
122                ("Content-Type".to_string(), "application/json".to_string()),
123                ("Content-Length".to_string(), json.len().to_string()),
124            ],
125            body: json,
126        }
127    }
128    
129    /// Create a not found response
130    pub fn not_found() -> Self {
131        Self::text("Not Found")
132            .with_status(404)
133    }
134    
135    /// Create an internal server error response
136    pub fn server_error(message: &str) -> Self {
137        Self::text(message)
138            .with_status(500)
139    }
140    
141    /// Set the status code
142    pub fn with_status(mut self, status: u16) -> Self {
143        self.status = status;
144        self
145    }
146    
147    /// Add a header
148    pub fn with_header(mut self, name: &str, value: &str) -> Self {
149        self.headers.push((name.to_string(), value.to_string()));
150        self
151    }
152}
153
154/// HTTP server configuration
155#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct HttpServerConfig {
157    /// Bind address
158    pub address: IpAddr,
159    
160    /// Port
161    pub port: u16,
162    
163    /// Thread count
164    pub threads: Option<usize>,
165    
166    /// Connection timeout in seconds
167    pub timeout_seconds: Option<u64>,
168    
169    /// Maximum request size in bytes
170    pub max_request_size: Option<usize>,
171    
172    /// CORS configuration
173    pub cors: Option<CorsConfig>,
174    
175    /// TLS configuration
176    pub tls: Option<TlsConfig>,
177}
178
179/// CORS configuration
180#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct CorsConfig {
182    /// Allowed origins
183    pub allowed_origins: Vec<String>,
184    
185    /// Allowed methods
186    pub allowed_methods: Vec<String>,
187    
188    /// Allowed headers
189    pub allowed_headers: Vec<String>,
190    
191    /// Allow credentials
192    pub allow_credentials: bool,
193    
194    /// Max age in seconds
195    pub max_age: Option<u32>,
196}
197
198/// TLS configuration
199#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct TlsConfig {
201    /// Certificate path
202    pub cert_path: PathBuf,
203    
204    /// Key path
205    pub key_path: PathBuf,
206}
207
208/// HTTP server wrapper generator
209pub struct HttpServerGenerator;
210
211impl WrapperGenerator for HttpServerGenerator {
212    fn generate_wrapper(&self, spec: &WrapperSpec) -> Result<String> {
213        // Parse the spec template variables for HTTP server options
214        let config = HttpServerConfig {
215            address: spec.template_variables.get("address")
216                .and_then(|s| s.parse().ok())
217                .unwrap_or_else(|| "127.0.0.1".parse().unwrap()),
218            port: spec.template_variables.get("port")
219                .and_then(|s| s.parse().ok())
220                .unwrap_or(8080),
221            threads: spec.template_variables.get("threads")
222                .and_then(|s| s.parse().ok()),
223            timeout_seconds: spec.template_variables.get("timeout_seconds")
224                .and_then(|s| s.parse().ok()),
225            max_request_size: spec.template_variables.get("max_request_size")
226                .and_then(|s| s.parse().ok()),
227            cors: None,
228            tls: None,
229        };
230        
231        // Generate a basic HTTP server wrapper
232        let code = self.generate_code(&config, spec)?;
233        
234        Ok(code)
235    }
236    
237    fn compile_wrapper(&self, code: &str, output_path: &std::path::Path) -> Result<()> {
238        // For now, just write the code to the output path
239        std::fs::write(output_path, code)
240            .map_err(|e| crate::error::Error::WrapperGeneration {
241                reason: format!("Failed to write wrapper code: {}", e),
242                wrapper_type: Some("http_server".to_string()),
243            })?;
244        Ok(())
245    }
246}
247
248impl HttpServerGenerator {
249    /// Generate HTTP server wrapper code
250    fn generate_code(&self, config: &HttpServerConfig, spec: &WrapperSpec) -> Result<String> {
251        let address = format!("{}:{}", config.address, config.port);
252        let _threads = config.threads.unwrap_or(4);
253        let _timeout = config.timeout_seconds.unwrap_or(30);
254        
255        // Generate the server code
256        let mut code = format!(
257            r#"//! HTTP server wrapper for WASM sandbox
258
259use std::{{
260    net::{{IpAddr, SocketAddr}},
261    sync::{{Arc, Mutex}},
262    time::Duration,
263}};
264
265use tokio::{{
266    net::TcpListener,
267    runtime::Runtime,
268    io::{{AsyncReadExt, AsyncWriteExt}},
269}};
270use hyper::{{
271    Body, Request, Response, Server, StatusCode,
272    service::{{make_service_fn, service_fn}},
273    header::{{HeaderValue, CONTENT_TYPE}},
274}};
275use serde_json::{{Value, json}};
276use tracing::{{info, error, debug, warn}};
277
278use wasm_sandbox::{{
279    WasmSandbox, InstanceId, SandboxConfig, InstanceConfig,
280    security::{{ResourceLimits, Capabilities}},
281}};
282
283/// Main function
284#[tokio::main]
285async fn main() -> Result<(), Box<dyn std::error::Error>> {{
286    // Initialize logging
287    tracing_subscriber::fmt::init();
288    info!("Starting HTTP server on {address}");
289    
290    // Create sandbox
291    let mut sandbox = WasmSandbox::new()?;
292    
293    // Load the WASM module
294    let wasm_bytes = include_bytes!("{wasm_path}");
295    let module_id = sandbox.load_module(wasm_bytes)?;
296    
297    info!("Loaded WASM module");
298    
299    // Create instance config
300    let instance_config = {instance_config};
301    
302    // Create the instance
303    let instance_id = sandbox.create_instance(module_id, Some(instance_config))?;
304    info!("Created WASM instance: {{instance_id}}");
305    
306    // Store the instance ID
307    let instance_id = Arc::new(instance_id);
308    
309    // Create HTTP server
310    let make_svc = make_service_fn(move |conn| {{
311        let remote_addr = conn.remote_addr();
312        let instance_id = instance_id.clone();
313        let sandbox = sandbox.clone();
314        
315        async move {{
316            Ok::<_, hyper::Error>(service_fn(move |req| {{
317                let instance_id = instance_id.clone();
318                let sandbox = sandbox.clone();
319                handle_request(req, sandbox, instance_id, remote_addr)
320            }}))
321        }}
322    }});
323    
324    let addr = "{address}".parse()?;
325    let server = Server::bind(&addr)
326        .serve(make_svc)
327        .with_graceful_shutdown(shutdown_signal());
328        
329    info!("Server running on http://{address}");
330    
331    if let Err(e) = server.await {{
332        error!("Server error: {{}}", e);
333    }}
334    
335    Ok(())
336}}
337
338/// Handle HTTP request
339async fn handle_request(
340    req: Request<Body>, 
341    sandbox: Arc<Mutex<WasmSandbox>>,
342    instance_id: Arc<InstanceId>,
343    remote_addr: SocketAddr,
344) -> Result<Response<Body>, hyper::Error> {{
345    // Convert hyper request to our format
346    let (parts, body) = req.into_parts();
347    let body_bytes = hyper::body::to_bytes(body).await?;
348    
349    // Create request object
350    let request = json!({{
351        "method": parts.method.as_str(),
352        "path": parts.uri.path(),
353        "query": parts.uri.query(),
354        "headers": parts.headers.iter().map(|(k, v)| {{
355            (k.as_str(), v.to_str().unwrap_or_default())
356        }}).collect::<Vec<_>>(),
357        "body": body_bytes.to_vec(),
358        "remote_addr": remote_addr.to_string(),
359    }});
360    
361    // Call handler function in sandbox
362    let response = match sandbox.lock().unwrap().call_function::<_, Value>(
363        *instance_id,
364        "handle_http_request",
365        &request,
366    ).await {{
367        Ok(res) => res,
368        Err(e) => {{
369            error!("Error calling WASM function: {{}}", e);
370            return Ok(Response::builder()
371                .status(StatusCode::INTERNAL_SERVER_ERROR)
372                .body(Body::from("Internal Server Error"))?);
373        }}
374    }};
375    
376    // Convert JSON response to hyper response
377    let status = response["status"].as_u64().unwrap_or(500) as u16;
378    let headers = response["headers"].as_array().unwrap_or(&Vec::new());
379    let body = response["body"].as_array().unwrap_or(&Vec::new())
380        .iter()
381        .filter_map(|v| v.as_u64().map(|n| n as u8))
382        .collect::<Vec<_>>();
383        
384    // Build response
385    let mut builder = Response::builder().status(status);
386    
387    // Add headers
388    for header in headers {{
389        if let (Some(name), Some(value)) = (
390            header[0].as_str(),
391            header[1].as_str(),
392        ) {{
393            builder = builder.header(name, value);
394        }}
395    }}
396    
397    // Add default content type if not present
398    if !headers.iter().any(|h| h[0].as_str() == Some("content-type")) {{
399        builder = builder.header(CONTENT_TYPE, "text/plain");
400    }}
401    
402    Ok(builder.body(Body::from(body))?)
403}}
404
405/// Shutdown signal handler
406async fn shutdown_signal() {{
407    tokio::signal::ctrl_c()
408        .await
409        .expect("Failed to install CTRL+C signal handler");
410        
411    info!("Shutdown signal received, stopping server...");
412}}
413"#,
414            address = address,
415            wasm_path = spec.app_path.display(),
416            instance_config = "InstanceConfig::default()",  // Simplified for now
417        );
418        
419        // Add TLS configuration if enabled
420        if let Some(_tls_config) = &config.cors {
421            // Add TLS imports and config
422            code = code.replace(
423                "use hyper::",
424                r#"use rustls::{{Certificate, PrivateKey, ServerConfig}};
425use tokio_rustls::TlsAcceptor;
426use std::fs::File;
427use std::io::BufReader;
428use rustls_pemfile::{certs, rsa_private_keys};
429use hyper::"#
430            );
431            
432            // This is a simplified placeholder - real TLS configuration would be more complex
433            code = code.replace(
434                "let addr = \"{address}\".parse()?;",
435                r#"let addr = "{address}".parse()?;
436    
437    // Configure TLS
438    let tls_config = {
439        // Load certificate and private key
440        let cert_file = File::open("certificate.pem")?;
441        let mut reader = BufReader::new(cert_file);
442        let certs = certs(&mut reader)?;
443        let certs = certs.into_iter().map(Certificate).collect();
444        
445        let key_file = File::open("private_key.pem")?;
446        let mut reader = BufReader::new(key_file);
447        let keys = rsa_private_keys(&mut reader)?;
448        let key = PrivateKey(keys[0].clone());
449        
450        let mut config = ServerConfig::builder()
451            .with_safe_defaults()
452            .with_no_client_auth()
453            .with_single_cert(certs, key)?;
454            
455        Arc::new(config)
456    };"#
457            );
458        }
459        
460        // Add CORS configuration if enabled
461        if let Some(cors_config) = &config.cors {
462            let _allowed_origins = cors_config.allowed_origins
463                .iter()
464                .map(|o| format!("\"{}\"", o))
465                .collect::<Vec<_>>()
466                .join(", ");
467                
468            let _allowed_methods = cors_config.allowed_methods
469                .iter()
470                .map(|m| format!("\"{}\"", m))
471                .collect::<Vec<_>>()
472                .join(", ");
473                
474            let _allowed_headers = cors_config.allowed_headers
475                .iter()
476                .map(|h| format!("\"{}\"", h))
477                .collect::<Vec<_>>()
478                .join(", ");
479                
480            // Add CORS middleware
481            code = code.replace(
482                "/// Handle HTTP request",
483                r#"/// Apply CORS headers
484fn apply_cors(builder: &mut http::response::Builder) {
485    builder
486        .header("Access-Control-Allow-Origin", "*")
487        .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
488        .header("Access-Control-Allow-Headers", "Content-Type, Authorization")
489        .header("Access-Control-Allow-Credentials", "true")
490        .header("Access-Control-Max-Age", "3600");
491}
492
493/// Handle HTTP request"#
494            );
495            
496            // Use CORS in the handler
497            code = code.replace(
498                "// Add default content type if not present",
499                r#"// Add CORS headers
500    apply_cors(&mut builder);
501    
502    // Add default content type if not present"#
503            );
504        }
505        
506        Ok(code)
507    }
508}