From 9e4c4551a786830bf34d74c4ef111a8ed292fa9f Mon Sep 17 00:00:00 2001 From: Vika Date: Tue, 15 Feb 2022 02:44:33 +0300 Subject: WIP: convert to Tokio and Warp Warp allows requests to be applied as "filters", allowing to flexibly split up logic and have it work in a functional style, similar to pipes. Tokio is just an alternative runtime. I thought that maybe switching runtimes and refactoring the code might allow me to fish out that pesky bug with the whole application hanging after a certain amount of requests... --- src/main.rs | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 101 insertions(+), 17 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 79e0cf5..4036d46 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,10 @@ use log::{debug, error, info}; use std::env; -use surf::Url; +use http_types::Url; +use warp::{Filter, host::Authority, path::FullPath}; -#[async_std::main] -async fn main() -> Result<(), std::io::Error> { +#[tokio::main] +async fn main() -> Result<(), kittybox::database::StorageError> { // TODO json logging in the future? let logger_env = env_logger::Env::new().filter_or("RUST_LOG", "info"); env_logger::init_from_env(logger_env); @@ -64,13 +65,14 @@ async fn main() -> Result<(), std::io::Error> { Some(value) => value, None => { if let Ok(filename) = env::var("COOKIE_SECRET_FILE") { - use async_std::io::ReadExt; + /*use async_std::io::ReadExt; let mut file = async_std::fs::File::open(filename).await?; let mut temp_string = String::new(); file.read_to_string(&mut temp_string).await?; - temp_string + temp_string*/ + todo!() } else { error!("COOKIE_SECRET or COOKIE_SECRET_FILE is not set, will not be able to log in users securely!"); std::process::exit(1); @@ -78,24 +80,106 @@ async fn main() -> Result<(), std::io::Error> { } }; - let host = env::var("SERVE_AT") + let host: std::net::SocketAddr = match env::var("SERVE_AT") .ok() - .unwrap_or_else(|| "0.0.0.0:8080".to_string()); + .unwrap_or_else(|| "0.0.0.0:8080".to_string()) + .parse() { + Ok(addr) => addr, + Err(e) => { + error!("Cannot parse SERVE_AT: {}", e); + std::process::exit(1); + } + }; if backend_uri.starts_with("redis") { println!("The Redis backend is deprecated."); std::process::exit(1); } else if backend_uri.starts_with("file") { - let app = kittybox::get_app_with_file( - token_endpoint, - authorization_endpoint, - backend_uri, - media_endpoint, - cookie_secret, - internal_token, - ) - .await; - app.listen(host).await + + let database = { + let folder = backend_uri.strip_prefix("file://").unwrap(); + let path = std::path::PathBuf::from(folder); + kittybox::database::FileStorage::new(path).await? + }; + + // TODO interpret HEAD + let homepage = kittybox::util::require_host() + .and(warp::get()) + .and(warp::path::end()) + // TODO fetch content from the database + // TODO parse content-type and determine appropriate response + .map(|host| format!("front page for {}!", host)); + + let micropub = warp::path("micropub") + .and(warp::path::end() + .and(warp::get() + .and(kittybox::micropub::query(database)) + .or(warp::post() + .and(kittybox::util::require_host()) + .map(|host| "micropub post!")) + .or(warp::options() + .map(|| warp::reply::json::>(&None)) + // TODO: why doesn't this work? + // .map(warp::reply::with::header("Allow", "GET, POST")) + .map(|reply| warp::reply::with_header(reply, "Allow", "GET, POST")) + )) + .or(warp::get() + .and(warp::path("client")) + .and(warp::path::end()) + .map(|| kittybox::MICROPUB_CLIENT))); + + let media = warp::path("media") + .and(warp::path::end() + .and(kittybox::util::require_host()) + .map(|host| "media endpoint?...") + .or(kittybox::util::require_host() + .and(warp::path::param()) + .map(|host: Authority, path: String| format!("media file {}", path)))); + + // TODO remember how login logic works because I forgor + let login = warp::path("login") + .and(warp::path("callback") + .map(|| "callback!") + // TODO form on GET and handler on POST + .or(warp::path::end().map(|| "login page!"))); + + // TODO prettier error response + let coffee = warp::path("coffee") + .map(|| warp::reply::with_status("I'm a teapot!", warp::http::StatusCode::IM_A_TEAPOT)); + + // TODO interpret HEAD + let static_files = warp::get() + .and(warp::path!("static" / String)) + .map(|path| path); + + // TODO interpret HEAD + let catchall = warp::get() + .and(kittybox::util::require_host()) + .and(warp::path::full()) + .map(|host: Authority, path: FullPath| host.to_string() + path.as_str() + ".json") + // TODO fetch content from the database + // TODO parse content-type and determine appropriate response + ; + + let health = warp::path("health").and(warp::path::end()).map(|| "OK"); + // TODO instrumentation middleware (see metrics.rs for comments) + //let metrics = warp::path("metrics").and(warp::path::end()).map(kittybox::metrics::gather); + let app = homepage + .or(login) + .or(static_files) + .or(coffee) + .or(health) + .or(micropub) + .or(media) + .or(catchall) + ; + + let server = warp::serve(app); + + // TODO use warp::Server::bind_with_graceful_shutdown + info!("Listening on {:?}", host); + server.bind(host).await; + Ok(()) } else { println!("Unknown backend, not starting."); std::process::exit(1); -- cgit 1.4.1