Skip to main content

brooks_lib/dcdn/
imdi.rs

1// brooks, Copyright 2026, Will Hawkins
2//
3// This file is part of brooks.
4
5// This file is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see <https://www.gnu.org/licenses/>.
17
18//! Interpret CDNI Interconnection Metadata as a dCDN
19
20use crate::dcdn::error::{ImdInterpretError, ImdInterpretResult};
21use crate::dcdn::interpret::{Error, MetadataInformationResultElements, Stages};
22use crate::dcdn::processors::SimpleProcessorsInterpreterContext;
23use crate::environment::scope::Scopes;
24use crate::logging::{LogLevel, LogMsg, LogMsgs};
25use crate::processing::interpret::{InterpretationResult, Interpreter};
26use crate::verifying::verify::CdniVerificationKey;
27
28use crate::prr;
29use crate::{cdni::imd::spec::HostMetadata, mel::interpreter::values::TypedValue};
30
31pub type ResultValue = Option<http::Response<Vec<u8>>>;
32
33#[derive(Debug, Clone)]
34pub enum InterpretMode {
35    Request,
36    Response,
37}
38
39// Generic implementation of `interpret::Interpreter` for CdniVerificationKey
40// that simply redirects to the implementation of the underlying key.
41impl
42    Interpreter<
43        SimpleProcessorsInterpreterContext<'_>,
44        (ResultValue, MetadataInformationResultElements),
45        Stages,
46        Error,
47    > for CdniVerificationKey
48{
49    fn interpret<'a>(
50        &self,
51        input: SimpleProcessorsInterpreterContext<'a>,
52    ) -> InterpretationResult<
53        SimpleProcessorsInterpreterContext<'a>,
54        (ResultValue, MetadataInformationResultElements),
55        Error,
56    > {
57        match &self {
58            CdniVerificationKey::Stage(typed_stage) => typed_stage.interpret(input),
59            CdniVerificationKey::Source(typed_source) => typed_source.interpret(input),
60            CdniVerificationKey::SetVariable(typed_set_variable) => {
61                typed_set_variable.interpret(input)
62            }
63            CdniVerificationKey::CrossoriginPolicy(typed_crossorigin_policy) => {
64                typed_crossorigin_policy.interpret(input)
65            }
66            CdniVerificationKey::None => todo!(),
67        }
68    }
69
70    fn at(&self, stage: Stages) -> bool {
71        match &self {
72            CdniVerificationKey::Stage(typed_stage) => typed_stage.at(stage),
73            CdniVerificationKey::Source(typed_source) => typed_source.at(stage),
74            CdniVerificationKey::SetVariable(typed_set_variable) => typed_set_variable.at(stage),
75            CdniVerificationKey::CrossoriginPolicy(typed_crossorigin_policy) => {
76                typed_crossorigin_policy.at(stage)
77            }
78            CdniVerificationKey::None => todo!(),
79        }
80    }
81}
82
83/// Interpret given Host Metadata for the given request.
84///
85/// TODO: Make clear in the documentation that this function is a _major_ entry point for library clients.
86pub fn interpret_interconnection_metadata(
87    hmd: &HostMetadata<CdniVerificationKey>,
88    mel_scopes: Scopes<TypedValue>,
89    request: Box<dyn prr::Prr<Vec<u8>>>,
90    runtime: &tokio::runtime::Handle,
91    mut log: LogMsgs,
92) -> ImdInterpretResult {
93    let mut request_context = SimpleProcessorsInterpreterContext {
94        scopes: mel_scopes,
95        mode: InterpretMode::Request,
96        runtime,
97        req: request,
98        res: None,
99    };
100
101    // Pre -- processors that want to do something _before_ processing, per se, starts.
102    for hmd in &hmd.metadata {
103        if hmd.aug.at(Stages::Pre) {
104            (request_context, _) = match hmd.aug.interpret(request_context) {
105                Ok((c, r)) => (c, r),
106                Err(e) => return Err((ImdInterpretError::MetadataProcessingError(e).into(), log)),
107            };
108        }
109    }
110
111    // ClientRequest
112    for stage in &hmd.metadata {
113        // TODO: Determine if/when/how processing will stop when there is a terminating metadata object.
114        #[allow(clippy::collapsible_if)]
115        if stage.aug.at(Stages::ClientRequest) {
116            // TODO: Handle a stage that generates MI.
117            (request_context, _) = match stage.aug.interpret(request_context) {
118                Ok((c, r)) => (c, r),
119                Err(e) => return Err((ImdInterpretError::MetadataProcessingError(e).into(), log)),
120            };
121
122            // There are no synthetic responses at this stage.
123        }
124    }
125
126    // If there were a cache, we would access it here.
127    if false {
128        todo!("Implement caching.")
129    }
130
131    log = debug!(log, "Start: processing origin request stages.");
132
133    // OriginRequest
134    for stage in &hmd.metadata {
135        // TODO: Determine if/when/how processing will stop when there is a terminating metadata object.
136        if stage.aug.at(Stages::OriginRequest) {
137            // TODO: Handle a stage that generates MI.
138            (request_context, _) = match stage.aug.interpret(request_context) {
139                Ok((c, r)) => (c, r),
140                Err(e) => return Err((ImdInterpretError::MetadataProcessingError(e).into(), log)),
141            };
142        }
143    }
144
145    log = debug!(log, "Stop: processing origin request stages.");
146
147    log = debug!(log, "Start: processing source stages.");
148
149    let mut source_context = SimpleProcessorsInterpreterContext {
150        scopes: request_context.scopes,
151        mode: InterpretMode::Request,
152        req: request_context.req,
153        res: None,
154        runtime: request_context.runtime,
155    };
156
157    let mut source_processor = false;
158
159    for hmd in &hmd.metadata {
160        // TODO: Determine if/when/how processing will stop when there is a terminating metadata object.
161        if hmd.aug.at(Stages::Source) {
162            source_processor = true;
163            (source_context, _) = match hmd.aug.interpret(source_context) {
164                Ok((c, r)) => (c, r),
165                Err(e) => {
166                    return Err((ImdInterpretError::MetadataProcessingError(e).into(), log));
167                }
168            };
169            // There is only one Source processor allowed.
170            break;
171        }
172    }
173
174    log = debug!(
175        log,
176        &format!(
177            "There was{}a source processor available.",
178            if source_processor { " " } else { " not " }
179        )
180    );
181
182    log = debug!(
183        log,
184        &format!(
185            "There was{}data retrieved by the source processor.",
186            if let Some(res) = &source_context.res
187                && res.tpe() == prr::PrrType::Response
188            {
189                " "
190            } else {
191                " not "
192            }
193        )
194    );
195
196    // TODO: Handle no source processor!
197
198    let mut response_context = SimpleProcessorsInterpreterContext {
199        scopes: source_context.scopes,
200        mode: InterpretMode::Response,
201        req: source_context.req,
202        res: source_context.res,
203        runtime: source_context.runtime,
204    };
205
206    log = debug!(log, "Stop: processing source stages.");
207
208    log = debug!(log, "Start: processing origin response stages.");
209    // For any of the host metadata entries that are origin requests or origin responses,
210    // do them now. Remember: The Request metadata objects can contain response transformations, too.
211    for stage in &hmd.metadata {
212        // TODO: Determine if/when/how processing will stop when there is a terminating metadata object.
213        if stage.aug.at(Stages::OriginResponse) {
214            let (updated_response_context, result) = match stage.aug.interpret(response_context) {
215                Ok((c, r)) => (c, r),
216                Err(e) => {
217                    return Err((ImdInterpretError::MetadataProcessingError(e).into(), log));
218                }
219            };
220
221            // Handle a result, if any.
222            if let Some(result) = result.0 {
223                log = debug!(log, "Got a response from an origin response stage.");
224                return Ok((result.status(), result, log));
225            }
226
227            response_context = updated_response_context;
228
229            // TODO: Handle other values from the PS Interpreter that indicate
230            // that processing should stop.
231        }
232    }
233    log = debug!(log, "Stop: processing origin response stages.");
234
235    // If there were a cache, we would updated it here.
236    if false {
237        todo!("Implement caching.")
238    }
239
240    log = debug!(log, "Start: processing client response stages.");
241    // For any of the host metadata entries that are client requests or client responses,
242    // do them now. Remember: The *Client metadata objects can contain response transformations, too.
243    // do them now.
244    for stage in &hmd.metadata {
245        // TODO: Determine if/when/how processing will stop when there is a terminating metadata object.
246        if stage.aug.at(Stages::ClientResponse) {
247            let (updated_response_context, result) = match stage.aug.interpret(response_context) {
248                Ok((c, r)) => (c, r),
249                Err(e) => return Err((ImdInterpretError::MetadataProcessingError(e).into(), log)),
250            };
251
252            // Handle a result, if any.
253            if let Some(result) = result.0 {
254                log = debug!(log, "Got a response from an origin response stage.");
255                return Ok((result.status(), result, log));
256            }
257
258            // TODO: Handle other values from the PS Interpreter that indicate
259            // that processing should stop.
260
261            response_context = updated_response_context;
262        }
263    }
264
265    log = debug!(log, "Stop: processing client response stages.");
266
267    log = debug!(log, "Start: processing post stages.");
268
269    // For any of the host metadata entries that are client requests or client responses,
270    // do them now. Remember: The *Client metadata objects can contain response transformations, too.
271    // do them now.
272    for stage in &hmd.metadata {
273        // TODO: Determine if/when/how processing will stop when there is a terminating metadata object.
274        if stage.aug.at(Stages::Post) {
275            let (updated_response_context, _) = match stage.aug.interpret(response_context) {
276                Ok((c, r)) => (c, r),
277                Err(e) => return Err((ImdInterpretError::MetadataProcessingError(e).into(), log)),
278            };
279
280            // TODO: Handle other values from the PS Interpreter that indicate
281            // that processing should stop.
282
283            response_context = updated_response_context;
284        }
285    }
286    log = debug!(log, "Stop: processing post stages.");
287
288    log = debug!(log, "Trying to convert a response into a response!");
289
290    let processed_http_response = match response_context.res {
291        Some(res) => res,
292        None => todo!(),
293    };
294    let interpreted_http_response: http::Response<Vec<u8>> =
295        match (&*processed_http_response as &dyn prr::Prr<Vec<u8>>).try_into() {
296            Ok(o) => o,
297            Err(e) => {
298                return Err((ImdInterpretError::TransformError(e.into()).into(), log));
299            }
300        };
301
302    log = debug!(
303        log,
304        &format!("Response code: {}", interpreted_http_response.status())
305    );
306
307    Ok((
308        interpreted_http_response.status(),
309        interpreted_http_response,
310        log,
311    ))
312}