]> git.amazingca.dev Git - autumn/ws/commitdiff
Initial commit
authorAmazingca <autumn@amazingca.dev>
Wed, 27 May 2026 00:05:54 +0000 (18:05 -0600)
committerAmazingca <autumn@amazingca.dev>
Wed, 27 May 2026 00:05:54 +0000 (18:05 -0600)
.gitignore [new file with mode: 0644]
Cargo.lock [new file with mode: 0644]
Cargo.toml [new file with mode: 0644]
src/main.rs [new file with mode: 0644]
src/routes.rs [new file with mode: 0644]
src/server.rs [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..ea8c4bf
--- /dev/null
@@ -0,0 +1 @@
+/target
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644 (file)
index 0000000..94bbc72
--- /dev/null
@@ -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 (file)
index 0000000..21f0c62
--- /dev/null
@@ -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 (file)
index 0000000..61910e2
--- /dev/null
@@ -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<String>) {
+    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::<u16>()
+                .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 (file)
index 0000000..0d89f99
--- /dev/null
@@ -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, 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 (file)
index 0000000..f51b426
--- /dev/null
@@ -0,0 +1,56 @@
+pub mod server {
+
+    use crate::routes;
+    use std::{
+        env,
+        io::{BufReader, prelude::*},
+        net::TcpStream,
+    };
+
+    pub fn get_authorization(headers: &Vec<String>) -> 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<String>, 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"),
+            ),
+        }
+    }
+}