mkit_server/http_objects/
seams.rs1use std::sync::Arc;
5
6use mkit_core::hash::Hash;
7
8use super::body::EndHook;
9use super::reach::{Reachability, TtlReachability};
10use super::{HttpObjectResponse, HttpObjectsConfig};
11use crate::Procedure;
12use crate::repo::RepoId;
13use crate::{BoxFuture, MaybeSend, MaybeSync, Redacted, ServerError};
14
15pub trait TokenGate: MaybeSend + MaybeSync {
20 fn precheck(
22 &self,
23 token: &Redacted,
24 now_ms: i64,
25 ) -> Result<crate::url_token::Prechecked, crate::url_token::TokenRejected>;
26 fn ttl_ms(&self) -> u64;
28}
29
30#[derive(Debug, Clone, Copy, Default)]
32pub struct NoTokens;
33impl TokenGate for NoTokens {
34 fn precheck(
35 &self,
36 _: &Redacted,
37 _: i64,
38 ) -> Result<crate::url_token::Prechecked, crate::url_token::TokenRejected> {
39 Err(crate::url_token::TokenRejected)
40 }
41 fn ttl_ms(&self) -> u64 {
42 0
43 }
44}
45impl TokenGate for crate::url_token::UrlTokenConfig {
46 fn precheck(
47 &self,
48 token: &Redacted,
49 now_ms: i64,
50 ) -> Result<crate::url_token::Prechecked, crate::url_token::TokenRejected> {
51 self.precheck(token.expose(), now_ms)
52 }
53 fn ttl_ms(&self) -> u64 {
54 self.ttl_ms()
55 }
56}
57
58#[derive(Debug)]
61#[non_exhaustive]
62pub struct AdmitRequest<'a> {
63 pub repo: &'a RepoId,
65 pub procedure: Procedure,
67 pub head: bool,
69 pub ref_path: bool,
71 pub declared_bytes: u64,
73 pub credential_headers: &'a [crate::pipeline::CredentialHeader],
75}
76
77#[derive(Default)]
79pub struct Admitted {
80 pub private: bool,
82 pub headers: Vec<(&'static str, String)>,
84 pub on_end: Option<EndHook>,
87}
88
89impl core::fmt::Debug for Admitted {
90 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
91 f.debug_struct("Admitted")
92 .field("private", &self.private)
93 .field("on_end", &self.on_end.is_some())
94 .finish_non_exhaustive()
95 }
96}
97
98#[derive(Debug)]
100pub enum AdmitDecision {
101 Allow(Admitted),
103 Respond(HttpObjectResponse),
105}
106
107pub trait HttpAdmission: MaybeSend + MaybeSync {
109 fn is_configured(&self) -> bool {
112 true
113 }
114
115 fn admit<'a>(
117 &'a self,
118 request: &'a AdmitRequest<'a>,
119 ) -> BoxFuture<'a, Result<AdmitDecision, ServerError>>;
120}
121
122#[derive(Debug, Clone, Copy, Default)]
124pub struct NoAdmission;
125
126impl HttpAdmission for NoAdmission {
127 fn is_configured(&self) -> bool {
128 false
129 }
130
131 fn admit<'a>(
132 &'a self,
133 _: &'a AdmitRequest<'a>,
134 ) -> BoxFuture<'a, Result<AdmitDecision, ServerError>> {
135 Box::pin(async { Ok(AdmitDecision::Allow(Admitted::default())) })
136 }
137}
138
139#[derive(Debug)]
141pub enum TakedownVerdict {
142 Clear,
144 NotFound,
146 Respond(HttpObjectResponse),
148}
149
150pub trait TakedownGate: MaybeSend + MaybeSync {
152 fn stops_descent(&self, _repo: &RepoId, _id: &Hash) -> bool {
155 false
156 }
157
158 fn check<'a>(
163 &'a self,
164 repo: &'a RepoId,
165 leaf: &'a Hash,
166 ) -> BoxFuture<'a, Result<TakedownVerdict, ServerError>>;
167}
168
169#[derive(Debug, Clone, Copy, Default)]
171pub struct NoTakedown;
172
173impl TakedownGate for NoTakedown {
174 fn check<'a>(
175 &'a self,
176 _: &'a RepoId,
177 _: &'a Hash,
178 ) -> BoxFuture<'a, Result<TakedownVerdict, ServerError>> {
179 Box::pin(async { Ok(TakedownVerdict::Clear) })
180 }
181}
182
183pub trait ProofSource: MaybeSend {
186 fn read(&mut self, id: Hash) -> BoxFuture<'_, Result<Vec<u8>, ServerError>>;
188}
189
190#[derive(Debug, Clone)]
192pub struct PreparedProof {
193 pub commit: Hash,
195 pub leaf: Hash,
197 pub path: Vec<Vec<u8>>,
199 pub range: Option<(u64, u64)>,
201 pub encoded_len: u64,
203 pub span: bool,
205}
206
207pub trait ProofServer: MaybeSend + MaybeSync {
209 fn is_supported(&self) -> bool {
212 true
213 }
214 fn build<'a>(
216 &'a self,
217 request: &'a PreparedProof,
218 source: &'a mut dyn ProofSource,
219 ) -> BoxFuture<'a, Result<Vec<u8>, ServerError>>;
220}
221
222#[derive(Debug, Clone, Copy, Default)]
224pub struct UnsupportedProofs;
225impl ProofServer for UnsupportedProofs {
226 fn is_supported(&self) -> bool {
227 false
228 }
229 fn build<'a>(
230 &'a self,
231 _: &'a PreparedProof,
232 _: &'a mut dyn ProofSource,
233 ) -> BoxFuture<'a, Result<Vec<u8>, ServerError>> {
234 Box::pin(async {
235 Err(ServerError::new(
236 crate::Code::OutOfRange,
237 "proof unsupported",
238 ))
239 })
240 }
241}
242
243#[derive(Clone)]
245pub struct HttpSeams {
246 pub tokens: Arc<dyn TokenGate>,
248 pub read_runtime: Option<super::HttpReadRuntime>,
250 pub admission: Arc<dyn HttpAdmission>,
252 pub takedown: Arc<dyn TakedownGate>,
254 pub proofs: Arc<dyn ProofServer>,
256 pub reachability: Arc<dyn Reachability>,
258}
259
260impl HttpSeams {
261 #[must_use]
263 pub fn new(cfg: &HttpObjectsConfig) -> Self {
264 Self {
265 tokens: Arc::new(NoTokens),
266 read_runtime: None,
267 admission: Arc::new(NoAdmission),
268 takedown: Arc::new(NoTakedown),
269 proofs: Arc::new(UnsupportedProofs),
270 reachability: Arc::new(TtlReachability::new(
271 cfg.reachability_lag_ms,
272 cfg.reach_cache_entries,
273 )),
274 }
275 }
276}
277
278impl core::fmt::Debug for HttpSeams {
279 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
280 f.debug_struct("HttpSeams").finish_non_exhaustive()
281 }
282}