surwiki/src/site.rs

162 lines
4.8 KiB
Rust
Raw Normal View History

2017-09-08 15:43:47 +03:00
// #[derive(BartDisplay)] can cause unused extern crates warning:
#![allow(unused_extern_crates)]
use std::fmt;
2017-08-20 21:46:08 +03:00
use futures::{self, Future};
use hyper::header::{Accept, ContentType, Server};
2017-08-20 21:46:08 +03:00
use hyper::mime;
2017-08-20 21:24:10 +03:00
use hyper::server::*;
use hyper;
use assets::{ThemesCss, StyleCss, SearchJs};
use build_config;
use theme;
2017-09-15 18:28:23 +03:00
use web::Lookup;
use wiki_lookup::WikiLookup;
2017-09-01 17:34:24 +03:00
2017-08-20 21:46:08 +03:00
lazy_static! {
static ref TEXT_HTML: mime::Mime = "text/html;charset=utf-8".parse().unwrap();
2017-11-14 13:29:31 +03:00
static ref SERVER: Server =
Server::new(build_config::HTTP_SERVER.as_str());
2017-08-20 21:46:08 +03:00
}
header! { (XIdentity, "X-Identity") => [String] }
#[derive(BartDisplay)]
#[template = "templates/layout.html"]
2017-09-15 18:28:23 +03:00
pub struct Layout<'a, T: 'a + fmt::Display> {
2017-10-13 17:05:22 +03:00
pub base: Option<&'a str>,
pub title: &'a str,
pub theme: theme::Theme,
pub body: T,
2017-10-25 14:24:42 +03:00
}
impl<'a, T: 'a + fmt::Display> Layout<'a, T> {
pub fn themes_css(&self) -> &str { ThemesCss::resource_name() }
pub fn style_css(&self) -> &str { StyleCss::resource_name() }
pub fn search_js(&self) -> &str { SearchJs::resource_name() }
2017-10-25 14:24:42 +03:00
pub fn project_name(&self) -> &str { build_config::PROJECT_NAME }
pub fn version(&self) -> &str { build_config::VERSION.as_str() }
}
#[derive(BartDisplay)]
#[template="templates/system_page_layout.html"]
pub struct SystemPageLayout<'a, T: 'a + fmt::Display> {
title: &'a str,
html_body: T,
}
pub fn system_page<'a, T>(base: Option<&'a str>, title: &'a str, body: T)
-> Layout<'a, SystemPageLayout<'a, T>>
where
T: 'a + fmt::Display
{
Layout {
base,
title,
2018-09-19 09:20:43 +03:00
theme: theme::theme_from_str_hash(title),
body: SystemPageLayout {
title,
html_body: body,
},
}
}
2017-08-20 23:39:52 +03:00
#[derive(BartDisplay)]
#[template = "templates/error/404.html"]
2017-08-20 23:39:52 +03:00
struct NotFound;
#[derive(BartDisplay)]
#[template = "templates/error/500.html"]
2017-08-20 23:39:52 +03:00
struct InternalServerError;
2017-08-20 21:24:10 +03:00
pub struct Site {
root: WikiLookup,
trust_identity: bool,
}
impl Site {
pub fn new(root: WikiLookup, trust_identity: bool) -> Site {
Site { root, trust_identity }
}
2017-10-13 17:05:22 +03:00
fn not_found(base: Option<&str>) -> Response {
Response::new()
.with_header(ContentType(TEXT_HTML.clone()))
.with_body(system_page(
base,
"Not found",
NotFound,
).to_string())
.with_status(hyper::StatusCode::NotFound)
}
2017-10-13 17:05:22 +03:00
fn internal_server_error(base: Option<&str>, err: Box<::std::error::Error + Send + Sync>) -> Response {
eprintln!("Internal Server Error:\n{:#?}", err);
Response::new()
.with_header(ContentType(TEXT_HTML.clone()))
.with_body(system_page(
2017-10-13 17:05:22 +03:00
base,
"Internal server error",
InternalServerError,
).to_string())
.with_status(hyper::StatusCode::InternalServerError)
}
2017-08-20 21:24:10 +03:00
}
2017-10-13 17:05:22 +03:00
fn root_base_from_request_uri(path: &str) -> Option<String> {
assert!(path.starts_with("/"));
let slashes = path[1..].matches('/').count();
match slashes {
0 => None,
n => Some(::std::iter::repeat("../").take(n).collect())
}
}
2017-08-20 21:24:10 +03:00
impl Service for Site {
type Request = Request;
type Response = Response;
type Error = hyper::Error;
type Future = Box<futures::Future<Item = Response, Error = Self::Error>>;
2017-08-20 21:24:10 +03:00
fn call(&self, req: Request) -> Self::Future {
2017-10-18 17:33:21 +03:00
let (method, uri, _http_version, headers, body) = req.deconstruct();
println!("{} {}", method, uri);
let identity: Option<String> = match self.trust_identity {
true => headers.get().map(|x: &XIdentity| x.to_string()),
false => None,
};
2017-10-18 17:33:21 +03:00
let accept_header = headers.get().map(|x: &Accept| x.clone()).unwrap_or(Accept(vec![]));
2017-10-13 17:05:22 +03:00
let base = root_base_from_request_uri(uri.path());
let base2 = base.clone(); // Bah, stupid clone
Box::new(self.root.lookup(uri.path(), uri.query())
.and_then(move |resource| match resource {
Some(mut resource) => {
use hyper::Method::*;
resource.hacky_inject_accept_header(accept_header);
match method {
Options => Box::new(futures::finished(resource.options())),
Head => resource.head(),
Get => resource.get(),
2017-10-18 17:33:21 +03:00
Put => resource.put(body, identity),
Post => resource.post(body, identity),
_ => Box::new(futures::finished(resource.method_not_allowed()))
2017-08-21 00:44:52 +03:00
}
},
2017-10-13 17:05:22 +03:00
None => Box::new(futures::finished(Self::not_found(base.as_ref().map(|x| &**x))))
})
2017-10-13 17:05:22 +03:00
.or_else(move |err| Ok(Self::internal_server_error(base2.as_ref().map(|x| &**x), err)))
2017-11-14 13:29:31 +03:00
.map(|response| response.with_header(SERVER.clone()))
)
2017-08-20 21:24:10 +03:00
}
}