Garrit Franke
3 years ago
12 changed files with 1146 additions and 23 deletions
@ -0,0 +1,359 @@
|
||||
# This file is automatically @generated by Cargo. |
||||
# It is not intended for manual editing. |
||||
[[package]] |
||||
name = "autocfg" |
||||
version = "1.0.1" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" |
||||
|
||||
[[package]] |
||||
name = "bitflags" |
||||
version = "1.3.2" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" |
||||
|
||||
[[package]] |
||||
name = "cc" |
||||
version = "1.0.70" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0" |
||||
|
||||
[[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 = "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 = "gempress" |
||||
version = "0.1.0" |
||||
dependencies = [ |
||||
"native-tls", |
||||
"thiserror", |
||||
] |
||||
|
||||
[[package]] |
||||
name = "getrandom" |
||||
version = "0.2.3" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" |
||||
dependencies = [ |
||||
"cfg-if", |
||||
"libc", |
||||
"wasi", |
||||
] |
||||
|
||||
[[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.101" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" |
||||
|
||||
[[package]] |
||||
name = "log" |
||||
version = "0.4.14" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" |
||||
dependencies = [ |
||||
"cfg-if", |
||||
] |
||||
|
||||
[[package]] |
||||
name = "native-tls" |
||||
version = "0.2.8" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" |
||||
dependencies = [ |
||||
"lazy_static", |
||||
"libc", |
||||
"log", |
||||
"openssl", |
||||
"openssl-probe", |
||||
"openssl-sys", |
||||
"schannel", |
||||
"security-framework", |
||||
"security-framework-sys", |
||||
"tempfile", |
||||
] |
||||
|
||||
[[package]] |
||||
name = "once_cell" |
||||
version = "1.8.0" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" |
||||
|
||||
[[package]] |
||||
name = "openssl" |
||||
version = "0.10.36" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "8d9facdb76fec0b73c406f125d44d86fdad818d66fef0531eec9233ca425ff4a" |
||||
dependencies = [ |
||||
"bitflags", |
||||
"cfg-if", |
||||
"foreign-types", |
||||
"libc", |
||||
"once_cell", |
||||
"openssl-sys", |
||||
] |
||||
|
||||
[[package]] |
||||
name = "openssl-probe" |
||||
version = "0.1.4" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" |
||||
|
||||
[[package]] |
||||
name = "openssl-sys" |
||||
version = "0.9.66" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "1996d2d305e561b70d1ee0c53f1542833f4e1ac6ce9a6708b6ff2738ca67dc82" |
||||
dependencies = [ |
||||
"autocfg", |
||||
"cc", |
||||
"libc", |
||||
"pkg-config", |
||||
"vcpkg", |
||||
] |
||||
|
||||
[[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 = "proc-macro2" |
||||
version = "1.0.29" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" |
||||
dependencies = [ |
||||
"unicode-xid", |
||||
] |
||||
|
||||
[[package]] |
||||
name = "quote" |
||||
version = "1.0.9" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" |
||||
dependencies = [ |
||||
"proc-macro2", |
||||
] |
||||
|
||||
[[package]] |
||||
name = "rand" |
||||
version = "0.8.4" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" |
||||
dependencies = [ |
||||
"libc", |
||||
"rand_chacha", |
||||
"rand_core", |
||||
"rand_hc", |
||||
] |
||||
|
||||
[[package]] |
||||
name = "rand_chacha" |
||||
version = "0.3.1" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" |
||||
dependencies = [ |
||||
"ppv-lite86", |
||||
"rand_core", |
||||
] |
||||
|
||||
[[package]] |
||||
name = "rand_core" |
||||
version = "0.6.3" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" |
||||
dependencies = [ |
||||
"getrandom", |
||||
] |
||||
|
||||
[[package]] |
||||
name = "rand_hc" |
||||
version = "0.3.1" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" |
||||
dependencies = [ |
||||
"rand_core", |
||||
] |
||||
|
||||
[[package]] |
||||
name = "redox_syscall" |
||||
version = "0.2.10" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" |
||||
dependencies = [ |
||||
"bitflags", |
||||
] |
||||
|
||||
[[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.4.2" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87" |
||||
dependencies = [ |
||||
"bitflags", |
||||
"core-foundation", |
||||
"core-foundation-sys", |
||||
"libc", |
||||
"security-framework-sys", |
||||
] |
||||
|
||||
[[package]] |
||||
name = "security-framework-sys" |
||||
version = "2.4.2" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" |
||||
dependencies = [ |
||||
"core-foundation-sys", |
||||
"libc", |
||||
] |
||||
|
||||
[[package]] |
||||
name = "syn" |
||||
version = "1.0.76" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84" |
||||
dependencies = [ |
||||
"proc-macro2", |
||||
"quote", |
||||
"unicode-xid", |
||||
] |
||||
|
||||
[[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 = "thiserror" |
||||
version = "1.0.28" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "283d5230e63df9608ac7d9691adc1dfb6e701225436eb64d0b9a7f0a5a04f6ec" |
||||
dependencies = [ |
||||
"thiserror-impl", |
||||
] |
||||
|
||||
[[package]] |
||||
name = "thiserror-impl" |
||||
version = "1.0.28" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "fa3884228611f5cd3608e2d409bf7dce832e4eb3135e3f11addbd7e41bd68e71" |
||||
dependencies = [ |
||||
"proc-macro2", |
||||
"quote", |
||||
"syn", |
||||
] |
||||
|
||||
[[package]] |
||||
name = "unicode-xid" |
||||
version = "0.2.2" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" |
||||
|
||||
[[package]] |
||||
name = "vcpkg" |
||||
version = "0.2.15" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" |
||||
|
||||
[[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-x86_64-pc-windows-gnu" |
||||
version = "0.4.0" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" |
@ -1,24 +1,16 @@
|
||||
mod gempress; |
||||
extern crate gempress; |
||||
|
||||
use gempress::{Gempress, Request, Response}; |
||||
use std::path::Path; |
||||
|
||||
pub fn handler(req: &Box<Request>, res: &mut Box<Response>) { |
||||
res.send("Hello World!\n"); |
||||
} |
||||
use gempress::Gempress; |
||||
use std::path::PathBuf; |
||||
|
||||
fn main() { |
||||
// Run make_cert.sh to generate a certificate
|
||||
let config = Gempress::Config { |
||||
certPath: Path::new("../cert.pem"); |
||||
keyPath: Path::new("../key.pem"); |
||||
} |
||||
let config = gempress::Config::with_identity(PathBuf::from("identity.pfx"), "qqqq".into()); |
||||
|
||||
let app = Gempress::new(config); |
||||
|
||||
app.on("/", handler); |
||||
|
||||
app.listen(1965, || { |
||||
println!("Listening on port 1965"); |
||||
}); |
||||
}).unwrap(); |
||||
} |
||||
} |
||||
|
@ -1 +1,7 @@
|
||||
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes -subj '/CN=localhost' |
||||
|
||||
openssl pkcs12 -export -out identity.pfx -inkey key.pem -in cert.pem |
||||
|
||||
# Cleanup |
||||
rm key.pem cert.pem |
||||
|
||||
|
@ -0,0 +1,28 @@
|
||||
use std::io; |
||||
use thiserror::Error; |
||||
|
||||
pub type GempressResult<T> = Result<T, GempressError>; |
||||
|
||||
#[derive(Error, Debug)] |
||||
pub enum GempressError { |
||||
#[error("failed to open identity file: {0}")] |
||||
NoIdentity(io::Error), |
||||
|
||||
#[error("failed parse certificate: {0:#?}")] |
||||
InvalidCertificate(#[from] native_tls::Error), |
||||
|
||||
#[error("invalid request: {0}")] |
||||
InvalidRequest(String), |
||||
|
||||
#[error("invalid Unicode character in the input")] |
||||
InvalidUnicode(#[from] std::string::FromUtf8Error), |
||||
|
||||
#[error("failed to bind: {0}")] |
||||
BindFailed(io::Error), |
||||
|
||||
#[error("could not read the stream")] |
||||
StreamReadFailed(io::Error), |
||||
|
||||
#[error("could not write to the stream")] |
||||
StreamWriteFailed(io::Error), |
||||
} |
@ -0,0 +1,147 @@
|
||||
use crate::error::{GempressError, GempressResult}; |
||||
use native_tls::TlsStream; |
||||
use std::{io::Write, net::TcpStream, str::FromStr}; |
||||
use url::Url; |
||||
|
||||
#[derive(Debug, PartialEq, Eq)] |
||||
pub struct Request { |
||||
url: Url, |
||||
} |
||||
|
||||
impl Request { |
||||
pub fn parse(request: &str) -> GempressResult<Self> { |
||||
Self::from_str(request) |
||||
} |
||||
|
||||
/// Get file path
|
||||
pub fn file_path(&self) -> &str { |
||||
self.url |
||||
.path() |
||||
.chars() |
||||
.next() |
||||
.map_or("", |c| &self.url.path()[c.len_utf8()..]) |
||||
} |
||||
} |
||||
|
||||
impl FromStr for Request { |
||||
type Err = GempressError; |
||||
|
||||
fn from_str(s: &str) -> GempressResult<Self> { |
||||
let mut s = s.to_string(); |
||||
|
||||
// Add gemini: scheme if not explicitly set
|
||||
if s.starts_with("//") { |
||||
s = format!("gemini:{}", s); |
||||
} |
||||
|
||||
// Check protocol
|
||||
if let Some(proto_end) = s.find("://") { |
||||
// If set, check if it's allowed
|
||||
let protocol = &s[..proto_end]; |
||||
|
||||
if protocol != "gemini" { |
||||
// TODO: return 53 error instead of dropping
|
||||
return Err(GempressError::InvalidRequest("invalid protocol".into())); |
||||
} |
||||
} else { |
||||
// If no protocol is found, gemini: is implied
|
||||
s = format!("gemini://{}", s); |
||||
} |
||||
|
||||
// Extract and parse the url from the request.
|
||||
let raw = s |
||||
.trim_end_matches(0x0 as char) |
||||
.strip_suffix("\r\n") |
||||
.ok_or_else(|| GempressError::InvalidRequest("malformed request".into()))?; |
||||
let url = Url::parse(&raw) |
||||
.map_err(|e| GempressError::InvalidRequest(format!("invalid url: {}", e)))?; |
||||
|
||||
Ok(Self { url }) |
||||
} |
||||
} |
||||
|
||||
pub struct Response { |
||||
pub status: [u8; 2], |
||||
pub meta: Vec<u8>, |
||||
pub body: Option<Vec<u8>>, |
||||
} |
||||
|
||||
impl Response { |
||||
pub fn success(body: Vec<u8>, mime_type: &str) -> Self { |
||||
Response { |
||||
status: [b'2', b'0'], |
||||
meta: mime_type.as_bytes().to_vec(), |
||||
body: Some(body), |
||||
} |
||||
} |
||||
|
||||
pub fn not_found() -> Self { |
||||
Response { |
||||
status: [b'5', b'1'], |
||||
meta: "Resource not found".into(), |
||||
body: None, |
||||
} |
||||
} |
||||
|
||||
pub fn send(&self, mut stream: TlsStream<TcpStream>) -> GempressResult<usize> { |
||||
let mut buf: Vec<u8> = Vec::new(); |
||||
|
||||
// <Status>
|
||||
buf.extend(&self.status); |
||||
|
||||
// <Space>
|
||||
buf.push(0x20); |
||||
|
||||
// <Meta>
|
||||
buf.extend(&self.meta); |
||||
|
||||
buf.extend(b"\r\n"); |
||||
|
||||
if let Some(body) = &self.body { |
||||
buf.extend(body); |
||||
} |
||||
|
||||
stream.write(&buf).map_err(GempressError::StreamWriteFailed) |
||||
} |
||||
} |
||||
|
||||
#[cfg(test)] |
||||
mod tests { |
||||
use super::*; |
||||
|
||||
fn check_request(raw: &str, expected_url: &str) { |
||||
let req = Request::parse(raw).unwrap(); |
||||
|
||||
assert_eq!( |
||||
req, |
||||
Request { |
||||
url: Url::parse(expected_url).unwrap() |
||||
} |
||||
); |
||||
} |
||||
|
||||
#[test] |
||||
fn parse_request() { |
||||
check_request("gemini://example.space\r\n", "gemini://example.space"); |
||||
} |
||||
|
||||
#[test] |
||||
fn parse_without_scheme() { |
||||
check_request("example.space\r\n", "gemini://example.space"); |
||||
} |
||||
|
||||
#[test] |
||||
fn parse_without_scheme_double_slash() { |
||||
check_request("//example.space\r\n", "gemini://example.space"); |
||||
} |
||||
|
||||
#[test] |
||||
fn parse_malformed_request() { |
||||
let raw = "gemini://example.space"; |
||||
|
||||
match Request::parse(raw) { |
||||
Err(TaurusError::InvalidRequest(_)) => {} |
||||
x => panic!("expected TaurusError::InvalidRequest, got: {:?}", x), |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,54 @@
|
||||
use crate::error::{GempressError, GempressResult}; |
||||
use native_tls::Identity; |
||||
use std::fs::File; |
||||
use std::io::{self, Read}; |
||||
use std::path::Path; |
||||
|
||||
/// Read a file into Vec
|
||||
pub fn read_file(file_path: &str) -> Result<Vec<u8>, io::Error> { |
||||
let mut file = File::open(file_path)?; |
||||
let mut buf = Vec::new(); |
||||
|
||||
file.read_to_end(&mut buf)?; |
||||
|
||||
Ok(buf) |
||||
} |
||||
|
||||
/// Read certificate file
|
||||
pub fn load_cert(cert_file: &str, password: &str) -> GempressResult<Identity> { |
||||
let identity = read_file(&cert_file).map_err(GempressError::NoIdentity)?; |
||||
let identity = Identity::from_pkcs12(&identity, &password)?; |
||||
|
||||
Ok(identity) |
||||
} |
||||
|
||||
/// Resolve path to a file, returning index.gmi if a subdirectory is encountered
|
||||
///
|
||||
/// If path points to a file, it is returned.
|
||||
/// If path points to a directory, `./index.gmi` is returned
|
||||
pub fn resolve_path(path: &Path) -> String { |
||||
if path.is_dir() { |
||||
path.join("index.gmi").to_string_lossy().into_owned() |
||||
} else { |
||||
path.to_string_lossy().into_owned() |
||||
} |
||||
} |
||||
|
||||
#[cfg(test)] |
||||
mod tests { |
||||
use super::*; |
||||
|
||||
#[test] |
||||
fn resolve_path_file() { |
||||
let path = Path::new("./file.gmi"); |
||||
|
||||
assert_eq!(resolve_path(&path), String::from("./file.gmi")); |
||||
} |
||||
|
||||
#[test] |
||||
fn resolve_path_dir() { |
||||
let path = Path::new("./"); |
||||
|
||||
assert_eq!(resolve_path(&path), String::from("./index.gmi")); |
||||
} |
||||
} |
@ -1,11 +1,95 @@
|
||||
pub fn add(a: u32, b: u32) -> u32 { |
||||
return a + b; |
||||
extern crate native_tls; |
||||
|
||||
mod logger; |
||||
mod io; |
||||
mod error; |
||||
mod gemini; |
||||
|
||||
use std::io::Read; |
||||
use native_tls::Identity; |
||||
use std::sync::Arc; |
||||
use std::path::PathBuf; |
||||
use std::net::{TcpListener, TcpStream}; |
||||
use std::thread; |
||||
use std::path; |
||||
use native_tls::{TlsAcceptor, TlsStream}; |
||||
use error::{GempressResult, GempressError}; |
||||
|
||||
pub struct Config { |
||||
// Path to the identity file
|
||||
identityPath: PathBuf, |
||||
password: String, |
||||
} |
||||
|
||||
impl Config { |
||||
pub fn with_identity(identityPath: PathBuf, password: String) -> Self { |
||||
Self { |
||||
identityPath, |
||||
password, |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[cfg(test)] |
||||
mod tests { |
||||
#[test] |
||||
fn it_works() { |
||||
assert_eq!(add(1, 1), 2); |
||||
pub struct Gempress { |
||||
pub config: Config, |
||||
} |
||||
|
||||
impl Gempress { |
||||
pub fn new(config: Config) -> Self { |
||||
Gempress { |
||||
config, |
||||
} |
||||
} |
||||
|
||||
pub fn listen<F: Fn()>(&self, port: u16, callback: F) -> GempressResult<()> { |
||||
// Read certificate
|
||||
// TODO: Can a password be optional?
|
||||
let identity = io::load_cert(&self.config.identityPath.to_str().unwrap_or(""), &self.config.password)?; |
||||
|
||||
let address = format!("0.0.0.0:{}", port); |
||||
let listener = TcpListener::bind(address).map_err(GempressError::BindFailed)?; |
||||
let acceptor = TlsAcceptor::new(identity).unwrap(); |
||||
let acceptor = Arc::new(acceptor); |
||||
|
||||
logger::info(format!("Listening on port {}", port)); |
||||
|
||||
for stream in listener.incoming() { |
||||
match stream { |
||||
Ok(stream) => { |
||||
let acceptor = acceptor.clone(); |
||||
|
||||
thread::spawn(move || match acceptor.accept(stream) { |
||||
Ok(stream) => { |
||||
if let Err(e) = handle_client(stream) { |
||||
logger::error(format!("Can't handle client: {}", e)); |
||||
} |
||||
} |
||||
Err(e) => { |
||||
logger::error(format!("Can't handle stream: {}", e)); |
||||
} |
||||
}); |
||||
} |
||||
Err(err) => logger::error(err), |
||||
} |
||||
} |
||||
|
||||
Ok(())
|
||||
} |
||||
|
||||
} |
||||
|
||||
fn handle_client(mut stream: TlsStream<TcpStream>) -> GempressResult<usize> { |
||||
let mut buffer = [0; 1024]; |
||||
|
||||
stream |
||||
.read(&mut buffer) |
||||
.map_err(GempressError::StreamReadFailed)?; |
||||
|
||||
let raw_request = String::from_utf8(buffer.to_vec())?; |
||||
|
||||
let request = gemini::Request::parse(&raw_request)?; |
||||
let url_path = request.file_path(); |
||||
let file_path = path::Path::new(url_path); |
||||
|
||||
gemini::Response::success("It Works!\n".into(), "text/gemini").send(stream) |
||||
} |
||||
|
@ -0,0 +1,9 @@
|
||||
use std::fmt::Display; |
||||
|
||||
pub fn info(message: impl Display) { |
||||
println!("[info] {}", message); |
||||
} |
||||
|
||||
pub fn error(message: impl Display) { |
||||
println!("[err] {}", message); |
||||
} |
@ -1,5 +1,15 @@
|
||||
extern crate gempress; |
||||
|
||||
use gempress::Gempress; |
||||
use std::path::PathBuf; |
||||
|
||||
fn main() { |
||||
println!("{}", gempress::add(1, 1)); |
||||
// Run make_cert.sh to generate a certificate
|
||||
let config = gempress::Config::with_identity(PathBuf::from("identity.pfx"), "qqqq".into()); |
||||
|
||||
let app = Gempress::new(config); |
||||
|
||||
app.listen(1965, || { |
||||
println!("Listening on port 1965"); |
||||
}).unwrap(); |
||||
} |
||||
|
Loading…
Reference in new issue