Browse Source

Initial commit

master
astria 3 months ago
commit
e1237dfa6f
  1. 1753
      Cargo.lock
  2. 8
      Cargo.toml
  3. 66
      src/http.rs
  4. 12
      src/main.rs
  5. 6
      src/models/HttpRequest.rs
  6. 1
      src/models/mod.rs
  7. 27
      src/routes/auth.rs
  8. 3
      src/routes/mod.rs
  9. 37
      src/routes/proxy.rs
  10. 26
      src/routes/statics.rs
  11. 12
      static/index.html
  12. 6
      static/public/style.css

1753
Cargo.lock

File diff suppressed because it is too large

8
Cargo.toml

@ -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"]}

66
src/http.rs

@ -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())
}
}

12
src/main.rs

@ -0,0 +1,12 @@
mod http;
mod routes;
mod models;
#[tokio::main]
async fn main() {
println!("Starting application...");
http::http_server();
}

6
src/models/HttpRequest.rs

@ -0,0 +1,6 @@
#[derive(Debug, Clone,)]
pub struct HttpRequest {
pub method: String,
pub path: String,
pub version: String,
}

1
src/models/mod.rs

@ -0,0 +1 @@
pub mod HttpRequest;

27
src/routes/auth.rs

@ -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;
}
}
}

3
src/routes/mod.rs

@ -0,0 +1,3 @@
pub mod proxy;
pub mod statics;
pub mod auth;

37
src/routes/proxy.rs

@ -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),
_ => (),
}
},
_ => (),
};
}

26
src/routes/statics.rs

@ -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();
}

12
static/index.html

@ -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>

6
static/public/style.css

@ -0,0 +1,6 @@
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #FFAAAA;
}
Loading…
Cancel
Save