Skip to content
Clone
2f11d69
284 lines 6.6 KB Rust
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<String, String>,
}
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<String>, value: impl Into<String>) -> Option<String> {
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<String> {
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<Item = (&String, &String)> {
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<String>, scheme: AuthScheme) -> Self {
Self {
name: name.into(),
scheme,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct AuthContext {
pub scheme: AuthScheme,
pub principal: Option<Principal>,
pub raw: Option<String>,
}
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<String>,
}
#[derive(Debug, Clone, Default)]
pub struct RefUpdateBatch {
pub updates: Vec<RefUpdateEntry>,
}
impl RefUpdateBatch {
pub fn new() -> Self {
Self::default()
}
pub fn push(
mut self,
name: impl Into<String>,
new_oid: impl Into<String>,
old_oid: Option<String>,
) -> 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<String>,
pub headers: HeaderMap,
pub body: Box<dyn AsyncRead + Unpin + Send + Sync>,
pub guard: StreamGuard,
pub output_cap: u64,
pub auth: Option<AuthContext>,
pub endpoint: EndpointKind,
pub remote_addr: Option<String>,
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", &"<dyn AsyncRead>")
.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<String>,
path: impl Into<String>,
endpoint: EndpointKind,
service: impl Into<String>,
body: Box<dyn AsyncRead + Unpin + Send + Sync>,
) -> 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<String>) -> Self {
self.query = Some(query.into());
self
}
pub fn with_header(mut self, key: impl Into<String>, value: impl Into<String>) -> 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<String>) -> Self {
self.remote_addr = Some(addr.into());
self
}
}
#[derive(Debug, Clone)]
pub struct StreamGuard {
cancelled: Arc<AtomicBool>,
}
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<AtomicBool>,
}
impl StreamGuardHandle {
pub fn is_cancelled(&self) -> bool {
self.cancelled.load(Ordering::SeqCst)
}
pub fn cancel(&self) {
self.cancelled.store(true, Ordering::SeqCst);
}
}