use crate::router::{Handler, HandlerObj, ResponseFuture, Router, RouterError};
use crate::web::response;
use hyper::server::conn::AddrIncoming;
use hyper::service::make_service_fn;
use hyper::{Body, Request, Server, StatusCode};
use hyper_rustls::TlsAcceptor;
use std::convert::Infallible;
use std::fs::File;
use std::net::SocketAddr;
use std::sync::Arc;
use std::{io, thread};
#[derive(Clone, Eq, PartialEq, Debug, thiserror::Error, Serialize, Deserialize)]
pub enum Error {
#[error("Internal error: {0}")]
Internal(String),
#[error("Bad arguments: {0}")]
Argument(String),
#[error("Not found.")]
NotFound,
#[error("Request error: {0}")]
RequestError(String),
#[error("ResponseError error: {0}")]
ResponseError(String),
#[error("Router error: {source}")]
Router {
#[from]
source: RouterError,
},
}
impl From<hyper::http::Error> for Error {
fn from(error: hyper::http::Error) -> Error {
Error::RequestError(error.to_string())
}
}
impl From<crate::chain::Error> for Error {
fn from(error: crate::chain::Error) -> Error {
Error::Internal(error.to_string())
}
}
#[derive(Clone)]
pub struct TLSConfig {
pub certificate: String,
pub private_key: String,
}
impl TLSConfig {
pub fn new(certificate: String, private_key: String) -> TLSConfig {
TLSConfig {
certificate,
private_key,
}
}
fn load_certs(&self) -> Vec<rustls::Certificate> {
let certfile = File::open(&self.certificate)
.expect(&format!("failed to open file {}", self.certificate));
let mut reader = io::BufReader::new(certfile);
rustls_pemfile::certs(&mut reader)
.unwrap()
.iter()
.map(|v| rustls::Certificate(v.clone()))
.collect()
}
fn load_private_key(&self) -> rustls::PrivateKey {
let keyfile = File::open(&self.private_key).expect("cannot open private key file");
let mut reader = io::BufReader::new(keyfile);
loop {
match rustls_pemfile::read_one(&mut reader).expect("cannot parse private key .pem file")
{
Some(rustls_pemfile::Item::RSAKey(key)) => return rustls::PrivateKey(key),
Some(rustls_pemfile::Item::PKCS8Key(key)) => return rustls::PrivateKey(key),
Some(rustls_pemfile::Item::ECKey(key)) => return rustls::PrivateKey(key),
None => break,
_ => {}
}
}
panic!(
"no keys found in {:?} (encrypted keys not supported)",
&self.private_key
);
}
pub fn build_server_config(&self) -> Result<Arc<rustls::ServerConfig>, Error> {
let certs = self.load_certs();
let key = self.load_private_key();
let cfg = rustls::ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_single_cert(certs, key)
.expect("bad certificate/key");
Ok(Arc::new(cfg))
}
}
pub struct ApiServer {
shutdown_sender: Option<tokio::sync::oneshot::Sender<()>>,
}
impl ApiServer {
pub fn new() -> ApiServer {
ApiServer {
shutdown_sender: None,
}
}
pub fn start(
&mut self,
addr: SocketAddr,
router: Router,
conf: Option<TLSConfig>,
api_chan: &'static mut (
tokio::sync::oneshot::Sender<()>,
tokio::sync::oneshot::Receiver<()>,
),
) -> Result<thread::JoinHandle<()>, Error> {
match conf {
Some(conf) => self.start_tls(addr, router, conf, api_chan),
None => self.start_no_tls(addr, router, api_chan),
}
}
fn start_no_tls(
&mut self,
addr: SocketAddr,
router: Router,
api_chan: &'static mut (
tokio::sync::oneshot::Sender<()>,
tokio::sync::oneshot::Receiver<()>,
),
) -> Result<thread::JoinHandle<()>, Error> {
if self.shutdown_sender.is_some() {
return Err(Error::Internal(
"Can't start HTTP API server, it's running already".to_string(),
));
}
let tx = &mut api_chan.0;
let rx = &mut api_chan.1;
let m = tokio::sync::oneshot::channel::<()>();
let tx = std::mem::replace(tx, m.0);
self.shutdown_sender = Some(tx);
thread::Builder::new()
.name("apis".to_string())
.spawn(move || {
let task = async {
let server = Server::bind(&addr)
.serve(make_service_fn(move |_| {
let router = router.clone();
async { Ok::<_, Infallible>(router) }
}))
.with_graceful_shutdown(async {
rx.await.ok();
});
if let Err(e) = server.await {
error!("HTTP API server error: {}", e);
}
};
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
rt.block_on(task);
})
.map_err(|_| Error::Internal("failed to spawn API thread".to_string()))
}
fn start_tls(
&mut self,
addr: SocketAddr,
router: Router,
conf: TLSConfig,
api_chan: &'static mut (
tokio::sync::oneshot::Sender<()>,
tokio::sync::oneshot::Receiver<()>,
),
) -> Result<thread::JoinHandle<()>, Error> {
if self.shutdown_sender.is_some() {
return Err(Error::Internal(
"Can't start HTTPS API server, it's running already".to_string(),
));
}
let tx = &mut api_chan.0;
let rx = &mut api_chan.1;
let m = tokio::sync::oneshot::channel::<()>();
let tx = std::mem::replace(tx, m.0);
self.shutdown_sender = Some(tx);
thread::Builder::new()
.name("apis".to_string())
.spawn(move || {
let task = async {
let incoming = AddrIncoming::bind(&addr).unwrap();
let server_config = conf.build_server_config().unwrap();
let acceptor = TlsAcceptor::new(server_config.to_owned(), incoming);
let service = make_service_fn(move |_| {
let router = router.clone();
async move { Ok::<_, Infallible>(router) }
});
let server = Server::builder(acceptor)
.serve(service)
.with_graceful_shutdown(async {
rx.await.ok();
});
if let Err(e) = server.await {
error!("HTTPS API server error: {}", e);
}
};
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
rt.block_on(task);
})
.map_err(|_| Error::Internal("failed to spawn API thread".to_string()))
}
pub fn stop(&mut self) -> bool {
if self.shutdown_sender.is_some() {
if let Some(tx) = self.shutdown_sender.take() {
tx.send(()).expect("Failed to stop API server");
info!("API server has been stopped");
}
true
} else {
error!("Can't stop API server, it's not running or doesn't support stop operation");
false
}
}
}
pub struct LoggingMiddleware {}
impl Handler for LoggingMiddleware {
fn call(
&self,
req: Request<Body>,
mut handlers: Box<dyn Iterator<Item = HandlerObj>>,
) -> ResponseFuture {
debug!("REST call: {} {}", req.method(), req.uri().path());
match handlers.next() {
Some(handler) => handler.call(req, handlers),
None => response(StatusCode::INTERNAL_SERVER_ERROR, "no handler found"),
}
}
}