From f649c7d389aad56178d36169d134e9473f6e3074 Mon Sep 17 00:00:00 2001 From: Amazingca Date: Tue, 26 May 2026 18:05:54 -0600 Subject: [PATCH 1/1] Initial commit --- .gitignore | 1 + Cargo.lock | 54 ++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 9 +++++++ src/main.rs | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/routes.rs | 22 ++++++++++++++++ src/server.rs | 56 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 211 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/main.rs create mode 100644 src/routes.rs create mode 100644 src/server.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..94bbc72 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,54 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "ws" +version = "0.1.0" +dependencies = [ + "regex", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..21f0c62 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ws" +version = "0.1.0" +edition = "2024" +description = "web-sync project" +repository = "https://tangled.org/autumn.bsky.gay/ws" + +[dependencies] +regex = "1.12.3" diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..61910e2 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,69 @@ +use regex::Regex; +use std::{env, net::TcpListener, process::exit, thread}; + +mod routes; +mod server; + +struct ServerConfig { + port: u16, + ip: String, +} + +fn args_handler(conf: &mut ServerConfig, args: Vec) { + if args.iter().any(|i| i == "--help") { + println!( + r" + --help: print this help message + --ip: set the IP address to bind the server to + --port: set the port for the server + --token: token used to authenticate" + ); + exit(0); + } + + for item in args { + if item.contains("--ip=") { + conf.ip = item.replace("--ip=", ""); + let checker = Regex::new(r"^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$").unwrap(); // Grabbed from https://stackoverflow.com/questions/5284147/validating-ipv4-addresses-with-regexp + if !checker.is_match(&conf.ip) { + println!("IP provided is not within acceptable range!"); + exit(-1); + } + } else if item.contains("--port=") { + conf.port = item + .replace("--port=", "") + .parse::() + .unwrap_or_else(|e| { + println!("Port provided must be within 16-bit range! => {e}"); + exit(-1); + }); + } else if item.contains("--token=") { + unsafe { + env::set_var("token", item.replace("--token=", "")); + } + } + } +} + +fn main() { + println!("{}", routes::home().1); + + let mut conf = ServerConfig { + port: 7878, + ip: String::from("127.0.0.1"), + }; + + args_handler(&mut conf, env::args().collect()); + + println!("Started server on {}:{}", conf.ip, conf.port); + + let listener = TcpListener::bind(format!("{}:{}", conf.ip, conf.port)).unwrap(); + + for stream in listener.incoming() { + let stream = stream.unwrap(); + + thread::spawn(|| { + server::server::handle_connection(stream); + }); + } +} diff --git a/src/routes.rs b/src/routes.rs new file mode 100644 index 0000000..0d89f99 --- /dev/null +++ b/src/routes.rs @@ -0,0 +1,22 @@ +use crate::server::server::get_authorization; + +pub fn home() -> (String, String) { + ( + String::from("200 OK"), + format!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")), + ) +} + +pub fn auth(headers: &Vec) -> (String, String) { + if !get_authorization(headers) { + ( + String::from("403 FORBIDDEN"), + String::from("You are not authorized to view this endpoint"), + ) + } else { + ( + String::from("200 OK"), + String::from("Successfully authenticated!"), + ) + } +} diff --git a/src/server.rs b/src/server.rs new file mode 100644 index 0000000..f51b426 --- /dev/null +++ b/src/server.rs @@ -0,0 +1,56 @@ +pub mod server { + + use crate::routes; + use std::{ + env, + io::{BufReader, prelude::*}, + net::TcpStream, + }; + + pub fn get_authorization(headers: &Vec) -> bool { + match env::var("token") { + Ok(x) => { + for header in headers { + if header.contains("Authorization: Basic ") { + return header.replace("Authorization: Basic ", "") == x; + } + } + } + Err(_) => return false, + } + + false + } + + pub fn handle_connection(mut stream: TcpStream) { + let buf_reader = BufReader::new(&stream); + let request_data: Vec<_> = buf_reader + .lines() + .map(|result| result.unwrap()) + .take_while(|line| !line.is_empty()) + .collect(); + + println!("{request_data:#?}"); + + let request_line = &request_data[0]; + + let (status, response) = router(&request_data, &request_line); + let status_line = format!("HTTP/1.1 {}", status); + let length = response.len(); + + let response = format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{response}"); + + stream.write_all(response.as_bytes()).unwrap(); + } + + fn router(headers: &Vec, request_line: &String) -> (String, String) { + match request_line.as_str() { + "GET / HTTP/1.1" => routes::home(), + "GET /auth HTTP/1.1" => routes::auth(headers), + _ => ( + String::from("404 NOT FOUND"), + String::from("The provided endpoint was not found"), + ), + } + } +} -- 2.54.0