Initial Commit

This commit is contained in:
Emi Simpson 2021-11-08 23:09:57 -05:00
commit 7d73f63cd4
Signed by untrusted user: Emi
GPG key ID: A12F2C2FFDC3D847
5 changed files with 479 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

270
Cargo.lock generated Normal file
View file

@ -0,0 +1,270 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "argh"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f023c76cd7975f9969f8e29f0e461decbdc7f51048ce43427107a3d192f1c9bf"
dependencies = [
"argh_derive",
"argh_shared",
]
[[package]]
name = "argh_derive"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48ad219abc0c06ca788aface2e3a1970587e3413ab70acd20e54b6ec524c1f8f"
dependencies = [
"argh_shared",
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "argh_shared"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38de00daab4eac7d753e97697066238d67ce9d7e2d823ab4f72fe14af29f3f33"
[[package]]
name = "ascii"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbf56136a5198c7b01a49e3afcbef6cf84597273d298f54432926024107b0109"
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[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.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [
"libc",
"num-integer",
"num-traits",
"winapi",
]
[[package]]
name = "chunked_transfer"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e"
[[package]]
name = "faery-ring"
version = "0.1.0"
dependencies = [
"argh",
"tiny_http",
]
[[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 = "heck"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "idna"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
dependencies = [
"matches",
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "libc"
version = "0.2.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219"
[[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.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "num-integer"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg",
]
[[package]]
name = "percent-encoding"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "proc-macro2"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
dependencies = [
"proc-macro2",
]
[[package]]
name = "syn"
version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "tiny_http"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96155b5f7149ba7a99ea5d516c538250b26eab60b4485c0f5344432573e7a450"
dependencies = [
"ascii",
"chrono",
"chunked_transfer",
"log",
"url",
]
[[package]]
name = "tinyvec"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83b2a3d4d9091d0abd7eba4dc2710b1718583bd4d8992e2190720ea38f391f7"
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.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
[[package]]
name = "unicode-normalization"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-segmentation"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "url"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
dependencies = [
"form_urlencoded",
"idna",
"matches",
"percent-encoding",
]
[[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"

10
Cargo.toml Normal file
View file

@ -0,0 +1,10 @@
[package]
name = "faery-ring"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
tiny_http = "0.9.0"
argh = "0.1.6"

84
src/main.rs Normal file
View file

@ -0,0 +1,84 @@
pub mod server;
use std::io::Read;
use std::process::exit;
use std::fs::File;
use std::io::ErrorKind;
use std::path::PathBuf;
use argh;
fn main() {
let args: CommandlineArgs = argh::from_env();
//
// Step 1: Read the file
//
let mut config = match File::open(&args.domains) {
Ok(file) => file,
Err(e) => {
match e.kind() {
ErrorKind::NotFound => {
eprintln!("Domains file not found: {}", args.domains.display());
exit(1001);
},
ErrorKind::PermissionDenied => {
eprintln!(
"Permission denied to read domain file: {}",
args.domains.display()
);
exit(1002);
},
_ => {
eprintln!(
"Unexepected error opening domain file: {}",
args.domains.display()
);
exit(1000);
}
}
}
};
let mut read_into = String::with_capacity(1_000);
match config.read_to_string(&mut read_into) {
Ok(_) => {},
Err(e) => {
match e.kind() {
ErrorKind::BrokenPipe => {
eprintln!("Broken pipe (filesystem error) while reading {}", args.domains.display());
exit(1000);
},
ErrorKind::InvalidData => {
eprintln!(
"Domain file {} contains non-UTF-8 data. Are you sure this is a domains file?",
args.domains.display()
);
exit(1003);
}
_ => {
eprintln!(
"Unexepected error opening domain file: {}",
args.domains.display()
);
exit(1000);
}
}
}
};
let domains: Vec<_> = read_into.split('\n')
.filter(|s| !s.is_empty())
.collect();
//
// Step 2: Run the server
//
server::go(&domains);
}
#[derive(argh::FromArgs)]
/// Run a basic webring hub
struct CommandlineArgs {
/// a text file listing all domains in the ring, one per line
#[argh(positional)]
domains: PathBuf,
}

114
src/server.rs Normal file
View file

@ -0,0 +1,114 @@
use std::borrow::Cow;
use std::io::Cursor;
use std::ops::Deref;
use std::sync::Arc;
use tiny_http::Header;
use tiny_http::Request;
use tiny_http::Response;
use tiny_http::Server;
use tiny_http::StatusCode;
pub fn go(domains: &[&str]) {
let server = Server::http("0.0.0.0:3243").unwrap();
let server = Arc::new(server);
for req in server.incoming_requests() {
let route = Route::parse_request(&req);
let resp = route.render(domains);
if let Err(e) = req.respond(resp) {
eprintln!("WARN: {}", e);
}
}
}
pub enum Route<'a> {
Next(&'a str),
Prev(&'a str),
NotFound,
}
pub enum RuntimeStatic {
JsonIndex,
}
impl<'a> Route<'a> {
pub fn parse_request(r: &'a Request) -> Self {
let segments: Vec<_> = r.url()
.split('/')
.filter(|s| !s.is_empty())
.collect();
// TODO automatically detect a fallback url from the request's Referer header
let referrer_domain = segments.get(1).map(|s| *s).unwrap_or("missing_referrer_domain");
match segments.get(0).map(Deref::deref) {
Some("next") => {
Route::Next(referrer_domain)
},
Some("prev") => {
Route::Prev(referrer_domain)
},
_ => Route::NotFound,
}
}
pub fn render<'b>(&self, domains: &[&'b str]) -> Response<Cursor<Cow<'b, [u8]>>> {
match self {
Self::Next(this_domain) | Self::Prev(this_domain) => {
let offset: isize = if let Self::Next(_) = self {
1
} else {
-1
};
let destination = domains.iter()
.position(|d| this_domain.starts_with(d))
.map(|i| (i as isize + offset).rem_euclid(domains.len() as isize))
.map(|i| domains[i as usize]);
if let Some(url) = destination {
let destination = format!(
"https://{}",
url
);
Response::new(
StatusCode(303),
vec![
Header::from_bytes(
b"Location".to_owned(),
destination.as_bytes(),
).unwrap(),
],
Cursor::new(Cow::Borrowed(b"")),
Some(0),
None,
)
} else {
let bytes = format!(
"Misconfigured server. Website {} not found in webring.",
this_domain,
);
let blen = bytes.len();
Response::new(
StatusCode(400),
Vec::new(),
Cursor::new(Cow::Owned(bytes.into_bytes())),
Some(blen),
None,
)
}
},
Self::NotFound => {
Response::new(
StatusCode(404),
Vec::new(),
Cursor::new(Cow::Borrowed(b"Page not found")),
Some(14),
None,
)
}
}
}
}