1use crate::link_handshake::{LinkTransport, NOISE_MAX_MESSAGE, NOISE_OVERHEAD};
5use liminal::protocol::{Frame, codec};
6use zeroize::Zeroizing;
7
8#[path = "link_route_call/client.rs"]
9pub mod client;
10#[path = "link_route_call/pending.rs"]
11pub mod pending;
12#[cfg(test)]
13#[path = "link_route_call_tests.rs"]
14mod tests;
15#[path = "link_route_call/wire.rs"]
16mod wire;
17
18pub trait CallExecution: Send + std::fmt::Debug {
20 fn finish(&mut self) -> Result<(), CallError>;
25}
26
27pub trait CallExecutor: Send + Sync + std::fmt::Debug {
29 fn retained(&self) -> usize;
31 fn observe_empty(&self) -> Result<(), CallError>;
36 fn start(
41 &self,
42 request: CallRequest,
43 completed: Box<dyn FnOnce(Result<CallReply, CallError>) + Send>,
44 ) -> Result<Box<dyn CallExecution>, CallError>;
45}
46
47#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
49pub enum CallError {
50 #[error("link_call_too_large")]
52 Bound,
53 #[error("link_call_capacity")]
55 Capacity,
56 #[error("link_call_invalid")]
58 Invalid,
59 #[error("link_call_correlation_mismatch")]
61 Correlation,
62 #[error("link_call_peer_gone")]
64 PeerGone,
65 #[error("link_call_cancelled")]
67 Cancelled,
68 #[error("link_call_deadline")]
70 Deadline,
71 #[error("link_call_unavailable")]
73 Unavailable,
74 #[error("no_server")]
76 NoServer,
77 #[error("link_call_grant_refused")]
79 GrantRefused,
80}
81
82pub struct CallRequest {
85 id: u64,
86 subject: String,
87 bytes: Zeroizing<Vec<u8>>,
88}
89
90impl std::fmt::Debug for CallRequest {
91 fn fmt(&self, out: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92 out.debug_struct("CallRequest")
93 .field("id", &self.id)
94 .field("bytes", &self.bytes.len())
95 .finish_non_exhaustive()
96 }
97}
98
99impl CallRequest {
100 pub fn new(
105 id: u64,
106 subject: String,
107 frames: &[Frame],
108 bound: usize,
109 ) -> Result<Self, CallError> {
110 let bytes = wire::encode_frames(frames, bound)?;
111 Self::decode(id, subject, bytes, bound)
112 }
113
114 fn decode(
115 id: u64,
116 subject: String,
117 bytes: Zeroizing<Vec<u8>>,
118 bound: usize,
119 ) -> Result<Self, CallError> {
120 if id == 0 || bytes.len() > bound {
121 return Err(CallError::Invalid);
122 }
123 crate::auth_lys::SubjectBinding::new("wire".into(), subject.clone(), subject.clone())
124 .map_err(|_| CallError::Invalid)?;
125 let mut frames = wire::request_frames(&bytes, &subject)?;
126 for frame in &mut frames {
127 if let Frame::Connect { auth_token, .. } = frame {
128 zeroize::Zeroize::zeroize(auth_token);
129 }
130 }
131 drop(frames);
132 Ok(Self { id, subject, bytes })
133 }
134 #[must_use]
136 pub const fn id(&self) -> u64 {
137 self.id
138 }
139 #[must_use]
141 pub fn subject(&self) -> &str {
142 &self.subject
143 }
144 pub fn frames(&self) -> Result<Vec<Frame>, CallError> {
149 wire::request_frames(&self.bytes, &self.subject)
150 }
151 #[must_use]
153 pub fn wire_bytes(&self) -> &[u8] {
154 &self.bytes
155 }
156 pub fn correlation(&self) -> Result<(u32, u64), CallError> {
161 let mut frames = self.frames()?;
162 for frame in &mut frames {
163 if let Frame::Connect { auth_token, .. } = frame {
164 zeroize::Zeroize::zeroize(auth_token);
165 }
166 }
167 let Some(Frame::ConversationOpen {
168 stream_id,
169 conversation_id,
170 ..
171 }) = frames.get(1)
172 else {
173 return Err(CallError::Invalid);
174 };
175 Ok((*stream_id, *conversation_id))
176 }
177}
178
179pub struct CallReply {
181 id: u64,
182 frame: Frame,
183}
184impl std::fmt::Debug for CallReply {
185 fn fmt(&self, out: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
186 out.debug_struct("CallReply")
187 .field("id", &self.id)
188 .finish_non_exhaustive()
189 }
190}
191impl CallReply {
192 pub fn new(id: u64, frame: Frame) -> Result<Self, CallError> {
197 if id == 0
198 || !matches!(
199 frame,
200 Frame::ConversationMessage { .. } | Frame::ConversationError { .. }
201 )
202 {
203 return Err(CallError::Invalid);
204 }
205 Ok(Self { id, frame })
206 }
207 #[must_use]
209 pub const fn id(&self) -> u64 {
210 self.id
211 }
212 #[must_use]
214 pub fn into_frame(self) -> Frame {
215 self.frame
216 }
217}
218
219pub enum CallPacket {
222 Request(CallRequest),
224 Reply(CallReply),
226 Cancel(u64),
228 Topology(Zeroizing<Vec<u8>>),
230}
231
232impl CallPacket {
233 #[must_use]
235 pub fn is_call(bytes: &[u8]) -> bool {
236 bytes.starts_with(wire::MAGIC)
237 }
238 pub fn encode(&self, bound: usize) -> Result<Zeroizing<Vec<u8>>, CallError> {
243 wire::encode(self, bound)
244 }
245 pub fn decode(bytes: &[u8], bound: usize) -> Result<Self, CallError> {
250 wire::decode(bytes, bound)
251 }
252 pub fn seal(&self, transport: &mut LinkTransport, bound: usize) -> Result<Vec<u8>, CallError> {
257 let bytes = self.encode(bound)?;
258 let mut sealed = vec![0; bytes.len() + NOISE_OVERHEAD];
259 let written = transport
260 .seal(&bytes, &mut sealed)
261 .map_err(|_| CallError::Invalid)?;
262 sealed.truncate(written);
263 Ok(sealed)
264 }
265 pub fn open(
270 transport: &mut LinkTransport,
271 sealed: &[u8],
272 bound: usize,
273 ) -> Result<Self, CallError> {
274 if sealed.len() > NOISE_MAX_MESSAGE || sealed.len() > bound.saturating_add(NOISE_OVERHEAD) {
275 return Err(CallError::Bound);
276 }
277 let mut plain = Zeroizing::new(vec![0; sealed.len()]);
278 let written = transport
279 .open(sealed, &mut plain)
280 .map_err(|_| CallError::Invalid)?;
281 plain.truncate(written);
282 Self::decode(&plain, bound)
283 }
284}