use reqwest::blocking::Client;
use std::time::Duration;
pub type NetResult<T> = Result<T, String>;
#[derive(Debug, Clone)]
pub struct Response {
pub status: i32,
pub headers: Vec<(String, String)>,
pub body: String,
}
#[derive(Debug, Clone)]
pub struct Request {
pub url: String,
pub method: String,
pub headers: Vec<(String, String)>,
pub body: Option<String>,
pub timeout: Option<i32>,
}
impl Request {
pub fn get(url: String) -> Self {
Self {
url,
method: "GET".to_string(),
headers: Vec::new(),
body: None,
timeout: None,
}
}
pub fn post(url: String, body: String) -> Self {
Self {
url,
method: "POST".to_string(),
headers: Vec::new(),
body: Some(body),
timeout: None,
}
}
pub fn header(mut self, key: String, value: String) -> Self {
self.headers.push((key, value));
self
}
pub fn timeout(mut self, seconds: i32) -> Self {
self.timeout = Some(seconds);
self
}
pub fn send(self) -> NetResult<Response> {
let client = Client::builder()
.timeout(
self.timeout
.map(|s| Duration::from_secs(s as u64))
.unwrap_or(Duration::from_secs(30)),
)
.build()
.map_err(|e| format!("Failed to create HTTP client: {}", e))?;
let mut request = match self.method.as_str() {
"GET" => client.get(&self.url),
"POST" => client.post(&self.url),
"PUT" => client.put(&self.url),
"DELETE" => client.delete(&self.url),
"PATCH" => client.patch(&self.url),
_ => return Err(format!("Unsupported HTTP method: {}", self.method)),
};
for (key, value) in self.headers {
request = request.header(&key, &value);
}
if let Some(body) = self.body {
request = request.body(body);
}
let response = request
.send()
.map_err(|e| format!("HTTP request failed: {}", e))?;
let status = response.status().as_u16() as i32;
let headers: Vec<(String, String)> = response
.headers()
.iter()
.filter_map(
|(k, v): (&reqwest::header::HeaderName, &reqwest::header::HeaderValue)| {
v.to_str()
.ok()
.map(|v_str: &str| (k.as_str().to_string(), v_str.to_string()))
},
)
.collect();
let body: String = response
.text()
.map_err(|e| format!("Failed to read response body: {}", e))?;
Ok(Response {
status,
headers,
body,
})
}
pub async fn send_async(self) -> NetResult<Response> {
let client = reqwest::Client::builder()
.timeout(
self.timeout
.map(|s| Duration::from_secs(s as u64))
.unwrap_or(Duration::from_secs(30)),
)
.build()
.map_err(|e| format!("Failed to create HTTP client: {}", e))?;
let mut request = match self.method.as_str() {
"GET" => client.get(&self.url),
"POST" => client.post(&self.url),
"PUT" => client.put(&self.url),
"DELETE" => client.delete(&self.url),
"PATCH" => client.patch(&self.url),
_ => return Err(format!("Unsupported HTTP method: {}", self.method)),
};
for (key, value) in self.headers {
request = request.header(&key, &value);
}
if let Some(body) = self.body {
request = request.body(body);
}
let response: reqwest::Response = request
.send()
.await
.map_err(|e: reqwest::Error| format!("HTTP request failed: {}", e))?;
let status = response.status().as_u16() as i32;
let headers: Vec<(String, String)> = response
.headers()
.iter()
.filter_map(
|(k, v): (&reqwest::header::HeaderName, &reqwest::header::HeaderValue)| {
v.to_str()
.ok()
.map(|v_str: &str| (k.as_str().to_string(), v_str.to_string()))
},
)
.collect();
let body: String = response
.text()
.await
.map_err(|e: reqwest::Error| format!("Failed to read response body: {}", e))?;
Ok(Response {
status,
headers,
body,
})
}
}
pub fn get(url: String) -> NetResult<Response> {
Request::get(url).send()
}
pub fn post(url: String, body: String) -> NetResult<Response> {
Request::post(url, body).send()
}
pub async fn get_async(url: String) -> NetResult<Response> {
Request::get(url).send_async().await
}
pub async fn post_async(url: String, body: String) -> NetResult<Response> {
Request::post(url, body).send_async().await
}
pub fn download(url: String, path: String) -> NetResult<()> {
let response = get(url)?;
std::fs::write(&path, response.body).map_err(|e| format!("Failed to write file: {}", e))?;
Ok(())
}
pub fn upload(url: String, path: String) -> NetResult<Response> {
let body = std::fs::read_to_string(&path).map_err(|e| format!("Failed to read file: {}", e))?;
post(url, body)
}
pub struct WebSocket;
impl WebSocket {
pub fn connect(_url: String) -> NetResult<WebSocket> {
Err("WebSocket support requires tokio-tungstenite (not yet implemented)".to_string())
}
pub fn send(&self, _message: String) -> NetResult<()> {
Err("WebSocket not connected".to_string())
}
pub fn receive(&self) -> NetResult<String> {
Err("WebSocket not connected".to_string())
}
pub fn close(self) -> NetResult<()> {
Ok(())
}
}
use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream};
#[derive(Debug, Clone)]
pub struct ServerRequest {
pub method: String,
pub path: String,
pub headers: Vec<(String, String)>,
pub body: String,
}
#[derive(Debug, Clone)]
pub struct ServerResponse {
pub status: i32,
pub headers: Vec<(String, String)>,
pub body: String,
}
impl ServerResponse {
pub fn new(status: i32, body: String) -> Self {
Self {
status,
headers: Vec::new(),
body,
}
}
pub fn html(body: String) -> Self {
Self {
status: 200,
headers: vec![("Content-Type".to_string(), "text/html".to_string())],
body,
}
}
pub fn json(body: String) -> Self {
Self {
status: 200,
headers: vec![("Content-Type".to_string(), "application/json".to_string())],
body,
}
}
pub fn error(status: i32, message: String) -> Self {
Self {
status,
headers: vec![("Content-Type".to_string(), "text/plain".to_string())],
body: message,
}
}
pub fn header(mut self, key: String, value: String) -> Self {
self.headers.push((key, value));
self
}
}
#[derive(Debug, Clone)]
pub struct Server {
pub address: String,
pub port: i32,
}
impl Server {
pub fn new(address: String, port: i32) -> Self {
Self { address, port }
}
pub fn serve<F>(self, handler: F) -> NetResult<()>
where
F: Fn(ServerRequest) -> ServerResponse + Send + Sync + 'static,
{
let addr = format!("{}:{}", self.address, self.port);
let listener =
TcpListener::bind(&addr).map_err(|e| format!("Failed to bind to {}: {}", addr, e))?;
println!("🚀 Server listening on http://{}", addr);
for stream in listener.incoming() {
match stream {
Ok(stream) => {
if let Err(e) = handle_connection(stream, &handler) {
eprintln!("❌ Error handling connection: {}", e);
}
}
Err(e) => {
eprintln!("❌ Error accepting connection: {}", e);
}
}
}
Ok(())
}
}
fn handle_connection<F>(mut stream: TcpStream, handler: &F) -> NetResult<()>
where
F: Fn(ServerRequest) -> ServerResponse,
{
let mut buffer = [0; 4096];
let size = stream
.read(&mut buffer)
.map_err(|e| format!("Failed to read from stream: {}", e))?;
let request_str = String::from_utf8_lossy(&buffer[..size]);
let request = parse_request(&request_str)?;
println!("📄 {} {}", request.method, request.path);
let response = handler(request);
send_response(&mut stream, response)?;
Ok(())
}
fn parse_request(request_str: &str) -> NetResult<ServerRequest> {
let mut lines = request_str.lines();
let first_line = lines.next().ok_or("Empty request")?;
let parts: Vec<&str> = first_line.split_whitespace().collect();
if parts.len() < 2 {
return Err("Invalid request line".to_string());
}
let method = parts[0].to_string();
let path = parts[1].to_string();
let mut headers = Vec::new();
for line in lines {
if line.is_empty() {
break;
}
if let Some((key, value)) = line.split_once(':') {
headers.push((key.trim().to_string(), value.trim().to_string()));
}
}
Ok(ServerRequest {
method,
path,
headers,
body: String::new(),
})
}
fn send_response(stream: &mut TcpStream, response: ServerResponse) -> NetResult<()> {
let status_text = match response.status {
200 => "OK",
404 => "Not Found",
500 => "Internal Server Error",
_ => "Unknown",
};
let mut response_str = format!("HTTP/1.1 {} {}\r\n", response.status, status_text);
for (key, value) in &response.headers {
response_str.push_str(&format!("{}: {}\r\n", key, value));
}
response_str.push_str(&format!("Content-Length: {}\r\n", response.body.len()));
response_str.push_str("Access-Control-Allow-Origin: *\r\n");
response_str.push_str("\r\n");
response_str.push_str(&response.body);
stream
.write_all(response_str.as_bytes())
.map_err(|e| format!("Failed to write response: {}", e))?;
stream
.flush()
.map_err(|e| format!("Failed to flush stream: {}", e))?;
Ok(())
}