This commit is contained in:
Freya Murphy 2023-07-02 18:45:41 -04:00
commit cbb92993b5
15 changed files with 1075 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

554
Cargo.lock generated Normal file
View file

@ -0,0 +1,554 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "backtrace"
version = "0.3.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bumpalo"
version = "3.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
[[package]]
name = "bytes"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"time",
"wasm-bindgen",
"winapi",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
[[package]]
name = "gimli"
version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
[[package]]
name = "hermit-abi"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "iana-time-zone"
version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "js-sys"
version = "0.3.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "lock_api"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "miniz_oxide"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
dependencies = [
"adler",
]
[[package]]
name = "mio"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
dependencies = [
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "object"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
]
[[package]]
name = "pin-project-lite"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57"
[[package]]
name = "proc-macro2"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
"bitflags",
]
[[package]]
name = "rustc-demangle"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "signal-hook-registry"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
dependencies = [
"libc",
]
[[package]]
name = "smallvec"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
name = "socket2"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "syn"
version = "2.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "time"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
dependencies = [
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
"winapi",
]
[[package]]
name = "tokio"
version = "1.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da"
dependencies = [
"autocfg",
"backtrace",
"bytes",
"libc",
"mio",
"num_cpus",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys",
]
[[package]]
name = "tokio-macros"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
[[package]]
name = "web"
version = "0.1.0"
dependencies = [
"chrono",
"tokio",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"

8
Cargo.toml Normal file
View file

@ -0,0 +1,8 @@
[package]
name = "bashhttp"
version = "0.1.0"
edition = "2021"
[dependencies]
tokio = { version = "1.29", features = ["full"] }
chrono = "0.4.26"

13
LICENSE Normal file
View file

@ -0,0 +1,13 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2023 Tyler Murphy <tylerm@tylerm.dev>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

25
README Normal file
View file

@ -0,0 +1,25 @@
# bashttp
A very goofy http server that runs scripts based of the given URI route
## Config
All routs and scripts must be layed out in a file called config, or you can specift the path by setting the `CONFIG_PATH` env variable.
An example config is shown below
```
/ ./hello_world
/neo /usr/bin/neofetch
/joe ./bide
```
As shown above, each route to script is a single line seperated by a single space.
## License
This project is licensed under the [WTFPL](https://www.wtfpl.net/)
## Warrenty
This project is probably not secure and if it beaks, uh... have fun
...wtfpl

1
config.example Normal file
View file

@ -0,0 +1 @@
/ /usr/bin/neofetch

41
src/bash.rs Normal file
View file

@ -0,0 +1,41 @@
use std::{env, collections::HashMap, fs::read_to_string, process::{exit, Command}};
pub fn load_config() -> HashMap<String, String> {
let config_path = env::var("CONFIG_PATH").unwrap_or_else(|_| String::from("config"));
let config = match read_to_string(&config_path) {
Ok(data) => data,
Err(err) => {
eprintln!("cannot load '{config_path}': {err}");
exit(1);
},
};
let mut map = HashMap::new();
let lines = config.split("\n").into_iter();
for line in lines {
let mut parts = line.trim().split(" ");
let Some(route) = parts.next() else { continue };
let Some(script) = parts.next() else { continue };
println!("adding entry {route} => {script}");
map.insert(route.to_owned(), script.to_owned());
}
map
}
pub fn handle_script(script: &str, body: Option<&String>) -> Result<String, String> {
let mut command = Command::new(script);
if let Some(body) = body {
command.args([body]);
}
let output = match command.output() {
Ok(o) => o,
Err(err) => return Err(format!("{err}")),
};
Ok(String::from_utf8_lossy(&output.stdout).into_owned())
}

8
src/http/code.rs Normal file
View file

@ -0,0 +1,8 @@
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub enum Code {
Success = 200,
MethodNotAllowed = 405,
TooManyRequests = 429,
InternalServerError = 500,
}

96
src/http/header.rs Normal file
View file

@ -0,0 +1,96 @@
use std::{collections::HashMap, str::Split};
#[derive(Debug, Clone)]
pub struct HeaderMap {
map: HashMap<String, usize>,
headers: Vec<Header>
}
impl HeaderMap {
#[allow(dead_code)]
pub fn get(&self, key: &str) -> Option<&Header> {
let Some(index) = self.map.get(key) else {
return None;
};
return Some(&self.headers[index.to_owned()]);
}
pub fn put(&mut self, header: Header) {
if let Some(index) = self.map.get(&header.key) {
self.headers[index.to_owned()] = header;
return
}
let index = self.headers.len();
self.map.insert(header.key.clone(), index);
self.headers.push(header);
}
#[allow(dead_code)]
pub fn del(&mut self, key: &str) -> Option<Header> {
let Some(index) = self.map.get(key) else {
return None
};
let removed = self.headers.remove(index.to_owned());
for i in (index.to_owned())..self.headers.len() {
let key = &self.headers[i].key;
let Some(index) = self.map.get(key) else {
continue;
};
self.map.insert(key.clone(), index - 1);
}
Some(removed)
}
pub fn deserialize(&self) -> String {
let mut string = String::new();
for header in &self.headers {
string += &format!("{}: {}\n", header.key, header.value);
}
string
}
pub fn serialize(lines: &mut Split<char>) -> Self {
let mut headers = Self::new();
loop {
let Some(header) = lines.next() else { break };
if header.trim().len() < 1 { break }
let mut parts = header.split(": ").into_iter();
let Some(key) = parts.next() else { continue };
let Some(value) = parts.next() else { continue };
headers.put(Header::new(key.trim(), value.trim()));
}
headers
}
pub fn new() -> Self {
Self {
map: HashMap::new(),
headers: Vec::new()
}
}
}
#[derive(Debug, Clone)]
pub struct Header {
pub key: String,
pub value: String
}
impl Header {
pub fn new(key: &str, value: &str) -> Self {
return Self {key: key.to_owned(), value: value.to_owned()}
}
}

30
src/http/method.rs Normal file
View file

@ -0,0 +1,30 @@
#[derive(Debug, Clone)]
pub enum Method {
Get,
Head,
Post,
Put,
Delete,
Connect,
Options,
Trace,
Patch
}
impl Method {
pub fn serialize(string: &str) -> Option<Self> {
match string {
"GET" => Some(Self::Get),
"HEAD" => Some(Self::Head),
"POST" => Some(Self::Post),
"PUT" => Some(Self::Put),
"DELETE" => Some(Self::Delete),
"CONNECT" => Some(Self::Connect),
"OPTIONS" => Some(Self::Options),
"TRACE" => Some(Self::Trace),
"PATCH" => Some(Self::Patch),
_ => None
}
}
}

6
src/http/mod.rs Normal file
View file

@ -0,0 +1,6 @@
pub mod code;
pub mod method;
pub mod uri;
pub mod request;
pub mod response;
pub mod header;

52
src/http/request.rs Normal file
View file

@ -0,0 +1,52 @@
use super::{method::Method, uri::URI, header::HeaderMap};
#[derive(Debug, Clone)]
pub struct Request {
pub method: Method,
pub uri: URI,
pub headers: HeaderMap,
pub body: Option<String>
}
impl Request {
pub fn serialize(req: &str) -> Option<Self> {
let mut lines = req.split('\n').to_owned();
let Some(head) = lines.next() else {
eprintln!("missing head str");
return None
};
let mut parts = head.trim().split(" ");
let Some(method_str) = parts.next() else {
eprintln!("missing method str");
return None
};
let Some(method) = Method::serialize(method_str.trim()) else {
eprintln!("invalid http method");
return None
};
let Some(uri_str) = parts.next() else {
eprintln!("missing uri str");
return None
};
let Some(uri) = URI::serialize(uri_str.trim()) else {
eprintln!("invalid http uri");
return None
};
let headers = HeaderMap::serialize(&mut lines);
let body: String = lines.collect();
Some(Self {
method,
uri,
headers,
body: if body.len() > 0 { Some(body) } else { None },
})
}
}

42
src/http/response.rs Normal file
View file

@ -0,0 +1,42 @@
use super::{code::Code, header::{HeaderMap, Header}};
#[derive(Debug, Clone)]
pub struct Response {
pub status: Code,
pub headers: HeaderMap,
pub body: Option<String>
}
impl Response {
pub fn new() -> Self {
let mut headers = HeaderMap::new();
headers.put(Header::new("Connection", "close"));
let date = chrono::offset::Utc::now();
headers.put(Header::new("Date", &date.to_rfc2822()));
headers.put(Header::new("Server", "bashttp"));
return Self {
status: Code::Success,
headers,
body: None
}
}
pub fn deserialize(&self) -> String {
let mut string = String::new();
string += &format!("HTTP/1.1 {}\n", self.status.clone() as u16);
string += &self.headers.deserialize();
if let Some(body) = &self.body {
string += "\n";
string += body;
}
string
}
}

106
src/http/uri.rs Normal file
View file

@ -0,0 +1,106 @@
#[derive(Debug, Clone)]
pub enum Protocol {
HTTP,
HTTPS,
}
impl Protocol {
pub fn serialize(string: &str) -> Option<Self> {
match string {
"http" => return Some(Self::HTTP),
"https" => return Some(Self::HTTPS),
_ => return None
}
}
pub fn deserialize(&self) -> &str {
match self {
Self::HTTP => "http",
Self::HTTPS => "https",
}
}
}
#[derive(Debug, Clone)]
pub struct URI {
pub protocol: Option<Protocol>,
pub host: Option<String>,
pub port: Option<u16>,
pub route: String
}
impl URI {
#[allow(dead_code)]
pub fn deserialize(&self) -> String {
let mut string = String::new();
if let Some(protocol) = &self.protocol {
string += protocol.deserialize();
string += "://";
}
if let Some(host) = &self.host {
string += host;
}
if let Some(port) = self.port {
string += &format!(":{port}");
}
string += &self.route;
string
}
pub fn serialize(head: &str) -> Option<Self> {
let protocol_end = match head.find("://") {
Some(i) => i,
None => 0
};
let protocol: Option<Protocol>;
let host_start: usize;
if protocol_end == 0 {
protocol = None;
host_start = 0;
} else {
let Some(p) = Protocol::serialize(&head[..protocol_end]) else {
return None
};
protocol = Some(p);
host_start = protocol_end + 3;
}
let host_route = &head[host_start..];
let host_end = host_route.find("/").unwrap_or(head.len());
let host: Option<String>;
let port: Option<u16>;
if host_start == host_end {
host = None;
port = None;
} else {
if let Some (host_split) = host_route.find(":") {
let port_start = host_split + 1;
let port_str = &head[port_start..host_end];
let Ok(p) = port_str.parse::<u16>() else {
return None
};
host = Some(head[host_start..host_split].to_owned());
port = Some(p);
} else {
host = Some(head[host_start..host_end].to_owned());
port = None;
}
}
let route = &head[host_end..];
Some(Self {
protocol,
host,
port,
route: route.to_owned()
})
}
}

92
src/main.rs Normal file
View file

@ -0,0 +1,92 @@
use std::collections::HashMap;
use std::sync::Arc;
use http::code::Code;
use http::header::Header;
use http::request::Request;
use http::response::Response;
use tokio::net::{TcpListener, TcpStream};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use crate::bash::handle_script;
mod http;
mod bash;
async fn handle_response(mut socket: TcpStream, code: Code, body: String) {
let mut res = Response::new();
res.headers.put(Header::new("Content-Type", "text/plain"));
res.status = code;
res.body = Some(body);
let res_str = res.deserialize();
let _ = socket.write(res_str.as_bytes()).await;
}
async fn handle_connection(mut socket: TcpStream, config: Arc<HashMap<String, String>>) {
let mut buf = [0; 1204];
let n: usize = match socket.read(&mut buf).await {
Ok(n) if n == 0 => return,
Ok(n) => n as usize,
Err(e) => {
eprintln!("failed to read from socket; err = {:?}", e);
return
}
};
let str = String::from_utf8_lossy(&buf[0..n]);
let Some(req) = Request::serialize(&str) else {
return
};
let Some(script) = config.get(&req.uri.route) else {
handle_response(socket, Code::MethodNotAllowed, "Method Not Allowed".to_owned()).await;
return
};
match handle_script(script, req.body.as_ref()) {
Ok(out) => {
handle_response(socket, Code::Success, out).await;
},
Err(err) => {
handle_response(socket, Code::MethodNotAllowed, err).await;
},
}
}
#[tokio::main]
async fn main() {
let config = Arc::new(bash::load_config());
let port = std::env::var("PORT")
.unwrap_or_else(|_| String::from("8080"))
.parse::<u16>()
.unwrap_or_else(|_| 8080);
let addr = format!("127.0.0.1:{port}");
let Ok(listener) = TcpListener::bind(&addr).await else {
println!("failed to bind {addr}");
return
};
println!("listening to tcp requests on {addr}");
loop {
let Ok((socket, _)) = listener.accept().await else {
println!("failed to accept new connection");
continue
};
let config = config.clone();
tokio::spawn(async move {
handle_connection(socket, config).await;
});
}
}