summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTyler Murphy <tylerm@tylerm.dev>2023-07-02 18:45:41 -0400
committerTyler Murphy <tylerm@tylerm.dev>2023-07-02 18:45:41 -0400
commitcbb92993b592e6b68dbce7f283fb73d19fd1793e (patch)
tree975d41a0cd077cee9c0d9162b82256b4e8beb07b
downloadbashttp-cbb92993b592e6b68dbce7f283fb73d19fd1793e.tar.gz
bashttp-cbb92993b592e6b68dbce7f283fb73d19fd1793e.tar.bz2
bashttp-cbb92993b592e6b68dbce7f283fb73d19fd1793e.zip
hi
Diffstat (limited to '')
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock554
-rw-r--r--Cargo.toml8
-rw-r--r--LICENSE13
-rw-r--r--README25
-rw-r--r--config.example1
-rw-r--r--src/bash.rs41
-rw-r--r--src/http/code.rs8
-rw-r--r--src/http/header.rs96
-rw-r--r--src/http/method.rs30
-rw-r--r--src/http/mod.rs6
-rw-r--r--src/http/request.rs52
-rw-r--r--src/http/response.rs42
-rw-r--r--src/http/uri.rs106
-rw-r--r--src/main.rs92
15 files changed, 1075 insertions, 0 deletions
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..ba0aab4
--- /dev/null
+++ b/Cargo.lock
@@ -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"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..0e5451e
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "bashhttp"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+tokio = { version = "1.29", features = ["full"] }
+chrono = "0.4.26"
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..f85e174
--- /dev/null
+++ b/LICENSE
@@ -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.
diff --git a/README b/README
new file mode 100644
index 0000000..d68c6cc
--- /dev/null
+++ b/README
@@ -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
diff --git a/config.example b/config.example
new file mode 100644
index 0000000..8f9aba6
--- /dev/null
+++ b/config.example
@@ -0,0 +1 @@
+/ /usr/bin/neofetch
diff --git a/src/bash.rs b/src/bash.rs
new file mode 100644
index 0000000..059909f
--- /dev/null
+++ b/src/bash.rs
@@ -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())
+}
diff --git a/src/http/code.rs b/src/http/code.rs
new file mode 100644
index 0000000..ba47282
--- /dev/null
+++ b/src/http/code.rs
@@ -0,0 +1,8 @@
+#[derive(Debug, Clone)]
+#[allow(dead_code)]
+pub enum Code {
+ Success = 200,
+ MethodNotAllowed = 405,
+ TooManyRequests = 429,
+ InternalServerError = 500,
+}
diff --git a/src/http/header.rs b/src/http/header.rs
new file mode 100644
index 0000000..e6fc552
--- /dev/null
+++ b/src/http/header.rs
@@ -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()}
+ }
+}
diff --git a/src/http/method.rs b/src/http/method.rs
new file mode 100644
index 0000000..55cea65
--- /dev/null
+++ b/src/http/method.rs
@@ -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
+ }
+ }
+}
+
diff --git a/src/http/mod.rs b/src/http/mod.rs
new file mode 100644
index 0000000..62151bb
--- /dev/null
+++ b/src/http/mod.rs
@@ -0,0 +1,6 @@
+pub mod code;
+pub mod method;
+pub mod uri;
+pub mod request;
+pub mod response;
+pub mod header;
diff --git a/src/http/request.rs b/src/http/request.rs
new file mode 100644
index 0000000..5ba72c9
--- /dev/null
+++ b/src/http/request.rs
@@ -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 },
+ })
+ }
+}
diff --git a/src/http/response.rs b/src/http/response.rs
new file mode 100644
index 0000000..850f41e
--- /dev/null
+++ b/src/http/response.rs
@@ -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
+ }
+}
diff --git a/src/http/uri.rs b/src/http/uri.rs
new file mode 100644
index 0000000..8faff69
--- /dev/null
+++ b/src/http/uri.rs
@@ -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()
+ })
+ }
+
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..9cf8e90
--- /dev/null
+++ b/src/main.rs
@@ -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;
+ });
+ }
+}