Files
NLnetLabs-krill/src/daemon/http/server.rs
T

195 lines
5.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! The core of the HTTP server.
use std::env;
use std::sync::{Arc, Weak};
use clap::crate_version;
use hyper::StatusCode;
use log::{error, info, warn, trace};
use tokio::runtime;
use crate::api::admin::ServerInfo;
use crate::api::ca::Timestamp;
use crate::commons::KrillResult;
use crate::commons::error::FatalError;
use crate::config::Config;
use crate::constants::KRILL_ENV_HTTP_LOG_INFO;
use crate::server::manager::KrillManager;
use super::auth::Authorizer;
use super::dispatch::{DispatchError, dispatch_request};
use super::request::{BodyLimits, HyperRequest, Request};
use super::response::{HyperResponse, HttpResponse};
//------------ HttpServer ----------------------------------------------------
/// The Krill HTTP server.
///
pub struct HttpServer {
/// The Krill server.
krill: KrillManager,
/// The component responsible for API authorization checks
authorizer: Authorizer,
/// Time this server was started
started: Timestamp,
}
impl HttpServer {
/// Creates a new server from a Krill manager and the configuration.
pub fn new(
krill: KrillManager,
runtime: &runtime::Handle,
) -> KrillResult<Arc<Self>> {
let authorizer = Authorizer::new(krill.storage(), krill.config())?;
authorizer.spawn_sweep(runtime);
Ok(Self {
krill,
authorizer,
started: Timestamp::now(),
}.into())
}
/// Processes an HTTP request.
pub async fn process_request(
this: Weak<Self>, request: HyperRequest
) -> Result<HyperResponse, FatalError> {
// If we can’t upgrade the weak this, return a 503.
let Some(this) = this.upgrade() else {
return Ok(HttpResponse::error(
StatusCode::SERVICE_UNAVAILABLE,
("sys-unavailable", "Service Unavailable"),
).into_hyper())
};
let logger = RequestLogger::begin(&request);
let (auth, new_token) = this.authorizer.authenticate_request(
&request
).await;
let request = Request::new(
request, &this, auth,
BodyLimits::from_config(this.krill.config())
);
let path = match request.path() {
Ok(path) => path,
Err(err) => {
return Ok(
HttpResponse::error(
StatusCode::BAD_REQUEST, err
).into_hyper()
);
}
};
let mut response = match dispatch_request(
request, path.iter(),
).await {
Ok(response) => Ok(response),
Err(DispatchError::Response(response)) => Ok(response),
Err(DispatchError::Fatal(err)) => Err(err),
};
// Augment the response with any updated auth details that were
// determined above.
if let (Ok(response), Some(token)) = (response.as_mut(), new_token) {
response.add_authorization_token(token);
}
logger.end(response.as_ref());
response.map(HttpResponse::into_hyper)
}
}
impl HttpServer {
/// Returns a reference to the Krill server.
pub(super) fn krill(&self) -> &KrillManager {
&self.krill
}
/// Returns a reference to the authorizer.
pub(super) fn authorizer(&self) -> &Authorizer {
&self.authorizer
}
/// Returns a reference to the configuration.
pub fn config(&self) -> &Config {
self.krill.config()
}
pub(super) fn server_info(&self) -> ServerInfo {
ServerInfo { version: crate_version!().into(), started: self.started }
}
}
//------------ RequestLogger -------------------------------------------------
struct RequestLogger {
req_method: hyper::Method,
req_path: String,
}
impl RequestLogger {
fn begin(req: &HyperRequest) -> Self {
let req_method = req.method().clone();
let req_path = req.uri().path().into();
trace!(
"Request: method={} path={} headers={:?}",
req_method, req_path, req.headers()
);
RequestLogger {
req_method,
req_path,
}
}
fn end(&self, res: Result<&HttpResponse, &FatalError>) {
match res {
Ok(response) => {
match (response.status(), response.benign(), response.cause())
{
(s, false, Some(cause)) if s.is_client_error() => {
warn!("HTTP {}: {}", s.as_u16(), cause)
}
(s, false, Some(cause)) if s.is_server_error() => {
error!("HTTP {}: {}", s.as_u16(), cause)
}
_ => {}
}
if env::var(KRILL_ENV_HTTP_LOG_INFO).is_ok() {
info!(
"{} {} {}",
self.req_method,
self.req_path,
response.status()
);
}
if response.loggable() {
trace!(
"{} {} {}",
self.req_method,
self.req_path,
response.status()
);
trace!(
"Response: headers={:?} body={:?}",
response.headers(),
response.body()
);
}
}
Err(err) => {
error!(
"{} {} Fatal error: {}",
self.req_method, self.req_path, err
);
}
}
}
}