windjammer_runtime/platform/native/
http.rs

1/// Native implementation of std::http
2use std::io::{Read, Write};
3use std::net::{TcpListener, TcpStream};
4
5pub type HttpResult<T> = Result<T, String>;
6
7// ============================================================================
8// HTTP CLIENT (re-export from net.rs)
9// ============================================================================
10
11pub use super::net::{get, post, Request, Response};
12
13// Implement put and delete here
14pub fn put(url: String, body: String) -> HttpResult<Response> {
15    Request {
16        url,
17        method: "PUT".to_string(),
18        headers: Vec::new(),
19        body: Some(body),
20        timeout: None,
21    }
22    .send()
23}
24
25pub fn delete(url: String) -> HttpResult<Response> {
26    Request {
27        url,
28        method: "DELETE".to_string(),
29        headers: Vec::new(),
30        body: None,
31        timeout: None,
32    }
33    .send()
34}
35
36// ============================================================================
37// HTTP SERVER IMPLEMENTATION
38// ============================================================================
39
40/// HTTP Server Request (received by server)
41#[derive(Debug, Clone)]
42pub struct ServerRequest {
43    pub method: String,
44    pub path: String,
45    pub headers: Vec<(String, String)>,
46    pub body: String,
47}
48
49/// HTTP Server Response (sent by server)
50#[derive(Debug, Clone)]
51pub struct ServerResponse {
52    pub status: i32,
53    pub headers: Vec<(String, String)>,
54    pub body: String,
55    pub binary_body: Option<Vec<u8>>,
56}
57
58impl ServerResponse {
59    pub fn new(status: i32, body: String) -> Self {
60        Self {
61            status,
62            headers: Vec::new(),
63            body,
64            binary_body: None,
65        }
66    }
67
68    pub fn binary(status: i32, data: Vec<u8>) -> Self {
69        Self {
70            status,
71            headers: Vec::new(),
72            body: String::new(),
73            binary_body: Some(data),
74        }
75    }
76
77    pub fn html(body: String) -> Self {
78        Self {
79            status: 200,
80            headers: vec![("Content-Type".to_string(), "text/html".to_string())],
81            body,
82            binary_body: None,
83        }
84    }
85
86    pub fn json(body: String) -> Self {
87        Self {
88            status: 200,
89            headers: vec![("Content-Type".to_string(), "application/json".to_string())],
90            body,
91            binary_body: None,
92        }
93    }
94
95    pub fn error(status: i32, message: String) -> Self {
96        Self {
97            status,
98            headers: vec![("Content-Type".to_string(), "text/plain".to_string())],
99            body: message,
100            binary_body: None,
101        }
102    }
103
104    pub fn header(mut self, key: String, value: String) -> Self {
105        self.headers.push((key, value));
106        self
107    }
108}
109
110/// HTTP Server
111#[derive(Debug, Clone)]
112pub struct Server {
113    pub address: String,
114    pub port: i32,
115}
116
117impl Server {
118    pub fn new(address: String, port: i32) -> Self {
119        Self { address, port }
120    }
121
122    pub fn serve<F>(self, handler: F) -> HttpResult<()>
123    where
124        F: Fn(ServerRequest) -> ServerResponse + Send + Sync + 'static,
125    {
126        let addr = format!("{}:{}", self.address, self.port);
127        let listener =
128            TcpListener::bind(&addr).map_err(|e| format!("Failed to bind to {}: {}", addr, e))?;
129
130        println!("🚀 Server listening on http://{}", addr);
131
132        for stream in listener.incoming() {
133            match stream {
134                Ok(stream) => {
135                    if let Err(e) = handle_connection(stream, &handler) {
136                        eprintln!("❌ Error handling connection: {}", e);
137                    }
138                }
139                Err(e) => {
140                    eprintln!("❌ Error accepting connection: {}", e);
141                }
142            }
143        }
144
145        Ok(())
146    }
147}
148
149fn handle_connection<F>(mut stream: TcpStream, handler: &F) -> HttpResult<()>
150where
151    F: Fn(ServerRequest) -> ServerResponse,
152{
153    let mut buffer = [0; 4096];
154    let size = stream
155        .read(&mut buffer)
156        .map_err(|e| format!("Failed to read from stream: {}", e))?;
157
158    let request_str = String::from_utf8_lossy(&buffer[..size]);
159
160    // Parse request
161    let request = parse_request(&request_str)?;
162
163    println!("📄 {} {}", request.method, request.path);
164
165    // Call handler
166    let response = handler(request);
167
168    // Send response
169    send_response(&mut stream, response)?;
170
171    Ok(())
172}
173
174fn parse_request(request_str: &str) -> HttpResult<ServerRequest> {
175    let mut lines = request_str.lines();
176
177    // Parse request line
178    let first_line = lines.next().ok_or("Empty request")?;
179    let parts: Vec<&str> = first_line.split_whitespace().collect();
180
181    if parts.len() < 2 {
182        return Err("Invalid request line".to_string());
183    }
184
185    let method = parts[0].to_string();
186    let path = parts[1].to_string();
187
188    // Parse headers
189    let mut headers = Vec::new();
190    for line in lines {
191        if line.is_empty() {
192            break;
193        }
194        if let Some((key, value)) = line.split_once(':') {
195            headers.push((key.trim().to_string(), value.trim().to_string()));
196        }
197    }
198
199    Ok(ServerRequest {
200        method,
201        path,
202        headers,
203        body: String::new(),
204    })
205}
206
207fn send_response(stream: &mut TcpStream, response: ServerResponse) -> HttpResult<()> {
208    let status_text = match response.status {
209        200 => "OK",
210        404 => "Not Found",
211        500 => "Internal Server Error",
212        _ => "Unknown",
213    };
214
215    let mut response_str = format!("HTTP/1.1 {} {}\r\n", response.status, status_text);
216
217    // Add headers
218    for (key, value) in &response.headers {
219        response_str.push_str(&format!("{}: {}\r\n", key, value));
220    }
221
222    // Handle binary or text body
223    if let Some(binary_data) = &response.binary_body {
224        // Binary response (WASM, images, etc.)
225        response_str.push_str(&format!("Content-Length: {}\r\n", binary_data.len()));
226        response_str.push_str("Access-Control-Allow-Origin: *\r\n");
227        response_str.push_str("\r\n");
228
229        // Write headers
230        stream
231            .write_all(response_str.as_bytes())
232            .map_err(|e| format!("Failed to write response headers: {}", e))?;
233
234        // Write binary body
235        stream
236            .write_all(binary_data)
237            .map_err(|e| format!("Failed to write binary body: {}", e))?;
238    } else {
239        // Text response
240        response_str.push_str(&format!("Content-Length: {}\r\n", response.body.len()));
241        response_str.push_str("Access-Control-Allow-Origin: *\r\n");
242        response_str.push_str("\r\n");
243        response_str.push_str(&response.body);
244
245        stream
246            .write_all(response_str.as_bytes())
247            .map_err(|e| format!("Failed to write response: {}", e))?;
248    }
249
250    stream
251        .flush()
252        .map_err(|e| format!("Failed to flush stream: {}", e))?;
253
254    Ok(())
255}