use std::net::SocketAddr;
use std::sync::Arc;
use tokio_core::reactor::Handle;
use tk_http::Status;
use tk_http::server::{Dispatcher, Error as ServerError, Head};
use runtime::Runtime;
use incoming::{Request, Debug, Input, Transport};
use routing::{parse_host, route};
use default_error_page::serve_error_page;
use request_id;
use metrics::{Counter};
use logging;
use request_id::RequestId;
lazy_static! {
pub static ref REQUESTS: Counter = Counter::new();
}
pub struct Router {
addr: SocketAddr,
runtime: Arc<Runtime>,
handle: Handle,
}
pub enum Error {
Page(Status, Debug),
Fallback(ServerError),
}
impl Router {
pub fn new(addr: SocketAddr, runtime: Arc<Runtime>, handle: Handle)
-> Router
{
Router {
addr: addr,
runtime: runtime,
handle: handle,
}
}
}
impl Router {
fn start_request<S: Transport>(&mut self, headers: &Head,
request_id: RequestId)
-> Result<Request<S>, Error>
{
use self::Error::*;
REQUESTS.incr(1);
let cfg = self.runtime.config.get();
let mut debug = Debug::new(headers, request_id, &cfg);
let path = headers.path().unwrap_or("/");
let parsed_host = headers.host().map(parse_host);
let matched_route = parsed_host
.and_then(|host| route(host, &path, &cfg.routing));
let (route, pref, suf) = if let Some((route, p, s)) = matched_route {
(route, p, s)
} else {
return Err(Page(Status::NotFound, debug));
};
debug.set_route(route);
let mut inp = Input {
addr: self.addr,
runtime: &self.runtime,
config: &cfg,
debug: debug,
headers: headers,
prefix: pref,
suffix: suf,
handle: &self.handle,
request_id: request_id,
};
match route.authorizer.check(&mut inp) {
Ok(true) => {}
Ok(false) => {
return Err(Page(Status::Forbidden, inp.debug));
}
Err(e) => return Err(Fallback(e)),
}
route.handler.serve(inp).map_err(Fallback)
}
}
impl<S: Transport> Dispatcher<S> for Router {
type Codec = Request<S>;
fn headers_received(&mut self, headers: &Head)
-> Result<Self::Codec, ServerError>
{
let request_id = request_id::new();
match self.start_request(headers, request_id) {
Ok(x) => {
logging::log(&self.runtime,
logging::http::FakePage {
request: logging::http::EarlyRequest {
addr: self.addr,
head: headers,
request_id: request_id,
},
response: logging::http::FakeResponse {
},
});
Ok(x)
}
Err(Error::Page(status, debug)) => {
logging::log(&self.runtime,
logging::http::EarlyError {
request: logging::http::EarlyRequest {
addr: self.addr,
head: headers,
request_id: request_id,
},
response: logging::http::EarlyResponse {
status: status.into(),
}
});
Ok(serve_error_page(status,
(self.runtime.config.get(), debug)))
}
Err(Error::Fallback(e)) => Err(e),
}
}
}