Rewrite URL handling + add example
This commit is contained in:
parent
f5ff848ff8
commit
ecb32b205c
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,3 +1 @@
|
||||
/target
|
||||
/data/*.pem
|
||||
/data/*.pfx
|
||||
target
|
||||
|
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -4,6 +4,7 @@
|
||||
name = "algem"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"native-tls",
|
||||
"url",
|
||||
]
|
||||
|
@ -9,3 +9,4 @@ edition = "2018"
|
||||
[dependencies]
|
||||
native-tls = "*"
|
||||
url = "*"
|
||||
log = "*"
|
||||
|
@ -1,6 +0,0 @@
|
||||
Hello
|
||||
|
||||
=> gemini://gemini.circumlunar.space Gemini Project
|
||||
=> /index This page
|
||||
=> /other/page Other page
|
||||
=> relative Relative page
|
480
example/Cargo.lock
generated
Normal file
480
example/Cargo.lock
generated
Normal file
@ -0,0 +1,480 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "algem"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"native-tls",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.67"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "example"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"algem",
|
||||
"env_logger",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||
dependencies = [
|
||||
"foreign-types-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
|
||||
dependencies = [
|
||||
"matches",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89829a5d69c23d348314a7ac337fe39173b61149a9864deabd260983aed48c21"
|
||||
dependencies = [
|
||||
"matches",
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "538c092e5586f4cdd7dd8078c4a79220e3e168880218124dcbce860f0ea938c6"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matches"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8d96b2e1c8da3957d58100b09f102c6d9cfdfced01b7ec5a8974044bb09dbd4"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"openssl",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a61075b62a23fef5a29815de7536d940aa35ce96d18ce0cc5076272db678a577"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"openssl-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "313752393519e876837e09e1fa183ddef0be7735868dced3196f4472d536277f"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_hc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d493c5f39e02dfb062cd8f33301f90f9b13b650e8c1b1d0fd75c19dd64bff69d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dee48cdde5ed250b0d3252818f646e174ab414036edb884dde62d80a3ac6082d"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"rand",
|
||||
"redox_syscall",
|
||||
"remove_dir_all",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
|
||||
dependencies = [
|
||||
"matches",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ccd964113622c8e9322cfac19eb1004a07e636c545f325da085d5cdde6f1f8b"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"matches",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.2+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
|
||||
[[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-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
9
example/Cargo.toml
Normal file
9
example/Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "example"
|
||||
version = "0.1.0"
|
||||
authors = ["Mark <alnyan@airmail.cc>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
algem = { path = "../" }
|
||||
env_logger = "*"
|
BIN
example/identity.pfx
Normal file
BIN
example/identity.pfx
Normal file
Binary file not shown.
12
example/src/main.rs
Normal file
12
example/src/main.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use algem::{server, ServerConfig};
|
||||
|
||||
fn main() {
|
||||
let config = ServerConfig {
|
||||
bind_address: "0.0.0.0:1965".to_string(),
|
||||
identity_file: "identity.pfx".to_string(),
|
||||
thread_count: 4
|
||||
};
|
||||
env_logger::init();
|
||||
|
||||
server::run(&config);
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
openssl req \
|
||||
-x509 \
|
||||
-nodes \
|
||||
-newkey rsa:4096 \
|
||||
-keyout data/key.pem \
|
||||
-out data/cert.pem \
|
||||
-days 365
|
||||
|
||||
openssl pkcs12 \
|
||||
-export \
|
||||
-out data/identity.pfx \
|
||||
-inkey data/key.pem \
|
||||
-in data/cert.pem
|
6
src/lib.rs
Normal file
6
src/lib.rs
Normal file
@ -0,0 +1,6 @@
|
||||
pub mod proto;
|
||||
pub mod server;
|
||||
mod pool;
|
||||
|
||||
pub use proto::*;
|
||||
pub use server::ServerConfig;
|
121
src/main.rs
121
src/main.rs
@ -1,121 +0,0 @@
|
||||
use native_tls::{Identity, TlsAcceptor};
|
||||
use std::fs::File;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::net::TcpListener;
|
||||
use std::path::Path;
|
||||
use url::Url;
|
||||
|
||||
mod pool;
|
||||
use pool::ThreadPool;
|
||||
|
||||
fn read_line<T: Read>(s: &mut String, stream: &mut T) -> Result<(), io::Error> {
|
||||
let mut byte = [0; 1];
|
||||
s.clear();
|
||||
loop {
|
||||
stream.read_exact(&mut byte)?;
|
||||
if byte[0] == b'\r' {
|
||||
continue;
|
||||
}
|
||||
if byte[0] == b'\n' {
|
||||
break;
|
||||
}
|
||||
s.push(byte[0] as char);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
enum ResponseError {
|
||||
// TODO handle malformed URLs
|
||||
// TODO handle malformed paths
|
||||
FileError(String, io::Error),
|
||||
SocketError(io::Error),
|
||||
}
|
||||
|
||||
fn handle_stream<S: Read + Write>(stream: &mut S) -> Result<String, ResponseError> {
|
||||
let mut line = String::new();
|
||||
read_line(&mut line, stream).map_err(|e| ResponseError::SocketError(e))?;
|
||||
let url = Url::parse(&line).unwrap();
|
||||
let path = String::from("data/pages/") + url.path();
|
||||
let path = Path::new(&path);
|
||||
|
||||
match File::open(path) {
|
||||
Ok(mut file) => {
|
||||
let mut buf = [0; 4096];
|
||||
// TODO ability to serve different kinds of objects?
|
||||
stream
|
||||
.write_all(b"20 text/gemini; charset=utf8\r\n")
|
||||
.map_err(|e| ResponseError::SocketError(e))?;
|
||||
|
||||
loop {
|
||||
let count = file
|
||||
.read(&mut buf)
|
||||
.map_err(|e| ResponseError::FileError(path.to_str().unwrap().to_string(), e))?;
|
||||
if count == 0 {
|
||||
break;
|
||||
}
|
||||
stream
|
||||
.write(&buf[0..count])
|
||||
.map_err(|e| ResponseError::SocketError(e))?;
|
||||
}
|
||||
|
||||
Ok(line)
|
||||
}
|
||||
Err(err) => {
|
||||
// TODO better errors
|
||||
stream
|
||||
.write_fmt(format_args!("40 {:?}\r\n", err))
|
||||
.map_err(|e| ResponseError::SocketError(e))?;
|
||||
Err(ResponseError::FileError(path.to_str().unwrap().to_string(), err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut der = vec![];
|
||||
let mut file = File::open("data/identity.pfx").unwrap();
|
||||
file.read_to_end(&mut der).unwrap();
|
||||
let identity = Identity::from_pkcs12(&der, "").unwrap();
|
||||
|
||||
let pool = ThreadPool::new(4);
|
||||
|
||||
let listener = TcpListener::bind("0.0.0.0:1965").unwrap();
|
||||
let acceptor = TlsAcceptor::new(identity).unwrap();
|
||||
|
||||
for stream in listener.incoming() {
|
||||
match stream {
|
||||
Ok(stream) => {
|
||||
let peer = stream.peer_addr().unwrap();
|
||||
let mut stream = match acceptor.accept(stream) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
eprintln!("TLS handshake failed: {}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
pool.execute(move || {
|
||||
eprint!("{} ", peer.ip());
|
||||
match handle_stream(&mut stream) {
|
||||
Err(err) => {
|
||||
match err {
|
||||
ResponseError::FileError(path, err) => {
|
||||
eprintln!("ERR {}: {:?}", path, err);
|
||||
},
|
||||
ResponseError::SocketError(err) => {
|
||||
eprintln!("ERR SOCK {:?}", err);
|
||||
},
|
||||
}
|
||||
},
|
||||
Ok(req) => {
|
||||
println!(" OK {}", req);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("accept() failed: {}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
49
src/proto.rs
Normal file
49
src/proto.rs
Normal file
@ -0,0 +1,49 @@
|
||||
use std::fmt;
|
||||
|
||||
pub enum ResponseBody {
|
||||
File(String, String),
|
||||
GeminiText(String)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ErrorCode {
|
||||
Input = 10,
|
||||
SensitiveInput = 11,
|
||||
RedirectTemporary = 30,
|
||||
RedirectPermanent = 31,
|
||||
TemporaryFailure = 40,
|
||||
ServerUnavailable = 41,
|
||||
CgiError = 42,
|
||||
ProxyError = 43,
|
||||
SlowDown = 44,
|
||||
PermanentFailure = 50,
|
||||
NotFound = 51,
|
||||
Gone = 52,
|
||||
ProxyRequestRefused = 53,
|
||||
BadRequest = 59,
|
||||
// TODO certificate something
|
||||
}
|
||||
|
||||
pub struct Error {
|
||||
code: ErrorCode,
|
||||
meta: Option<String>
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn new(code: ErrorCode, meta: Option<&str>) -> Error {
|
||||
Error {
|
||||
code,
|
||||
meta: meta.map(str::to_string)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.code.clone() as u32)?;
|
||||
if let Some(meta) = self.meta.as_ref() {
|
||||
write!(f, " {}", meta)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
123
src/server.rs
Normal file
123
src/server.rs
Normal file
@ -0,0 +1,123 @@
|
||||
use native_tls::{Identity, TlsAcceptor};
|
||||
use std::fs::File;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::net::{SocketAddr, TcpListener};
|
||||
use url::Url;
|
||||
|
||||
use log::{warn, info, error};
|
||||
use crate::proto::{self, ErrorCode, ResponseBody};
|
||||
use crate::pool::ThreadPool;
|
||||
|
||||
pub struct ServerConfig {
|
||||
pub thread_count: usize,
|
||||
pub identity_file: String,
|
||||
pub bind_address: String,
|
||||
}
|
||||
|
||||
fn read_line<T: Read>(s: &mut String, stream: &mut T) -> Result<(), io::Error> {
|
||||
let mut byte = [0; 1];
|
||||
s.clear();
|
||||
loop {
|
||||
stream.read_exact(&mut byte)?;
|
||||
if byte[0] == b'\r' {
|
||||
continue;
|
||||
}
|
||||
if byte[0] == b'\n' {
|
||||
break;
|
||||
}
|
||||
s.push(byte[0] as char);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_request(stream: &mut impl Read) -> Result<Url, proto::Error> {
|
||||
let mut line = String::new();
|
||||
if read_line(&mut line, stream).is_err() {
|
||||
return Err(proto::Error::new(
|
||||
ErrorCode::BadRequest,
|
||||
Some("Bad Request")
|
||||
));
|
||||
}
|
||||
match Url::parse(&line) {
|
||||
Ok(url) => Ok(url),
|
||||
Err(_) => Err(proto::Error::new(
|
||||
ErrorCode::BadRequest,
|
||||
Some("Malformed URL"),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn send_response_body(stream: &mut impl Write, response: ResponseBody) -> Result<(), io::Error> {
|
||||
match response {
|
||||
ResponseBody::GeminiText(data) => {
|
||||
write!(stream, "20 text/gemini; charset=utf8\r\n{}", data)
|
||||
},
|
||||
ResponseBody::File(_, _) => todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_request(_peer: SocketAddr, url: Url) -> Result<ResponseBody, proto::Error> {
|
||||
// TODO url logic
|
||||
if url.path() == "/ping" {
|
||||
return Ok(ResponseBody::GeminiText("pong".to_string()));
|
||||
}
|
||||
Err(proto::Error::new(ErrorCode::NotFound, Some(url.path())))
|
||||
}
|
||||
|
||||
fn handle_stream<S: Read + Write>(peer: SocketAddr, stream: &mut S) {
|
||||
let url = match read_request(stream) {
|
||||
// TODO fix duplicate code here?
|
||||
Ok(url) => url,
|
||||
Err(err) => {
|
||||
let _ = writeln!(stream, "{}\r\n", err);
|
||||
error!("{} {}", peer, err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
match handle_request(peer, url.clone()) {
|
||||
Ok(response) => {
|
||||
let _ = send_response_body(stream, response);
|
||||
info!("{} {} 20", peer, url);
|
||||
},
|
||||
Err(err) => {
|
||||
let _ = writeln!(stream, "{}\r\n", err);
|
||||
error!("{} {} {}", peer, url, err);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// TODO Result
|
||||
pub fn run(config: &ServerConfig) {
|
||||
info!("Listening to gemini requests on {}", &config.bind_address);
|
||||
let mut der = vec![];
|
||||
let mut file = File::open(&config.identity_file).unwrap();
|
||||
file.read_to_end(&mut der).unwrap();
|
||||
let identity = Identity::from_pkcs12(&der, "").unwrap();
|
||||
|
||||
let listener = TcpListener::bind(&config.bind_address).unwrap();
|
||||
let acceptor = TlsAcceptor::new(identity).unwrap();
|
||||
let pool = ThreadPool::new(config.thread_count);
|
||||
|
||||
for stream in listener.incoming() {
|
||||
match stream {
|
||||
Ok(stream) => {
|
||||
let peer = stream.peer_addr().unwrap();
|
||||
let mut stream = match acceptor.accept(stream) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
warn!("TLS handshake failed: {}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
pool.execute(move || {
|
||||
handle_stream(peer, &mut stream);
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
error!("accept() failed: {}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user