use std::sync::Arc;
use mkit_core::hash::Hash;
use super::body::EndHook;
use super::reach::{Reachability, TtlReachability};
use super::{HttpObjectResponse, HttpObjectsConfig};
use crate::Procedure;
use crate::repo::RepoId;
use crate::{BoxFuture, MaybeSend, MaybeSync, Redacted, ServerError};
pub trait TokenGate: MaybeSend + MaybeSync {
fn precheck(
&self,
token: &Redacted,
now_ms: i64,
) -> Result<crate::url_token::Prechecked, crate::url_token::TokenRejected>;
fn ttl_ms(&self) -> u64;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct NoTokens;
impl TokenGate for NoTokens {
fn precheck(
&self,
_: &Redacted,
_: i64,
) -> Result<crate::url_token::Prechecked, crate::url_token::TokenRejected> {
Err(crate::url_token::TokenRejected)
}
fn ttl_ms(&self) -> u64 {
0
}
}
impl TokenGate for crate::url_token::UrlTokenConfig {
fn precheck(
&self,
token: &Redacted,
now_ms: i64,
) -> Result<crate::url_token::Prechecked, crate::url_token::TokenRejected> {
self.precheck(token.expose(), now_ms)
}
fn ttl_ms(&self) -> u64 {
self.ttl_ms()
}
}
#[derive(Debug)]
#[non_exhaustive]
pub struct AdmitRequest<'a> {
pub repo: &'a RepoId,
pub procedure: Procedure,
pub head: bool,
pub ref_path: bool,
pub declared_bytes: u64,
pub credential_headers: &'a [crate::pipeline::CredentialHeader],
}
#[derive(Default)]
pub struct Admitted {
pub private: bool,
pub headers: Vec<(&'static str, String)>,
pub on_end: Option<EndHook>,
}
impl core::fmt::Debug for Admitted {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Admitted")
.field("private", &self.private)
.field("on_end", &self.on_end.is_some())
.finish_non_exhaustive()
}
}
#[derive(Debug)]
pub enum AdmitDecision {
Allow(Admitted),
Respond(HttpObjectResponse),
}
pub trait HttpAdmission: MaybeSend + MaybeSync {
fn is_configured(&self) -> bool {
true
}
fn admit<'a>(
&'a self,
request: &'a AdmitRequest<'a>,
) -> BoxFuture<'a, Result<AdmitDecision, ServerError>>;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct NoAdmission;
impl HttpAdmission for NoAdmission {
fn is_configured(&self) -> bool {
false
}
fn admit<'a>(
&'a self,
_: &'a AdmitRequest<'a>,
) -> BoxFuture<'a, Result<AdmitDecision, ServerError>> {
Box::pin(async { Ok(AdmitDecision::Allow(Admitted::default())) })
}
}
#[derive(Debug)]
pub enum TakedownVerdict {
Clear,
NotFound,
Respond(HttpObjectResponse),
}
pub trait TakedownGate: MaybeSend + MaybeSync {
fn stops_descent(&self, _repo: &RepoId, _id: &Hash) -> bool {
false
}
fn check<'a>(
&'a self,
repo: &'a RepoId,
leaf: &'a Hash,
) -> BoxFuture<'a, Result<TakedownVerdict, ServerError>>;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct NoTakedown;
impl TakedownGate for NoTakedown {
fn check<'a>(
&'a self,
_: &'a RepoId,
_: &'a Hash,
) -> BoxFuture<'a, Result<TakedownVerdict, ServerError>> {
Box::pin(async { Ok(TakedownVerdict::Clear) })
}
}
pub trait ProofSource: MaybeSend {
fn read(&mut self, id: Hash) -> BoxFuture<'_, Result<Vec<u8>, ServerError>>;
}
#[derive(Debug, Clone)]
pub struct PreparedProof {
pub commit: Hash,
pub leaf: Hash,
pub path: Vec<Vec<u8>>,
pub range: Option<(u64, u64)>,
pub encoded_len: u64,
pub span: bool,
}
pub trait ProofServer: MaybeSend + MaybeSync {
fn is_supported(&self) -> bool {
true
}
fn build<'a>(
&'a self,
request: &'a PreparedProof,
source: &'a mut dyn ProofSource,
) -> BoxFuture<'a, Result<Vec<u8>, ServerError>>;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct UnsupportedProofs;
impl ProofServer for UnsupportedProofs {
fn is_supported(&self) -> bool {
false
}
fn build<'a>(
&'a self,
_: &'a PreparedProof,
_: &'a mut dyn ProofSource,
) -> BoxFuture<'a, Result<Vec<u8>, ServerError>> {
Box::pin(async {
Err(ServerError::new(
crate::Code::OutOfRange,
"proof unsupported",
))
})
}
}
#[derive(Clone)]
pub struct HttpSeams {
pub tokens: Arc<dyn TokenGate>,
pub read_runtime: Option<super::HttpReadRuntime>,
pub admission: Arc<dyn HttpAdmission>,
pub takedown: Arc<dyn TakedownGate>,
pub proofs: Arc<dyn ProofServer>,
pub reachability: Arc<dyn Reachability>,
}
impl HttpSeams {
#[must_use]
pub fn new(cfg: &HttpObjectsConfig) -> Self {
Self {
tokens: Arc::new(NoTokens),
read_runtime: None,
admission: Arc::new(NoAdmission),
takedown: Arc::new(NoTakedown),
proofs: Arc::new(UnsupportedProofs),
reachability: Arc::new(TtlReachability::new(
cfg.reachability_lag_ms,
cfg.reach_cache_entries,
)),
}
}
}
impl core::fmt::Debug for HttpSeams {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("HttpSeams").finish_non_exhaustive()
}
}