use super::config::HttpConfig;
use crate::{
connector::{
Connector, Error,
ErrorKind::{AddrInvalid, RequestError},
Message,
},
uuid,
};
use std::{io, process, time::Instant};
use tiny_http as http;
pub struct Server {
addr: String,
port: u16,
server: http::Server,
connector: Connector,
}
impl Server {
pub fn new(config: &HttpConfig, connector: Connector) -> Result<Server, Error> {
let server = http::Server::http(format!("{}:{}", &config.addr, config.port))
.map_err(|e| err!(AddrInvalid, "couldn't create HTTP server: {}", e))?;
Ok(Self {
addr: config.addr.clone(),
port: config.port,
server,
connector,
})
}
pub fn run(&self) -> Result<(), Error> {
loop {
self.handle_request()?;
}
}
pub fn handle_request(&self) -> Result<(), Error> {
let mut request = self.server.recv()?;
let response = match *request.method() {
http::Method::Get => match request.url() {
"/connector/status" => Some(self.status()?),
_ => None,
},
http::Method::Post => match request.url() {
"/connector/api" => Some(self.api(&mut request)?),
_ => None,
},
_ => None,
}
.unwrap_or_else(|| {
http::Response::new(
http::StatusCode::from(404),
vec![],
io::Cursor::new(vec![]),
None,
None,
)
});
request.respond(response)?;
Ok(())
}
fn status(&self) -> Result<http::Response<io::Cursor<Vec<u8>>>, Error> {
info!(
"yubihsm::http-server[{}:{}]: GET /connector/status",
&self.addr, self.port
);
let status = [
("status", "OK"),
("serial", "*"),
("version", env!("CARGO_PKG_VERSION")),
("pid", &process::id().to_string()),
("address", &self.addr),
("port", &self.port.to_string()),
];
let body = status
.iter()
.map(|(k, v)| [*k, *v].join("\n"))
.collect::<Vec<_>>()
.join("\n");
Ok(http::Response::from_string(body))
}
fn api(
&self,
request: &mut http::Request,
) -> Result<http::Response<io::Cursor<Vec<u8>>>, Error> {
let mut body = Vec::new();
request.as_reader().read_to_end(&mut body)?;
let command_msg = Message::from(body);
let command = command_msg
.clone()
.parse()
.map_err(|e| err!(RequestError, "couldn't parse request message: {}", e))?;
let started_at = Instant::now();
let response_msg = self.connector.send_message(uuid::new_v4(), command_msg)?;
let session = command
.session_id
.map(|s| s.to_string())
.unwrap_or_else(|| "none".to_owned());
info!(
"yubihsm::http-server[{}:{}]: POST /connector/api - session:{} cmd:{:?} t:{}ms",
&self.addr,
self.port,
&session,
command.command_type,
started_at.elapsed().as_millis()
);
Ok(http::Response::from_data(response_msg.as_ref()))
}
}