1use std::net::IpAddr;
8use std::time::Duration;
9
10use bytes::Bytes;
11use sipx_sip::build::ResponseBuilder;
12use sipx_sip::error::BuildError;
13use sipx_sip::transaction::{
14 Dispatch, Output, Reliability, Timer, TransactionKey, TransactionLayer, TuEvent,
15};
16use sipx_sip::{HeaderName, Limits, Request, Response, StatusCode, parse_datagram};
17use sipx_transport::timers::TimerQueue;
18use sipx_transport::{Handle, Incoming, Target, TransportKind};
19use thiserror::Error;
20use tokio::sync::mpsc;
21use tokio::task::JoinHandle;
22
23use crate::link::{Faults, Link, Side};
24use crate::time::Virtual;
25
26#[derive(Debug, Error)]
28#[non_exhaustive]
29pub enum HarnessError {
30 #[error("the request has no client transaction key")]
32 NoClientTransaction,
33 #[error("no invitation is waiting to be answered")]
35 NoInvitation,
36 #[error("{0} is outside the SIP response status range")]
38 InvalidStatus(u16),
39 #[error(transparent)]
41 Build(#[from] BuildError),
42 #[error("the in-process endpoint closed")]
44 EndpointClosed,
45 #[error(transparent)]
47 Call(#[from] sipx_call::Error),
48 #[error("the dial task stopped before returning a call")]
50 DialTask,
51 #[error("the dial completed before its invitation reached the peer")]
53 DialBeforeInvitation,
54 #[error("the established dialog did not deliver its ACK")]
56 MissingAck,
57 #[error(transparent)]
59 Transport(#[from] sipx_transport::Error),
60}
61
62#[derive(Debug)]
63struct Stack {
64 transactions: TransactionLayer,
65 timers: TimerQueue<(TransactionKey, Timer), Virtual>,
66}
67
68impl Stack {
69 fn new() -> Self {
70 Self {
71 transactions: TransactionLayer::new(sipx_sip::transaction::Timers::default()),
72 timers: TimerQueue::new(),
73 }
74 }
75
76 fn perform(
77 &mut self,
78 key: &TransactionKey,
79 outputs: Vec<Output>,
80 now: Virtual,
81 events: &mut Vec<TuEvent>,
82 ) -> Vec<Bytes> {
83 let mut wire = Vec::new();
84 for output in outputs {
85 match output {
86 Output::Send(message) => wire.push(message.to_bytes()),
87 Output::SetTimer { timer, after } => {
88 self.timers.set((key.clone(), timer), now, after);
89 }
90 Output::ClearTimer(timer) => self.timers.clear(&(key.clone(), timer)),
91 Output::ToTu(event) => events.push(*event),
92 Output::Terminated(_) => self.timers.forget_matching(|(other, _)| other == key),
93 }
94 }
95 wire
96 }
97
98 fn fire(&mut self, now: Virtual, events: &mut Vec<TuEvent>) -> Vec<Bytes> {
99 let mut wire = Vec::new();
100 for (key, timer) in self.timers.take_due(now) {
101 let outputs = self.transactions.on_timer(&key, timer);
102 wire.extend(self.perform(&key, outputs, now, events));
103 }
104 wire
105 }
106
107 fn receive(&mut self, bytes: Bytes, now: Virtual, events: &mut Vec<TuEvent>) -> Vec<Bytes> {
108 let Ok(message) = parse_datagram(bytes, &Limits::datagram()) else {
109 return Vec::new();
110 };
111 match self.transactions.receive(message, Reliability::Unreliable) {
112 Dispatch::Created { key, outputs } | Dispatch::Matched { key, outputs } => {
113 self.perform(&key, outputs, now, events)
114 }
115 Dispatch::Unmatched(_) => Vec::new(),
116 }
117 }
118}
119
120#[derive(Debug)]
122pub struct CallHarness {
123 caller: Handle,
124 callee: Handle,
125 callee_incoming: mpsc::Receiver<Incoming>,
126}
127
128#[derive(Debug)]
130pub struct PendingCall<'a> {
131 invitation: Incoming,
132 dial: DialTask,
133 callee: &'a Handle,
134 callee_incoming: &'a mut mpsc::Receiver<Incoming>,
135}
136
137#[derive(Debug)]
138struct DialTask(Option<JoinHandle<Result<sipx_call::Call, sipx_call::Error>>>);
139
140impl DialTask {
141 async fn finish(&mut self) -> Result<sipx_call::Call, HarnessError> {
142 let Some(task) = self.0.as_mut() else {
143 return Err(HarnessError::DialTask);
144 };
145 let result = task
146 .await
147 .map_err(|_| HarnessError::DialTask)?
148 .map_err(HarnessError::from);
149 self.0.take();
150 result
151 }
152}
153
154impl Drop for DialTask {
155 fn drop(&mut self) {
156 if let Some(task) = self.0.as_ref() {
157 task.abort();
158 }
159 }
160}
161
162#[derive(Debug)]
164pub struct EstablishedCall {
165 pub caller: sipx_call::Call,
167 pub callee: sipx_call::Call,
169}
170
171impl CallHarness {
172 pub fn new() -> Result<Self, HarnessError> {
177 let ((originating, _originating_incoming), (answering, answering_incoming)) =
178 sipx_transport::in_process_pair(32)?;
179 Ok(Self {
180 caller: originating,
181 callee: answering,
182 callee_incoming: answering_incoming,
183 })
184 }
185
186 pub async fn dial(
188 &mut self,
189 to: sipx_sip::Uri,
190 options: sipx_call::DialOptions,
191 ) -> Result<PendingCall<'_>, HarnessError> {
192 let endpoint = self.caller.clone();
193 let target = Target::new(self.callee.local_addr(), TransportKind::Udp);
194 let mut dial = DialTask(Some(tokio::spawn(async move {
195 sipx_call::dial(&endpoint, target, &to, &options).await
196 })));
197 let invitation = tokio::select! {
198 result = dial.finish() => {
199 return match result {
200 Ok(_) => Err(HarnessError::DialBeforeInvitation),
201 Err(error) => Err(error),
202 };
203 }
204 incoming = self.callee_incoming.recv() => {
205 incoming.ok_or(HarnessError::EndpointClosed)?
206 }
207 };
208 if invitation.request.method != sipx_sip::Method::Invite {
209 return Err(HarnessError::NoInvitation);
210 }
211 Ok(PendingCall {
212 invitation,
213 dial,
214 callee: &self.callee,
215 callee_incoming: &mut self.callee_incoming,
216 })
217 }
218}
219
220impl PendingCall<'_> {
221 #[must_use]
223 pub const fn invitation(&self) -> &Incoming {
224 &self.invitation
225 }
226
227 pub async fn answer(self, media_address: IpAddr) -> Result<EstablishedCall, HarnessError> {
229 let PendingCall {
230 invitation,
231 mut dial,
232 callee,
233 callee_incoming,
234 } = self;
235 let answer = sipx_call::answer(callee, &invitation, media_address);
236 let (caller, mut callee_call) = tokio::try_join!(dial.finish(), async {
237 answer.await.map_err(HarnessError::from)
238 })?;
239 let ack = callee_incoming
240 .recv()
241 .await
242 .ok_or(HarnessError::EndpointClosed)?;
243 if ack.request.method != sipx_sip::Method::Ack || !callee_call.handle(&ack).await? {
244 return Err(HarnessError::MissingAck);
245 }
246 Ok(EstablishedCall {
247 caller,
248 callee: callee_call,
249 })
250 }
251}
252
253#[derive(Debug)]
255pub struct TransactionHarness {
256 now: Virtual,
257 link: Link<Virtual>,
258 caller: Stack,
259 callee: Stack,
260 caller_events: Vec<TuEvent>,
261 callee_events: Vec<TuEvent>,
262 caller_scope: usize,
263 callee_scope: usize,
264}
265
266impl TransactionHarness {
267 #[must_use]
269 pub fn new(seed: u64, faults: Faults) -> Self {
270 Self {
271 now: Virtual::epoch(),
272 link: Link::new(seed, faults),
273 caller: Stack::new(),
274 callee: Stack::new(),
275 caller_events: Vec::new(),
276 callee_events: Vec::new(),
277 caller_scope: 0,
278 callee_scope: 0,
279 }
280 }
281
282 #[must_use]
284 pub fn perfect() -> Self {
285 Self::new(0, Faults::default())
286 }
287
288 pub fn place(&mut self, request: Request) -> Result<(), HarnessError> {
290 self.caller_scope = self.caller_events.len();
291 self.callee_scope = self.callee_events.len();
292 let Some((key, outputs)) = self
293 .caller
294 .transactions
295 .send_request(request, Reliability::Unreliable)
296 else {
297 return Err(HarnessError::NoClientTransaction);
298 };
299 let wire = self
300 .caller
301 .perform(&key, outputs, self.now, &mut self.caller_events);
302 self.send(Side::Left, wire);
303 self.pump();
304 Ok(())
305 }
306
307 pub fn answer(
312 &mut self,
313 status: StatusCode,
314 reason: impl Into<Bytes>,
315 ) -> Result<(), HarnessError> {
316 let Some(request) = self.invitation().cloned() else {
317 return Err(HarnessError::NoInvitation);
318 };
319 let Some(to) = request.headers.value(&HeaderName::To) else {
320 return Err(BuildError::MissingRequiredResponseHeader { header: "To" }.into());
321 };
322 let mut tagged_to = to.into_owned();
323 tagged_to.extend_from_slice(b";tag=sipx-testkit");
324 let contact = Bytes::from(format!("<{}>", request.uri));
325 let response = ResponseBuilder::to_request(&request, status, reason)?
326 .set_header(&HeaderName::To, Bytes::from(tagged_to))?
327 .header(HeaderName::Contact, contact)?
328 .build();
329 self.answer_with(response)
330 }
331
332 pub fn answer_ok(&mut self) -> Result<(), HarnessError> {
334 let ok = StatusCode::new(200).ok_or(HarnessError::InvalidStatus(200))?;
335 self.answer(ok, "OK")
336 }
337
338 pub fn answer_with(&mut self, response: Response) -> Result<(), HarnessError> {
340 let Some(request) = self.invitation() else {
341 return Err(HarnessError::NoInvitation);
342 };
343 let Some(key) = TransactionKey::from_request(request) else {
344 return Err(HarnessError::NoInvitation);
345 };
346 let outputs = self.callee.transactions.send_response(&key, response);
347 let wire = self
348 .callee
349 .perform(&key, outputs, self.now, &mut self.callee_events);
350 self.send(Side::Right, wire);
351 self.pump();
352 Ok(())
353 }
354
355 pub fn advance(&mut self, by: Duration) {
357 let until = self.now + by;
358 while self.now < until {
359 let next = [
360 self.link.next_arrival(),
361 self.caller.timers.next_deadline(),
362 self.callee.timers.next_deadline(),
363 Some(until),
364 ]
365 .into_iter()
366 .flatten()
367 .filter(|instant| *instant >= self.now)
368 .min()
369 .unwrap_or(until);
370 self.now = next;
371 self.pump();
372 let wire = self.caller.fire(self.now, &mut self.caller_events);
373 self.send(Side::Left, wire);
374 let wire = self.callee.fire(self.now, &mut self.callee_events);
375 self.send(Side::Right, wire);
376 self.pump();
377 if self.now == until {
378 break;
379 }
380 }
381 }
382
383 #[must_use]
385 pub const fn now(&self) -> Virtual {
386 self.now
387 }
388
389 #[must_use]
391 pub fn invitation(&self) -> Option<&Request> {
392 self.callee_events
393 .get(self.callee_scope..)
394 .unwrap_or(&[])
395 .iter()
396 .rev()
397 .find_map(|event| match event {
398 TuEvent::Request(request) if request.method == sipx_sip::Method::Invite => {
399 Some(request.as_ref())
400 }
401 _ => None,
402 })
403 }
404
405 #[must_use]
407 pub fn response(&self) -> Option<&Response> {
408 self.caller_events
409 .get(self.caller_scope..)
410 .unwrap_or(&[])
411 .iter()
412 .rev()
413 .find_map(|event| match event {
414 TuEvent::Response(response) => Some(response.as_ref()),
415 _ => None,
416 })
417 }
418
419 #[must_use]
421 pub fn dropped(&self) -> u64 {
422 self.link.dropped()
423 }
424
425 fn send(&mut self, from: Side, wire: Vec<Bytes>) {
426 for bytes in wire {
427 self.link.send(from, bytes, self.now);
428 }
429 }
430
431 fn pump(&mut self) {
432 loop {
433 let deliveries = self.link.take_due(self.now);
434 if deliveries.is_empty() {
435 break;
436 }
437 for delivery in deliveries {
438 let wire = match delivery.to {
439 Side::Left => {
440 self.caller
441 .receive(delivery.bytes, self.now, &mut self.caller_events)
442 }
443 Side::Right => {
444 self.callee
445 .receive(delivery.bytes, self.now, &mut self.callee_events)
446 }
447 };
448 self.send(delivery.to, wire);
449 }
450 }
451 }
452}
453
454#[cfg(test)]
455#[allow(
456 clippy::unwrap_used,
457 clippy::expect_used,
458 clippy::panic,
459 clippy::indexing_slicing
460)]
461mod tests {
462 use std::future::{Future, poll_fn};
463 use std::task::Poll;
464
465 use tokio::sync::oneshot;
466
467 use super::DialTask;
468
469 struct OnDrop(Option<oneshot::Sender<()>>);
470
471 impl Drop for OnDrop {
472 fn drop(&mut self) {
473 if let Some(sender) = self.0.take() {
474 let _ = sender.send(());
475 }
476 }
477 }
478
479 #[tokio::test]
480 async fn dropping_a_pending_dial_aborts_its_owned_task() {
481 let (started, running) = oneshot::channel();
482 let (dropped, cancelled) = oneshot::channel();
483 let task = tokio::spawn(async move {
484 let _on_drop = OnDrop(Some(dropped));
485 let _ = started.send(());
486 std::future::pending::<Result<sipx_call::Call, sipx_call::Error>>().await
487 });
488 let dial = DialTask(Some(task));
489 running.await.expect("dial task started");
490
491 drop(dial);
492
493 cancelled.await.expect("dial task was cancelled");
494 }
495
496 #[tokio::test]
497 async fn cancelling_finish_after_it_was_polled_still_aborts_the_dial() {
498 let (started, running) = oneshot::channel();
499 let (dropped, cancelled) = oneshot::channel();
500 let task = tokio::spawn(async move {
501 let _on_drop = OnDrop(Some(dropped));
502 let _ = started.send(());
503 std::future::pending::<Result<sipx_call::Call, sipx_call::Error>>().await
504 });
505 let mut dial = DialTask(Some(task));
506 running.await.expect("dial task started");
507 let mut finish = Box::pin(dial.finish());
508 poll_fn(|context| {
509 assert!(matches!(finish.as_mut().poll(context), Poll::Pending));
510 Poll::Ready(())
511 })
512 .await;
513
514 drop(finish);
515 drop(dial);
516
517 cancelled.await.expect("polled dial task was cancelled");
518 }
519}