windjammer_runtime/platform/native/
net.rs1use reqwest::blocking::Client;
3use std::time::Duration;
4
5pub type NetResult<T> = Result<T, String>;
6
7#[derive(Debug, Clone)]
9pub struct Response {
10 pub status: i32,
11 pub headers: Vec<(String, String)>,
12 pub body: String,
13}
14
15#[derive(Debug, Clone)]
17pub struct Request {
18 pub url: String,
19 pub method: String,
20 pub headers: Vec<(String, String)>,
21 pub body: Option<String>,
22 pub timeout: Option<i32>,
23}
24
25impl Request {
26 pub fn get(url: String) -> Self {
28 Self {
29 url,
30 method: "GET".to_string(),
31 headers: Vec::new(),
32 body: None,
33 timeout: None,
34 }
35 }
36
37 pub fn post(url: String, body: String) -> Self {
39 Self {
40 url,
41 method: "POST".to_string(),
42 headers: Vec::new(),
43 body: Some(body),
44 timeout: None,
45 }
46 }
47
48 pub fn header(mut self, key: String, value: String) -> Self {
50 self.headers.push((key, value));
51 self
52 }
53
54 pub fn timeout(mut self, seconds: i32) -> Self {
56 self.timeout = Some(seconds);
57 self
58 }
59
60 pub fn send(self) -> NetResult<Response> {
62 let client = Client::builder()
63 .timeout(
64 self.timeout
65 .map(|s| Duration::from_secs(s as u64))
66 .unwrap_or(Duration::from_secs(30)),
67 )
68 .build()
69 .map_err(|e| format!("Failed to create HTTP client: {}", e))?;
70
71 let mut request = match self.method.as_str() {
72 "GET" => client.get(&self.url),
73 "POST" => client.post(&self.url),
74 "PUT" => client.put(&self.url),
75 "DELETE" => client.delete(&self.url),
76 "PATCH" => client.patch(&self.url),
77 _ => return Err(format!("Unsupported HTTP method: {}", self.method)),
78 };
79
80 for (key, value) in self.headers {
82 request = request.header(&key, &value);
83 }
84
85 if let Some(body) = self.body {
87 request = request.body(body);
88 }
89
90 let response = request
92 .send()
93 .map_err(|e| format!("HTTP request failed: {}", e))?;
94
95 let status = response.status().as_u16() as i32;
97
98 let headers: Vec<(String, String)> = response
100 .headers()
101 .iter()
102 .filter_map(
103 |(k, v): (&reqwest::header::HeaderName, &reqwest::header::HeaderValue)| {
104 v.to_str()
105 .ok()
106 .map(|v_str: &str| (k.as_str().to_string(), v_str.to_string()))
107 },
108 )
109 .collect();
110
111 let body: String = response
113 .text()
114 .map_err(|e| format!("Failed to read response body: {}", e))?;
115
116 Ok(Response {
117 status,
118 headers,
119 body,
120 })
121 }
122
123 pub async fn send_async(self) -> NetResult<Response> {
125 let client = reqwest::Client::builder()
126 .timeout(
127 self.timeout
128 .map(|s| Duration::from_secs(s as u64))
129 .unwrap_or(Duration::from_secs(30)),
130 )
131 .build()
132 .map_err(|e| format!("Failed to create HTTP client: {}", e))?;
133
134 let mut request = match self.method.as_str() {
135 "GET" => client.get(&self.url),
136 "POST" => client.post(&self.url),
137 "PUT" => client.put(&self.url),
138 "DELETE" => client.delete(&self.url),
139 "PATCH" => client.patch(&self.url),
140 _ => return Err(format!("Unsupported HTTP method: {}", self.method)),
141 };
142
143 for (key, value) in self.headers {
145 request = request.header(&key, &value);
146 }
147
148 if let Some(body) = self.body {
150 request = request.body(body);
151 }
152
153 let response: reqwest::Response = request
155 .send()
156 .await
157 .map_err(|e: reqwest::Error| format!("HTTP request failed: {}", e))?;
158
159 let status = response.status().as_u16() as i32;
161
162 let headers: Vec<(String, String)> = response
164 .headers()
165 .iter()
166 .filter_map(
167 |(k, v): (&reqwest::header::HeaderName, &reqwest::header::HeaderValue)| {
168 v.to_str()
169 .ok()
170 .map(|v_str: &str| (k.as_str().to_string(), v_str.to_string()))
171 },
172 )
173 .collect();
174
175 let body: String = response
177 .text()
178 .await
179 .map_err(|e: reqwest::Error| format!("Failed to read response body: {}", e))?;
180
181 Ok(Response {
182 status,
183 headers,
184 body,
185 })
186 }
187}
188
189pub fn get(url: String) -> NetResult<Response> {
191 Request::get(url).send()
192}
193
194pub fn post(url: String, body: String) -> NetResult<Response> {
196 Request::post(url, body).send()
197}
198
199pub async fn get_async(url: String) -> NetResult<Response> {
201 Request::get(url).send_async().await
202}
203
204pub async fn post_async(url: String, body: String) -> NetResult<Response> {
206 Request::post(url, body).send_async().await
207}
208
209pub fn download(url: String, path: String) -> NetResult<()> {
211 let response = get(url)?;
212 std::fs::write(&path, response.body).map_err(|e| format!("Failed to write file: {}", e))?;
213 Ok(())
214}
215
216pub fn upload(url: String, path: String) -> NetResult<Response> {
218 let body = std::fs::read_to_string(&path).map_err(|e| format!("Failed to read file: {}", e))?;
219 post(url, body)
220}
221
222pub struct WebSocket;
225
226impl WebSocket {
227 pub fn connect(_url: String) -> NetResult<WebSocket> {
228 Err("WebSocket support requires tokio-tungstenite (not yet implemented)".to_string())
229 }
230
231 pub fn send(&self, _message: String) -> NetResult<()> {
232 Err("WebSocket not connected".to_string())
233 }
234
235 pub fn receive(&self) -> NetResult<String> {
236 Err("WebSocket not connected".to_string())
237 }
238
239 pub fn close(self) -> NetResult<()> {
240 Ok(())
241 }
242}
243
244use std::io::{Read, Write};
249use std::net::{TcpListener, TcpStream};
250
251#[derive(Debug, Clone)]
253pub struct ServerRequest {
254 pub method: String,
255 pub path: String,
256 pub headers: Vec<(String, String)>,
257 pub body: String,
258}
259
260#[derive(Debug, Clone)]
262pub struct ServerResponse {
263 pub status: i32,
264 pub headers: Vec<(String, String)>,
265 pub body: String,
266}
267
268impl ServerResponse {
269 pub fn new(status: i32, body: String) -> Self {
270 Self {
271 status,
272 headers: Vec::new(),
273 body,
274 }
275 }
276
277 pub fn html(body: String) -> Self {
278 Self {
279 status: 200,
280 headers: vec![("Content-Type".to_string(), "text/html".to_string())],
281 body,
282 }
283 }
284
285 pub fn json(body: String) -> Self {
286 Self {
287 status: 200,
288 headers: vec![("Content-Type".to_string(), "application/json".to_string())],
289 body,
290 }
291 }
292
293 pub fn error(status: i32, message: String) -> Self {
294 Self {
295 status,
296 headers: vec![("Content-Type".to_string(), "text/plain".to_string())],
297 body: message,
298 }
299 }
300
301 pub fn header(mut self, key: String, value: String) -> Self {
302 self.headers.push((key, value));
303 self
304 }
305}
306
307#[derive(Debug, Clone)]
309pub struct Server {
310 pub address: String,
311 pub port: i32,
312}
313
314impl Server {
315 pub fn new(address: String, port: i32) -> Self {
316 Self { address, port }
317 }
318
319 pub fn serve<F>(self, handler: F) -> NetResult<()>
320 where
321 F: Fn(ServerRequest) -> ServerResponse + Send + Sync + 'static,
322 {
323 let addr = format!("{}:{}", self.address, self.port);
324 let listener =
325 TcpListener::bind(&addr).map_err(|e| format!("Failed to bind to {}: {}", addr, e))?;
326
327 println!("🚀 Server listening on http://{}", addr);
328
329 for stream in listener.incoming() {
330 match stream {
331 Ok(stream) => {
332 if let Err(e) = handle_connection(stream, &handler) {
333 eprintln!("❌ Error handling connection: {}", e);
334 }
335 }
336 Err(e) => {
337 eprintln!("❌ Error accepting connection: {}", e);
338 }
339 }
340 }
341
342 Ok(())
343 }
344}
345
346fn handle_connection<F>(mut stream: TcpStream, handler: &F) -> NetResult<()>
347where
348 F: Fn(ServerRequest) -> ServerResponse,
349{
350 let mut buffer = [0; 4096];
351 let size = stream
352 .read(&mut buffer)
353 .map_err(|e| format!("Failed to read from stream: {}", e))?;
354
355 let request_str = String::from_utf8_lossy(&buffer[..size]);
356
357 let request = parse_request(&request_str)?;
359
360 println!("📄 {} {}", request.method, request.path);
361
362 let response = handler(request);
364
365 send_response(&mut stream, response)?;
367
368 Ok(())
369}
370
371fn parse_request(request_str: &str) -> NetResult<ServerRequest> {
372 let mut lines = request_str.lines();
373
374 let first_line = lines.next().ok_or("Empty request")?;
376 let parts: Vec<&str> = first_line.split_whitespace().collect();
377
378 if parts.len() < 2 {
379 return Err("Invalid request line".to_string());
380 }
381
382 let method = parts[0].to_string();
383 let path = parts[1].to_string();
384
385 let mut headers = Vec::new();
387 for line in lines {
388 if line.is_empty() {
389 break;
390 }
391 if let Some((key, value)) = line.split_once(':') {
392 headers.push((key.trim().to_string(), value.trim().to_string()));
393 }
394 }
395
396 Ok(ServerRequest {
397 method,
398 path,
399 headers,
400 body: String::new(),
401 })
402}
403
404fn send_response(stream: &mut TcpStream, response: ServerResponse) -> NetResult<()> {
405 let status_text = match response.status {
406 200 => "OK",
407 404 => "Not Found",
408 500 => "Internal Server Error",
409 _ => "Unknown",
410 };
411
412 let mut response_str = format!("HTTP/1.1 {} {}\r\n", response.status, status_text);
413
414 for (key, value) in &response.headers {
416 response_str.push_str(&format!("{}: {}\r\n", key, value));
417 }
418
419 response_str.push_str(&format!("Content-Length: {}\r\n", response.body.len()));
421 response_str.push_str("Access-Control-Allow-Origin: *\r\n");
422 response_str.push_str("\r\n");
423 response_str.push_str(&response.body);
424
425 stream
426 .write_all(response_str.as_bytes())
427 .map_err(|e| format!("Failed to write response: {}", e))?;
428
429 stream
430 .flush()
431 .map_err(|e| format!("Failed to flush stream: {}", e))?;
432
433 Ok(())
434}