Handle non-unicode domains before the server starts
(also annotate all of the panics)
This commit is contained in:
parent
efddac5e95
commit
3623cc77b4
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -54,6 +54,7 @@ name = "faery-ring"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"argh",
|
"argh",
|
||||||
|
"ascii",
|
||||||
"tiny_http",
|
"tiny_http",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ maintenance = { status = "passively-maintained" }
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tiny_http = "0.10.0"
|
tiny_http = "0.10.0"
|
||||||
argh = "0.1.7"
|
argh = "0.1.7"
|
||||||
|
ascii = "1.0.0"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = "fat"
|
lto = "fat"
|
||||||
|
|
12
src/main.rs
12
src/main.rs
|
@ -1,4 +1,5 @@
|
||||||
pub mod server;
|
pub mod server;
|
||||||
|
pub mod util;
|
||||||
|
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
@ -66,6 +67,17 @@ fn main() {
|
||||||
let domains: Vec<_> = read_into.split('\n')
|
let domains: Vec<_> = read_into.split('\n')
|
||||||
.filter(|s| !s.is_empty())
|
.filter(|s| !s.is_empty())
|
||||||
.collect();
|
.collect();
|
||||||
|
let domains = match util::to_ascii_list(&domains) {
|
||||||
|
Ok(d) => d,
|
||||||
|
Err((line, e)) => {
|
||||||
|
let line = domains[line]; // unwrap safe guaranteed by to_ascii_list()
|
||||||
|
let character = line.chars().nth(e.valid_up_to()).unwrap(); // unwrap safe
|
||||||
|
eprintln!(
|
||||||
|
"The domains file can only contain ASCII characters, but it looks like the domain {line} has a non-ASCII character ({character})"
|
||||||
|
);
|
||||||
|
exit(1005);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Step 2: Run the server
|
// Step 2: Run the server
|
||||||
|
|
|
@ -2,6 +2,7 @@ use std::borrow::Cow;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
||||||
|
use ascii::AsciiStr;
|
||||||
use tiny_http::Header;
|
use tiny_http::Header;
|
||||||
use tiny_http::Request;
|
use tiny_http::Request;
|
||||||
use tiny_http::Response;
|
use tiny_http::Response;
|
||||||
|
@ -12,7 +13,7 @@ use tiny_http::StatusCode;
|
||||||
///
|
///
|
||||||
/// Runs the server in a single thread, bound to 0.0.0.0 on the provided port. Requests are
|
/// Runs the server in a single thread, bound to 0.0.0.0 on the provided port. Requests are
|
||||||
/// handled first-come, first-serve
|
/// handled first-come, first-serve
|
||||||
pub fn go(domains: &[&str], port: u16) {
|
pub fn go(domains: &[&AsciiStr], port: u16) {
|
||||||
let server = match Server::http(("0.0.0.0", port)) {
|
let server = match Server::http(("0.0.0.0", port)) {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -62,14 +63,14 @@ impl<'a> Route<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a Response for this route
|
/// Generate a Response for this route
|
||||||
pub fn render<'b>(&self, domains: &[&'b str]) -> Response<Cursor<Cow<'b, [u8]>>> {
|
pub fn render<'b>(&self, domains: &[&'b AsciiStr]) -> Response<Cursor<Cow<'b, [u8]>>> {
|
||||||
match self {
|
match self {
|
||||||
Self::PrevOrNext(this_domain, offset) => {
|
Self::PrevOrNext(this_domain, offset) => {
|
||||||
|
|
||||||
let destination = domains.iter()
|
let destination = domains.iter()
|
||||||
.position(|d| d.starts_with(this_domain))
|
.position(|d| d.as_str().starts_with(this_domain))
|
||||||
.map(|i| (i as isize + offset).rem_euclid(domains.len() as isize))
|
.map(|i| (i as isize + offset).rem_euclid(domains.len() as isize))
|
||||||
.map(|i| domains[i as usize]);
|
.map(|i| domains[i as usize]); // safe to unwrap: we just did a rem_euclid
|
||||||
|
|
||||||
if let Some(url) = destination {
|
if let Some(url) = destination {
|
||||||
let destination = format!(
|
let destination = format!(
|
||||||
|
@ -82,11 +83,11 @@ impl<'a> Route<'a> {
|
||||||
Header::from_bytes(
|
Header::from_bytes(
|
||||||
b"Location".to_owned(),
|
b"Location".to_owned(),
|
||||||
destination.as_bytes(),
|
destination.as_bytes(),
|
||||||
).unwrap(),
|
).unwrap(), // safe to unwrap: both of these are guaranteed ASCII
|
||||||
Header::from_bytes(
|
Header::from_bytes(
|
||||||
b"Cache-Control".to_owned(),
|
b"Cache-Control".to_owned(),
|
||||||
b"max-age=86400,stale-while-revalidate=172800,stale-if-error=3155760000".to_owned(),
|
b"max-age=86400,stale-while-revalidate=172800,stale-if-error=3155760000".to_owned(),
|
||||||
).unwrap(),
|
).unwrap(),// safe to unwrap: both of these are guaranteed ASCII
|
||||||
],
|
],
|
||||||
Cursor::new(Cow::Borrowed(b"")),
|
Cursor::new(Cow::Borrowed(b"")),
|
||||||
Some(0),
|
Some(0),
|
||||||
|
|
22
src/util.rs
Normal file
22
src/util.rs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
//! Several small utility methods
|
||||||
|
|
||||||
|
use ascii::AsAsciiStrError;
|
||||||
|
use ascii::AsciiStr;
|
||||||
|
|
||||||
|
/// Convert a series of [`&str`][]s into [`AsciiStr`][]s
|
||||||
|
///
|
||||||
|
/// Usually used for the domain list. The result is either a [`Vec`][] containing all
|
||||||
|
/// mapped strings, or else a touple containing the index of the string with the first
|
||||||
|
/// error and the error that occurred.
|
||||||
|
pub fn to_ascii_list<'a>(strs: &'a [&'a str]) -> Result<Vec<&'a AsciiStr>, (usize, AsAsciiStrError)> {
|
||||||
|
strs.iter()
|
||||||
|
.map(AsciiStr::from_ascii)
|
||||||
|
.enumerate()
|
||||||
|
.try_fold(Vec::with_capacity(strs.len()), |mut v, (i, s)| {
|
||||||
|
match s {
|
||||||
|
Ok(s) => v.push(s),
|
||||||
|
Err(e) => return Err((i, e)),
|
||||||
|
};
|
||||||
|
Ok(v)
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in a new issue