use crate::error::{ServiceError, ServiceErrorKind}; use webframework_core::WebResult; use webframework_core::request::Request; use webframework_core::response::Response; use webframework_core::router::{Router, RouterResult}; use std::net::SocketAddr; use std::time::Instant; use std::collections::HashMap; use failure::{Compat, Error}; use futures::future::{self, Future, FutureResult}; use uuid::Uuid; use slog::{Drain, Logger}; struct ServiceCreator { logger: Logger, router: S, } impl hyper::service::MakeService for ServiceCreator { type ReqBody = hyper::Body; type ResBody = hyper::Body; type Error = Compat; type Service = Service; type Future = FutureResult; type MakeError = Compat; fn make_service(&mut self, _: Ctx) -> Self::Future { future::ok(Service { logger: self.logger.new(slog::o!()), router: self.router.clone(), }) } } struct Service { logger: Logger, router: S, } impl hyper::service::Service for Service { type ReqBody = hyper::Body; type ResBody = hyper::Body; type Error = Compat; type Future = Box, Error = Self::Error> + Send>; fn call(&mut self, req: hyper::Request) -> Self::Future { let id = Uuid::new_v4(); let string_id = id.to_string(); let new_logger = self.logger.new(slog::o!("path" => req.uri().path().to_string(), "id" => string_id.clone())); let time_logger = self.logger.new(slog::o!("path" => req.uri().path().to_string(), "id" => string_id.clone())); let now = Instant::now(); let request = Request::from_req(id, new_logger, req); let router = self.router.clone(); Box::new(request.and_then(move |req| { use futures::future::Either; match router.handle(req, None, HashMap::new()) { RouterResult::Handled(resp) => { return Either::A(resp.map(Response::as_response).flatten()); } RouterResult::Unhandled(req, _) => { let error = ServiceErrorKind::UnhandledError(req.path().to_string()).into(); return Either::B(future::err(error)); } } }).or_else(|err: Error| { let resp = Response::from_string(crate::templates::error_page(&err.compat())) .with_status(hyper::StatusCode::INTERNAL_SERVER_ERROR); future::ok(resp.as_response().unwrap()) }).then(move |resp: Result, _>| { let elapsed = now.elapsed(); let time: f64 = elapsed.as_secs() as f64 * 1000.0 + elapsed.subsec_nanos() as f64 / 1_000_000.0; let mut status = None; match &resp { Ok(resp) => { status = Some(resp.status().as_u16()); } _ => (), } slog::info!(time_logger, "Handled in {}ms", time; "elapsed_time" => time, "status" => status); resp })) } } pub struct Server { root_logger: Logger, addr: SocketAddr, } impl Server { pub fn handle_with(&self, router: S) -> WebResult<()> { let creator = ServiceCreator { logger: self.root_logger.new(slog::o!()), router }; slog::info!(&creator.logger, "Starting Webserver on http://{}", &self.addr; "port" => %self.addr.port(), "host" => %self.addr.ip()); let server = hyper::Server::bind(&self.addr) .serve(creator) .map_err(|err| eprintln!("error: {}", err)); hyper::rt::run(server); Ok(()) } } pub fn load() -> Server { let decorator = slog_term::TermDecorator::new().build(); let drain = slog_term::FullFormat::new(decorator).build().fuse(); let drain = slog_async::Async::new(drain).build().fuse(); let root_logger = Logger::root(drain, slog::o!()); Server { root_logger, addr: ([127, 0, 0, 1], 3000).into(), } }