harmos_runtime/operations/
middleware.rs1use std::future::Future;
18use std::pin::Pin;
19use std::sync::{Arc, Mutex, OnceLock};
20
21use super::{CallContext, OperationGeneration, OperationInstanceId, OperationOutcome};
25use crate::operations::Definition;
26use crate::{Error, Result};
27
28pub type Enter<'a> = Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>;
35
36pub struct Invocation {
39 operation: Definition,
40 id: OperationInstanceId,
41 generation: OperationGeneration,
42 context: CallContext,
43 origin: super::caller::Origin,
44}
45
46#[derive(Clone, Copy, Debug)]
47pub struct Observation {
48 pub outcome: OperationOutcome,
49 pub settled: bool,
50}
51
52#[derive(Default)]
53pub(super) struct Configuration {
54 state: Mutex<State>,
55}
56
57#[derive(Default)]
58struct State {
59 frozen: bool,
60 entries: Vec<Arc<dyn Middleware>>,
61 authorizer: Option<Arc<dyn super::authorization::Authorizer>>,
62 authorization_frozen: bool,
63}
64
65pub(super) struct Session {
66 call: Invocation,
67 entries: Vec<Arc<dyn Middleware>>,
68 entered: usize,
69 observed: bool,
70 context: Arc<OnceLock<CallContext>>,
71}
72
73pub trait Middleware: Send + Sync + 'static {
78 fn enter<'a>(&'a self, call: &'a mut Invocation) -> Enter<'a>;
79
80 fn observe(&self, _call: &Invocation, _observation: Observation) {}
81}
82
83impl Invocation {
86 pub fn is_host_call(&self) -> bool {
87 self.origin.identity == super::caller::Identity::Host
88 }
89
90 #[cfg(any(feature = "plugin", feature = "plugin-guest", feature = "plugin-sidecar"))]
91 pub fn caller_scope(&self) -> Option<&crate::plugin::PluginScope> {
92 self.origin.scope.as_ref()
93 }
94
95 pub fn operation(&self) -> &Definition {
96 &self.operation
97 }
98
99 pub fn id(&self) -> OperationInstanceId {
100 self.id
101 }
102
103 pub fn generation(&self) -> OperationGeneration {
104 self.generation
105 }
106
107 pub fn context(&self) -> &CallContext {
108 &self.context
109 }
110
111 pub fn context_mut(&mut self) -> crate::operations::ContextMut<'_> {
112 super::ContextMut::new(&mut self.context)
113 }
114}
115
116impl Configuration {
117 pub(super) fn register(&self, middleware: Arc<dyn Middleware>) -> Result<()> {
118 let mut state = self.state.lock().expect("middleware configuration lock is healthy");
119 if state.frozen {
120 return Err(Error::Closing { domain: "operation middleware configuration" });
121 }
122 state.entries.push(middleware);
123 Ok(())
124 }
125
126 pub(super) fn register_authorizer(&self, authorizer: Arc<dyn super::authorization::Authorizer>) -> Result<()> {
127 let mut state = self.state.lock().expect("middleware configuration lock is healthy");
128 if state.authorization_frozen {
129 return Err(Error::Closing { domain: "operation authorization configuration" });
130 }
131 if state.authorizer.is_some() {
132 return Err(Error::InvalidInput { field: "operation authorizer", reason: "authorization already has an owner" });
133 }
134 state.authorizer = Some(authorizer);
135 Ok(())
136 }
137
138 #[cfg(feature = "grpc")]
139 pub(super) fn authorizer(&self) -> Option<Arc<dyn super::authorization::Authorizer>> {
140 let mut state = self.state.lock().expect("middleware configuration lock is healthy");
141 state.authorization_frozen = true;
142 state.authorizer.clone()
143 }
144
145 pub(super) fn capture(&self) -> Vec<Arc<dyn Middleware>> {
146 let mut state = self.state.lock().expect("middleware configuration lock is healthy");
147 state.frozen = true;
148 state.entries.clone()
149 }
150}
151
152impl Session {
153 pub(super) fn new(
154 entries: Vec<Arc<dyn Middleware>>, operation: Definition, id: OperationInstanceId, generation: OperationGeneration,
155 inherited: CallContext, context: Arc<OnceLock<CallContext>>, origin: super::caller::Origin,
156 ) -> Self {
157 Self { call: Invocation { operation, id, generation, context: inherited, origin }, entries, entered: 0, observed: false, context }
158 }
159
160 pub(super) async fn enter(&mut self) -> Result<()> {
161 for entry in &self.entries {
162 self.entered += 1;
163 let result = guarded(Box::pin(async { entry.enter(&mut self.call).await })).await;
164 if let Err(error) = result {
165 self.freeze();
166 return Err(error);
167 }
168 }
169 self.freeze();
170 Ok(())
171 }
172
173 fn freeze(&self) {
174 let _ = self.context.set(self.call.context.clone());
175 }
176
177 pub(super) fn observe(&mut self, observation: Observation) {
178 if self.observed {
179 return;
180 }
181 self.observed = true;
182 for entry in self.entries[..self.entered].iter().rev() {
183 if std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
184 entry.observe(&self.call, observation);
185 }))
186 .is_err()
187 {
188 tracing::error!(operation = %self.call.operation.id, "operation middleware observation panicked");
189 }
190 }
191 }
192}
193
194impl Drop for Session {
195 fn drop(&mut self) {
196 self.observe(Observation { outcome: OperationOutcome::Unavailable, settled: false });
197 }
198}
199
200async fn guarded(mut future: Enter<'_>) -> Result<()> {
205 std::future::poll_fn(|context| match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| future.as_mut().poll(context))) {
206 Ok(result) => result,
207 Err(_) => std::task::Poll::Ready(Err(Error::InvalidInput { field: "operation middleware", reason: "middleware entry panicked" })),
208 })
209 .await
210}
211
212