commit
e1237dfa6f
12 changed files with 1957 additions and 0 deletions
File diff suppressed because it is too large
@ -0,0 +1,8 @@ |
|||||
|
[package] |
||||
|
name = "chatty" |
||||
|
version = "0.1.0" |
||||
|
edition = "2024" |
||||
|
|
||||
|
[dependencies] |
||||
|
sqlx = {version = "0.8.6", features = ["postgres", "runtime-tokio"]} |
||||
|
tokio = {version = "1.47.1", features = ["full"]} |
||||
@ -0,0 +1,66 @@ |
|||||
|
use std::net::{TcpListener, TcpStream}; |
||||
|
use std::thread; |
||||
|
use std::io::{BufReader, prelude::*}; |
||||
|
use std::boxed::Box; |
||||
|
use crate::routes::proxy; |
||||
|
use crate::models::HttpRequest::HttpRequest; |
||||
|
|
||||
|
pub fn http_server() { |
||||
|
let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); |
||||
|
println!("Listening on port 7878"); |
||||
|
|
||||
|
for stream in listener.incoming() { |
||||
|
match stream { |
||||
|
Ok(stream) => { |
||||
|
thread::spawn(move || { |
||||
|
handle_connection(stream); |
||||
|
}); |
||||
|
} |
||||
|
Err(e) => { |
||||
|
eprintln!("Error accepting connection: {}", e); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fn handle_connection(mut stream: TcpStream) { |
||||
|
// Handle the connection (currently does nothing)
|
||||
|
let reader = BufReader::new(&mut stream); |
||||
|
let http_request = reader.lines().next().unwrap().unwrap(); |
||||
|
|
||||
|
let parsed_request = parse_http_request(&http_request); |
||||
|
match parsed_request { |
||||
|
Ok(request) => { |
||||
|
proxy::proxy(request, &stream); |
||||
|
} |
||||
|
Err(e) => { |
||||
|
eprintln!("Failed to parse HTTP request: {}", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
// FUNCTION TO PARSE HTTP REQUESTS
|
||||
|
fn parse_http_request(request: &str) -> Result<HttpRequest, Box<dyn std::error::Error>> { |
||||
|
let parts: Vec<&str> = request.split_whitespace().collect(); |
||||
|
|
||||
|
if parts[1].ends_with('/') == false && parts[1].contains('.') == false { |
||||
|
// ADD TRAILING / IF MISSING
|
||||
|
println!("Adding trailing / to path"); |
||||
|
let new_path = format!("{}/", parts[1]); |
||||
|
let new_request = format!("{} {} {}", parts[0], new_path, parts[2]); |
||||
|
return parse_http_request(&new_request); |
||||
|
} |
||||
|
|
||||
|
if parts.len() >= 3 { |
||||
|
let request = HttpRequest { |
||||
|
method: parts[0].to_string(), |
||||
|
path: parts[1].to_string(), |
||||
|
version: parts[2].to_string(), |
||||
|
}; |
||||
|
Ok(request) |
||||
|
} else { |
||||
|
Err("Invalid HTTP request".into()) |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
mod http; |
||||
|
mod routes; |
||||
|
mod models; |
||||
|
|
||||
|
#[tokio::main] |
||||
|
async fn main() { |
||||
|
println!("Starting application..."); |
||||
|
|
||||
|
http::http_server(); |
||||
|
|
||||
|
|
||||
|
} |
||||
@ -0,0 +1,6 @@ |
|||||
|
#[derive(Debug, Clone,)] |
||||
|
pub struct HttpRequest { |
||||
|
pub method: String, |
||||
|
pub path: String, |
||||
|
pub version: String, |
||||
|
} |
||||
@ -0,0 +1 @@ |
|||||
|
pub mod HttpRequest; |
||||
@ -0,0 +1,27 @@ |
|||||
|
use crate::models::HttpRequest::HttpRequest; |
||||
|
use std::net::TcpStream; |
||||
|
use std::io::prelude::*; |
||||
|
|
||||
|
pub fn handle_auth_request(http_request: HttpRequest, mut stream: &TcpStream) { |
||||
|
|
||||
|
match http_request.method.as_str() { |
||||
|
"POST" => { |
||||
|
// Handle login or registration
|
||||
|
println!("Handling auth POST request for path: {}", http_request.path); |
||||
|
let response = "HTTP/1.1 200 OK\r\n\r\nAuth POST Response"; |
||||
|
stream.write_all(response.as_bytes()).unwrap(); |
||||
|
} |
||||
|
"GET" => { |
||||
|
// Handle fetching user info or logout
|
||||
|
println!("Handling auth GET request for path: {}", http_request.path); |
||||
|
let response = "HTTP/1.1 200 OK\r\n\r\nAuth GET Response"; |
||||
|
stream.write_all(response.as_bytes()).unwrap(); |
||||
|
} |
||||
|
_ => { |
||||
|
// Method not allowed
|
||||
|
let response = "HTTP/1.1 405 METHOD NOT ALLOWED\r\n\r\nMethod Not Allowed"; |
||||
|
stream.write_all(response.as_bytes()).unwrap(); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
pub mod proxy; |
||||
|
pub mod statics; |
||||
|
pub mod auth; |
||||
@ -0,0 +1,37 @@ |
|||||
|
use crate::models::HttpRequest::HttpRequest; |
||||
|
use crate::routes::statics; |
||||
|
use std::net::TcpStream; |
||||
|
use crate::routes::auth; |
||||
|
|
||||
|
fn is_file(file: &str) -> bool { |
||||
|
file.contains('.') |
||||
|
} |
||||
|
|
||||
|
pub fn proxy(http_request: HttpRequest, stream: &TcpStream) { |
||||
|
println!("Proxying request..."); |
||||
|
|
||||
|
let path = &http_request.path; |
||||
|
let parsed_path = path.trim_start_matches('/').split('/').collect::<Vec<&str>>(); |
||||
|
let target = parsed_path[parsed_path.len() -1]; |
||||
|
if is_file(target) { |
||||
|
statics::serve_static_file(http_request.clone(), stream); |
||||
|
} else { |
||||
|
handle_api_request(http_request.clone(), stream); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
pub fn handle_api_request(http_request: HttpRequest, stream: &TcpStream) { |
||||
|
|
||||
|
let parsed_path = http_request.path.trim_start_matches('/').split('/').collect::<Vec<&str>>(); |
||||
|
|
||||
|
match parsed_path[0] { |
||||
|
"api" => { |
||||
|
match parsed_path[1] { |
||||
|
"auth" => auth::handle_auth_request(http_request, &stream), |
||||
|
_ => (), |
||||
|
} |
||||
|
}, |
||||
|
_ => (), |
||||
|
}; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
use std::net::TcpStream; |
||||
|
use std::io::prelude::*; |
||||
|
|
||||
|
use crate::models::HttpRequest::HttpRequest; |
||||
|
|
||||
|
pub fn serve_static_file(file_path: HttpRequest, mut stream: &TcpStream) { |
||||
|
|
||||
|
let file = std::fs::read_to_string(format!("./static/{}", file_path.path.trim_start_matches('/'))); |
||||
|
|
||||
|
let response = match file { |
||||
|
Ok(contents) => { |
||||
|
format!( |
||||
|
"HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}", |
||||
|
contents.len(), |
||||
|
contents |
||||
|
) |
||||
|
} |
||||
|
Err(_) => { |
||||
|
let status_line = "HTTP/1.1 404 NOT FOUND\r\n\r\n"; |
||||
|
let body = "404 Not Found"; |
||||
|
format!("{}{}", status_line, body) |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
stream.write_all(response.as_bytes()).unwrap(); |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="en"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
<title>Document</title> |
||||
|
<link rel="stylesheet" href="/public/style.css"> |
||||
|
</head> |
||||
|
<body> |
||||
|
<h1>Hello, World!</h1> |
||||
|
</body> |
||||
|
</html> |
||||
@ -0,0 +1,6 @@ |
|||||
|
body { |
||||
|
font-family: Arial, sans-serif; |
||||
|
margin: 0; |
||||
|
padding: 0; |
||||
|
background-color: #FFAAAA; |
||||
|
} |
||||
Loading…
Reference in new issue