use std::collections::HashMap; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use tokio::io::AsyncRead; use crate::server::endpoint::EndpointKind; use crate::server::limits::DEFAULT_OUTPUT_CAP_BYTES; /// Case-insensitive HTTP header map. Header names are stored lower-cased so /// lookups match regardless of case (per HTTP semantics), without pulling in /// the `http` crate. #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct HeaderMap { inner: HashMap, } impl HeaderMap { pub fn new() -> Self { Self { inner: HashMap::new(), } } /// Insert a header, normalizing the name to lower-case. pub fn insert(&mut self, key: impl Into, value: impl Into) -> Option { self.inner .insert(key.into().to_ascii_lowercase(), value.into()) } /// Case-insensitive lookup. pub fn get(&self, key: &str) -> Option<&String> { self.inner.get(&key.to_ascii_lowercase()) } pub fn remove(&mut self, key: &str) -> Option { self.inner.remove(&key.to_ascii_lowercase()) } pub fn contains_key(&self, key: &str) -> bool { self.inner.contains_key(&key.to_ascii_lowercase()) } pub fn len(&self) -> usize { self.inner.len() } pub fn is_empty(&self) -> bool { self.inner.is_empty() } pub fn iter(&self) -> impl Iterator { self.inner.iter() } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum AuthScheme { #[default] Anonymous, Basic, Bearer, Ssh, } impl AuthScheme { pub fn as_str(&self) -> &'static str { match self { Self::Anonymous => "anonymous", Self::Basic => "basic", Self::Bearer => "bearer", Self::Ssh => "ssh", } } } #[derive(Debug, Clone)] pub struct Principal { pub name: String, pub scheme: AuthScheme, } impl Principal { pub fn new(name: impl Into, scheme: AuthScheme) -> Self { Self { name: name.into(), scheme, } } } #[derive(Debug, Clone, Default)] pub struct AuthContext { pub scheme: AuthScheme, pub principal: Option, pub raw: Option, } impl AuthContext { pub fn anonymous() -> Self { Self { scheme: AuthScheme::Anonymous, principal: None, raw: None, } } pub fn with_principal(scheme: AuthScheme, principal: Principal) -> Self { Self { scheme, principal: Some(principal), raw: None, } } } #[derive(Debug, Clone)] pub struct RefUpdateEntry { pub name: String, pub new_oid: String, pub old_oid: Option, } #[derive(Debug, Clone, Default)] pub struct RefUpdateBatch { pub updates: Vec, } impl RefUpdateBatch { pub fn new() -> Self { Self::default() } pub fn push( mut self, name: impl Into, new_oid: impl Into, old_oid: Option, ) -> Self { self.updates.push(RefUpdateEntry { name: name.into(), new_oid: new_oid.into(), old_oid, }); self } pub fn is_empty(&self) -> bool { self.updates.is_empty() } pub fn len(&self) -> usize { self.updates.len() } } pub struct ServerRequest { pub method: String, pub path: String, pub query: Option, pub headers: HeaderMap, pub body: Box, pub guard: StreamGuard, pub output_cap: u64, pub auth: Option, pub endpoint: EndpointKind, pub remote_addr: Option, pub service: String, } impl std::fmt::Debug for ServerRequest { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ServerRequest") .field("method", &self.method) .field("path", &self.path) .field("query", &self.query) .field("headers", &self.headers) .field("body", &"") .field("guard", &self.guard) .field("output_cap", &self.output_cap) .field("auth", &self.auth) .field("endpoint", &self.endpoint) .field("remote_addr", &self.remote_addr) .field("service", &self.service) .finish() } } impl ServerRequest { pub fn new( method: impl Into, path: impl Into, endpoint: EndpointKind, service: impl Into, body: Box, ) -> Self { Self { method: method.into(), path: path.into(), query: None, headers: HeaderMap::new(), body, guard: StreamGuard::new(), output_cap: DEFAULT_OUTPUT_CAP_BYTES as u64, auth: None, endpoint, remote_addr: None, service: service.into(), } } pub fn with_query(mut self, query: impl Into) -> Self { self.query = Some(query.into()); self } pub fn with_header(mut self, key: impl Into, value: impl Into) -> Self { self.headers.insert(key.into(), value.into()); self } pub fn with_auth(mut self, auth: AuthContext) -> Self { self.auth = Some(auth); self } pub fn with_output_cap(mut self, cap_bytes: u64) -> Self { self.output_cap = cap_bytes; self } pub fn with_remote_addr(mut self, addr: impl Into) -> Self { self.remote_addr = Some(addr.into()); self } } #[derive(Debug, Clone)] pub struct StreamGuard { cancelled: Arc, } impl Default for StreamGuard { fn default() -> Self { Self::new() } } impl StreamGuard { pub fn new() -> Self { Self { cancelled: Arc::new(AtomicBool::new(false)), } } pub fn cancel(&self) { self.cancelled.store(true, Ordering::SeqCst); } pub fn is_cancelled(&self) -> bool { self.cancelled.load(Ordering::SeqCst) } pub fn handle(&self) -> StreamGuardHandle { StreamGuardHandle { cancelled: self.cancelled.clone(), } } } #[derive(Debug, Clone)] pub struct StreamGuardHandle { cancelled: Arc, } impl StreamGuardHandle { pub fn is_cancelled(&self) -> bool { self.cancelled.load(Ordering::SeqCst) } pub fn cancel(&self) { self.cancelled.store(true, Ordering::SeqCst); } }