mirror of
https://github.com/NLnetLabs/krill.git
synced 2026-10-01 22:04:54 +02:00
195 lines
5.6 KiB
Rust
195 lines
5.6 KiB
Rust
//! 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
|
||
);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|