115 lines
2.7 KiB
Rust
115 lines
2.7 KiB
Rust
use std::borrow::Cow;
|
|
use std::io::Cursor;
|
|
|
|
use tiny_http::Header;
|
|
use tiny_http::Request;
|
|
use tiny_http::Response;
|
|
use tiny_http::Server;
|
|
use tiny_http::StatusCode;
|
|
|
|
/// Start the server on port 3243
|
|
///
|
|
/// Runs the server in a single thread, bound to port 3243. Requests are handled
|
|
/// first-come, first-serve
|
|
pub fn go(domains: &[&str], port: u16) {
|
|
let server = Server::http(("0.0.0.0", port)).unwrap();
|
|
|
|
println!("Running on 0.0.0.0:3243");
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// An endpoint and the associated data
|
|
///
|
|
/// Each route represents a set of logic that should be used to respond to the request,
|
|
/// and the necessary information from the request in order to do so
|
|
pub enum Route<'a> {
|
|
PrevOrNext(&'a str, isize),
|
|
NotFound,
|
|
}
|
|
|
|
impl<'a> Route<'a> {
|
|
|
|
/// Given a request, parse out the appropriate Route for that request
|
|
pub fn parse_request(r: &'a Request) -> Self {
|
|
let (first_segment, referrer_domain) = r.url()
|
|
.trim_matches('/')
|
|
.split_once('/')
|
|
.unwrap_or((r.url(), "missing_referrer_domain"));
|
|
|
|
match first_segment {
|
|
"next" => {
|
|
Route::PrevOrNext(referrer_domain, 1)
|
|
},
|
|
"prev" => {
|
|
Route::PrevOrNext(referrer_domain, -1)
|
|
},
|
|
_ => Route::NotFound,
|
|
}
|
|
}
|
|
|
|
/// Generate a Response for this route
|
|
pub fn render<'b>(&self, domains: &[&'b str]) -> Response<Cursor<Cow<'b, [u8]>>> {
|
|
match self {
|
|
Self::PrevOrNext(this_domain, offset) => {
|
|
|
|
let destination = domains.iter()
|
|
.position(|d| d.starts_with(this_domain))
|
|
.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(),
|
|
Header::from_bytes(
|
|
b"Cache-Control".to_owned(),
|
|
b"max-age=86400,stale-while-revalidate=172800,stale-if-error=3155760000".to_owned(),
|
|
).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,
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|