From 0b6e7c0ff2e67624a842125a3992b07cd4769e17 Mon Sep 17 00:00:00 2001 From: Kjetil Orbekk Date: Sun, 18 Jun 2017 12:41:14 -0400 Subject: Support base_url. --- Cargo.lock | 1 + Cargo.toml | 1 + src/bin/main.rs | 22 ++++++++++++++++------ src/lib.rs | 1 + src/server.rs | 49 ++++++++++++++++++++++++++++++++++++++----------- 5 files changed, 57 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4d907cb..949930f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,7 @@ dependencies = [ "rusqlite 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "staticfile 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f15b815..43e634c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,4 @@ clap = "*" rpassword = "*" params = "*" persistent = "*" +url = "*" diff --git a/src/bin/main.rs b/src/bin/main.rs index 245416c..506eba4 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -38,8 +38,8 @@ fn create_admin_user(conn: &rusqlite::Connection) { } } -fn serve(context: systemhttp::server::Context, port: u16) { - let _server = systemhttp::server::serve(context, port).unwrap(); +fn serve(context: systemhttp::server::Context, state: systemhttp::server::State, port: u16) { + let _server = systemhttp::server::serve(context, state, port).unwrap(); println!("Serving on {}", port); } @@ -58,6 +58,10 @@ fn main() { .long("db_file") .takes_value(true) .help("Path to sqlite database")) + .arg(Arg::with_name("base_url") + .long("base_url") + .takes_value(true) + .help("URL to prepend to links (useful with proxies)")) .subcommand(SubCommand::with_name("serve") .about("Start the systemhttpd server")) .subcommand(SubCommand::with_name("create_admin_user") @@ -72,19 +76,25 @@ fn main() { let db_file = matches.value_of("db_file").unwrap(); + let base_url = matches.value_of("base_url").unwrap_or("").to_string(); + env_logger::init().unwrap(); let mut conn = rusqlite::Connection::open(db_file) .expect(format!("opening sqlite database at {}", db_file).as_str()); systemhttp::db::init(&mut conn); - let mut context = systemhttp::server::Context { - base_url: "http://localhost:8080".to_string(), + let context = systemhttp::server::Context { + base_url: base_url, + }; + info!("{:?}", context); + let state = systemhttp::server::State { conn: conn, }; + info!("{:?}", state); match matches.subcommand_name() { - Some("serve") => serve(context, port), - Some("create_admin_user") => create_admin_user(&context.conn), + Some("serve") => serve(context, state, port), + Some("create_admin_user") => create_admin_user(&state.conn), x => panic!("Don't know about subcommand: {:?}", x), } } diff --git a/src/lib.rs b/src/lib.rs index 3fd5b67..70c942a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,7 @@ extern crate staticfile; extern crate regex; extern crate params; extern crate persistent; +extern crate url; pub mod systemd; pub mod render; diff --git a/src/server.rs b/src/server.rs index c0e9028..4e5b6f3 100644 --- a/src/server.rs +++ b/src/server.rs @@ -16,19 +16,26 @@ use router::Router; use staticfile::Static; use systemd::journal; use systemd::unit; -use persistent::Write; +use persistent::{Read, Write}; use rusqlite::Connection; use db; use auth; +use url; #[derive(Debug)] pub struct Context { pub base_url: String, - pub conn: Connection, } impl iron::typemap::Key for Context { type Value = Context; } +#[derive(Debug)] +pub struct State { + pub conn: Connection, +} +impl iron::typemap::Key for State { + type Value = State; +} #[derive(Default, Debug, Clone)] struct Login { @@ -47,6 +54,22 @@ impl iron_sessionstorage::Value for Login { } } +// Construct an absolute url from base_url if provided, or the request url +// otherwise. +fn url_for(r: &mut Request, path: &str) -> Url { + let context = r.get::>().unwrap(); + match context.base_url.as_ref() { + "" => { + let mut url: url::Url = r.url.clone().into(); + url.set_path(path); + Url::from_generic_url(url).unwrap() + }, + base_url => { + Url::parse(&format!("{}{}", base_url, path)).unwrap() + } + } +} + fn overview(r: &mut Request) -> IronResult { let mut _value = try!(r.session().get::()); @@ -139,16 +162,16 @@ fn authenticate(r: &mut Request) -> IronResult { }; let hash = { - let mutex = r.get::>().unwrap(); - let context = mutex.lock().unwrap(); - db::lookup_user(&context.conn, &user).unwrap() + let mutex = r.get::>().unwrap(); + let state = mutex.lock().unwrap(); + db::lookup_user(&state.conn, &user).unwrap() }; if let Some(true) = hash.map(|h| auth::validate(&password, &h)) { let login = Login { user: user.to_string() }; // TODO Make a validated login type info!("User logged in: {:?}", login); r.session().set(login)?; - let url = url_for!(r, "root"); + let url = url_for(r, ""); Ok(Response::with((status::Found, Redirect(url)))) } else { login(r) @@ -157,14 +180,17 @@ fn authenticate(r: &mut Request) -> IronResult { fn logout(r: &mut Request) -> IronResult { r.session().set::(Default::default()); - let url = url_for!(r, "root"); + let url = url_for(r, ""); Ok(Response::with((status::Found, Redirect(url)))) } fn make_renderer(r: &mut Request) -> IronResult { let user = get_logged_in_user(r)?.map(|u| u.user); - let base_url = format!("{}", url_for!(r, "root")); - Ok(render::Renderer { base_url: base_url, user: user }) + let context = r.get::>().unwrap(); + Ok(render::Renderer { + base_url: context.base_url.to_string(), + user: user + }) } fn static_file(r: &mut Request) -> IronResult { @@ -180,7 +206,7 @@ fn static_file(r: &mut Request) -> IronResult { }) } -pub fn serve(context: Context, port: u16) -> HttpResult { +pub fn serve(context: Context, state: State, port: u16) -> HttpResult { // TODO: Use a real secret. let secret = b"secret2".to_vec(); let router = router!( @@ -194,7 +220,8 @@ pub fn serve(context: Context, port: u16) -> HttpResult { ); let mut chain = Chain::new(router); chain.link_around(SessionStorage::new(SignedCookieBackend::new(secret))); - chain.link(Write::::both(context)); + chain.link(Read::::both(context)); + chain.link(Write::::both(state)); let bind_address = format!("{}:{}", "::", port); Iron::new(chain).http(bind_address.as_str()) } -- cgit v1.2.3