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::OperationDefinition;
26use crate::{Error, Result};
27
28pub type Enter<'a> = Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>;
35
36pub struct Invocation {
39 operation: OperationDefinition,
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) -> &OperationDefinition {
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 {
121 domain: "operation middleware configuration",
122 });
123 }
124 state.entries.push(middleware);
125 Ok(())
126 }
127
128 pub(super) fn register_authorizer(&self, authorizer: Arc<dyn super::authorization::Authorizer>) -> Result<()> {
129 let mut state = self.state.lock().expect("middleware configuration lock is healthy");
130 if state.authorization_frozen {
131 return Err(Error::Closing {
132 domain: "operation authorization configuration",
133 });
134 }
135 if state.authorizer.is_some() {
136 return Err(Error::InvalidInput {
137 field: "operation authorizer",
138 reason: "authorization already has an owner",
139 });
140 }
141 state.authorizer = Some(authorizer);
142 Ok(())
143 }
144
145 #[cfg(feature = "grpc")]
146 pub(super) fn authorizer(&self) -> Option<Arc<dyn super::authorization::Authorizer>> {
147 let mut state = self.state.lock().expect("middleware configuration lock is healthy");
148 state.authorization_frozen = true;
149 state.authorizer.clone()
150 }
151
152 pub(super) fn capture(&self) -> Vec<Arc<dyn Middleware>> {
153 let mut state = self.state.lock().expect("middleware configuration lock is healthy");
154 state.frozen = true;
155 state.entries.clone()
156 }
157}
158
159impl Session {
160 pub(super) fn new(
161 entries: Vec<Arc<dyn Middleware>>, operation: OperationDefinition, id: OperationInstanceId, generation: OperationGeneration,
162 inherited: CallContext, context: Arc<OnceLock<CallContext>>, origin: super::caller::Origin,
163 ) -> Self {
164 Self {
165 call: Invocation {
166 operation,
167 id,
168 generation,
169 context: inherited,
170 origin,
171 },
172 entries,
173 entered: 0,
174 observed: false,
175 context,
176 }
177 }
178
179 pub(super) async fn enter(&mut self) -> Result<()> {
180 for entry in &self.entries {
181 self.entered += 1;
182 let result = guarded(Box::pin(async { entry.enter(&mut self.call).await })).await;
183 if let Err(error) = result {
184 self.freeze();
185 return Err(error);
186 }
187 }
188 self.freeze();
189 Ok(())
190 }
191
192 fn freeze(&self) {
193 let _ = self.context.set(self.call.context.clone());
194 }
195
196 pub(super) fn observe(&mut self, observation: Observation) {
197 if self.observed {
198 return;
199 }
200 self.observed = true;
201 for entry in self.entries[..self.entered].iter().rev() {
202 if std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
203 entry.observe(&self.call, observation);
204 }))
205 .is_err()
206 {
207 tracing::error!(operation = %self.call.operation.id, "operation middleware observation panicked");
208 }
209 }
210 }
211}
212
213impl Drop for Session {
214 fn drop(&mut self) {
215 self.observe(Observation {
216 outcome: OperationOutcome::Unavailable,
217 settled: false,
218 });
219 }
220}
221
222async fn guarded(mut future: Enter<'_>) -> Result<()> {
227 std::future::poll_fn(|context| match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| future.as_mut().poll(context))) {
228 Ok(result) => result,
229 Err(_) => std::task::Poll::Ready(Err(Error::InvalidInput {
230 field: "operation middleware",
231 reason: "middleware entry panicked",
232 })),
233 })
234 .await
235}
236
237