1use std::{
2 future::{poll_fn, Future},
3 io,
4 net::SocketAddr,
5 pin::Pin,
6 task::{Context, Poll},
7 time::{Duration, Instant},
8};
9
10use tokio::time;
11
12use crate::{
13 client::validate_datagram_length,
14 config::ClientConfig,
15 error::ClientError,
16 event::{ClientEvent, OpenOutcome},
17 receive::{drain_tx_timestamps, try_enable_tx_timestamping, try_recv_tokio_datagram},
18 session::machine::{
19 recv_buffer_size, OpenDatagramDisposition, PreparedOpenAcceptance, PreparedOpenRequest,
20 PreparedProbe, SessionMachine, TimeoutBatch, MAX_OPEN_PACKET_SIZE,
21 },
22 socket::{connect_tokio_udp_socket, resolve_remote_tokio},
23 socket_options::{apply_traffic_class_to_tokio_socket, clear_dscp_on_tokio_socket},
24 timing::ClientTimestamp,
25 SendProbeError, SendReceipt,
26};
27
28const OPEN_POLL_WORK_BUDGET: usize = 64;
34
35const RECV_POLL_WORK_BUDGET: usize = 64;
47
48#[derive(Debug)]
49struct PreparedAsyncOpen {
50 machine: PreparedOpenAcceptance,
51 recv_buffer_len: Option<usize>,
52 negotiated_traffic_class: Option<u8>,
53}
54
55#[derive(Debug)]
56struct PreparedAsyncOpenFailure {
57 primary: ClientError,
58 machine: PreparedOpenAcceptance,
59}
60
61#[derive(Debug)]
62enum AsyncOpenCleanup {
63 Acceptance {
64 primary: ClientError,
65 packet: Option<Box<[u8]>>,
66 },
67 Adapter(Box<PreparedAsyncOpenFailure>),
68}
69
70impl AsyncOpenCleanup {
71 fn packet(&self) -> Option<&[u8]> {
72 match self {
73 Self::Acceptance { packet, .. } => packet.as_deref(),
74 Self::Adapter(failure) => failure.machine.cleanup_close_packet(),
75 }
76 }
77
78 fn into_primary(self) -> ClientError {
79 match self {
80 Self::Acceptance { primary, .. } => primary,
81 Self::Adapter(failure) => failure.primary,
82 }
83 }
84}
85
86#[derive(Debug)]
91pub(crate) struct AsyncOpenState {
92 attempt: usize,
93 deadline: Option<Instant>,
94 deadline_timer: Option<Pin<Box<time::Sleep>>>,
95 request_submitted: bool,
96 stop_after_current_attempt: bool,
97 stopped_after_current_attempt: bool,
98 buffer: [u8; MAX_OPEN_PACKET_SIZE],
99 cleanup: Option<AsyncOpenCleanup>,
100}
101
102impl AsyncOpenState {
103 pub(crate) fn new() -> Self {
104 Self {
105 attempt: 0,
106 deadline: None,
107 deadline_timer: None,
108 request_submitted: false,
109 stop_after_current_attempt: false,
110 stopped_after_current_attempt: false,
111 buffer: [0_u8; MAX_OPEN_PACKET_SIZE],
112 cleanup: None,
113 }
114 }
115
116 fn start_attempt(&mut self, timeout: Duration) -> Result<(), ClientError> {
117 let deadline = Instant::now()
118 .checked_add(timeout)
119 .ok_or(ClientError::DurationOverflow)?;
120 self.deadline = Some(deadline);
121 self.deadline_timer = Some(Box::pin(time::sleep_until(deadline.into())));
122 self.request_submitted = false;
123 Ok(())
124 }
125
126 fn finish_attempt(&mut self) {
127 self.attempt += 1;
128 self.deadline = None;
129 self.deadline_timer = None;
130 self.request_submitted = false;
131 self.stopped_after_current_attempt = self.stop_after_current_attempt;
132 }
133
134 pub(crate) fn has_in_flight_work(&self) -> bool {
135 self.request_submitted || self.cleanup.is_some()
136 }
137
138 pub(crate) fn request_stop_after_current_attempt(&mut self) {
139 self.stop_after_current_attempt = true;
140 }
141
142 pub(crate) fn stopped_after_current_attempt(&self) -> bool {
143 self.stopped_after_current_attempt
144 }
145
146 fn deadline(&self) -> Instant {
147 self.deadline
148 .expect("an open attempt has an active deadline")
149 }
150
151 fn deadline_timer(&mut self) -> &mut Pin<Box<time::Sleep>> {
152 self.deadline_timer
153 .as_mut()
154 .expect("an open attempt has an active deadline timer")
155 }
156}
157
158#[derive(Debug)]
189pub struct AsyncClient {
190 socket: tokio::net::UdpSocket,
191 machine: SessionMachine,
192 remote: SocketAddr,
193 recv_buffer: Vec<u8>,
194 applied_traffic_class: Option<u8>,
195 prepared_open: Option<PreparedOpenRequest>,
196 prepared_probe: Option<PreparedProbe>,
197 tx_timestamping_enabled: bool,
200}
201
202impl AsyncClient {
203 pub async fn connect(
214 endpoint: impl AsRef<str>,
215 config: ClientConfig,
216 ) -> Result<Self, ClientError> {
217 tokio::runtime::Handle::try_current().map_err(|_| ClientError::NoTokioRuntime)?;
218 config.open.validate()?;
219 let remote = resolve_remote_tokio(endpoint.as_ref(), config.address_family).await?;
220 let machine = SessionMachine::new(config.clone(), remote)?;
221 let prepared_open = machine.prepare_open_request()?;
222 let socket = connect_tokio_udp_socket(&config.socket, remote, config.address_family)?;
223
224 Ok(Self {
225 socket,
226 machine,
227 remote,
228 recv_buffer: vec![0_u8; recv_buffer_size(false, None)?],
229 applied_traffic_class: None,
230 prepared_open: Some(prepared_open),
231 prepared_probe: None,
232 tx_timestamping_enabled: false,
233 })
234 }
235
236 pub async fn open(&mut self) -> Result<OpenOutcome, ClientError> {
248 let mut state = AsyncOpenState::new();
249 poll_fn(|cx| self.poll_open(&mut state, cx)).await
250 }
251
252 pub async fn send_probe(&mut self) -> Result<SendReceipt, SendProbeError> {
264 poll_fn(|cx| self.poll_send_probe(cx)).await
265 }
266
267 pub async fn recv(&mut self) -> Result<Vec<ClientEvent>, ClientError> {
273 poll_fn(|cx| self.poll_recv(cx)).await
274 }
275
276 pub fn poll_timeouts(&mut self) -> Result<Vec<ClientEvent>, ClientError> {
278 self.poll_timeouts_at(Instant::now())
279 }
280
281 pub fn poll_timeouts_at(&mut self, now: Instant) -> Result<Vec<ClientEvent>, ClientError> {
283 self.drain_tx_timestamps()?;
284 self.machine.poll_timeouts_at(now)
285 }
286
287 pub(crate) fn poll_timeouts_bounded_at(
288 &mut self,
289 now: Instant,
290 limit: usize,
291 ) -> Result<TimeoutBatch, ClientError> {
292 self.drain_tx_timestamps()?;
293 self.machine.poll_timeouts_bounded_at(now, limit)
294 }
295
296 pub async fn close(&mut self) -> Result<Vec<ClientEvent>, ClientError> {
303 poll_fn(|cx| self.poll_close(cx)).await
304 }
305
306 pub(crate) fn poll_open(
307 &mut self,
308 state: &mut AsyncOpenState,
309 cx: &mut Context<'_>,
310 ) -> Poll<Result<OpenOutcome, ClientError>> {
311 if self.prepared_open.is_none() {
312 if let Err(error) = self.machine.prepare_open_request() {
313 return Poll::Ready(Err(error));
314 }
315 unreachable!("connected async clients retain an open request");
316 }
317
318 for _ in 0..OPEN_POLL_WORK_BUDGET {
319 if state.cleanup.is_some() {
320 return self.poll_open_cleanup(state, cx);
321 }
322
323 let attempt_count = self.machine.config().open.timeouts.len();
324 if state.attempt >= attempt_count {
325 return Poll::Ready(Err(ClientError::OpenTimeout));
326 }
327 if state.stop_after_current_attempt && state.deadline.is_none() {
328 state.stopped_after_current_attempt = true;
329 return Poll::Ready(Err(ClientError::OpenTimeout));
330 }
331 if state.deadline.is_none() {
332 let timeout = self.machine.config().open.timeouts[state.attempt];
333 if let Err(error) = state.start_attempt(timeout) {
334 return Poll::Ready(Err(error));
335 }
336 }
337
338 if !state.request_submitted {
339 match poll_writable_until(&self.socket, state.deadline_timer(), cx) {
340 Poll::Pending => return Poll::Pending,
341 Poll::Ready(Err(error)) => return Poll::Ready(Err(error)),
342 Poll::Ready(Ok(false)) => {
343 state.finish_attempt();
344 continue;
345 }
346 Poll::Ready(Ok(true)) => {}
347 }
348
349 let request = self
350 .prepared_open
351 .as_ref()
352 .expect("connected clients retain their prepared open request");
353 let send_result = self.socket.try_send(&request.bytes);
354 match send_result {
355 Ok(bytes) => {
356 state.request_submitted = true;
357 if let Err(error) = validate_datagram_length(request.bytes.len(), bytes) {
358 return Poll::Ready(Err(error));
359 }
360 }
361 Err(error) if error.kind() == io::ErrorKind::WouldBlock => continue,
362 Err(error) => return Poll::Ready(Err(ClientError::Socket(error))),
363 }
364 }
365
366 match poll_readable_until(&self.socket, state.deadline_timer(), cx) {
367 Poll::Pending => return Poll::Pending,
368 Poll::Ready(Err(error)) => return Poll::Ready(Err(error)),
369 Poll::Ready(Ok(false)) => {
370 state.finish_attempt();
371 if state.stopped_after_current_attempt() {
372 return Poll::Ready(Err(ClientError::OpenTimeout));
373 }
374 continue;
375 }
376 Poll::Ready(Ok(true)) => {}
377 }
378
379 let datagram = match try_recv_tokio_datagram(&self.socket, &mut state.buffer) {
380 Ok(datagram) => datagram,
381 Err(error)
388 if matches!(
389 error.kind(),
390 io::ErrorKind::WouldBlock | io::ErrorKind::Interrupted
391 ) =>
392 {
393 continue
394 }
395 Err(error) => return Poll::Ready(Err(ClientError::Socket(error))),
396 };
397 if datagram.received_at.mono > state.deadline() {
398 state.finish_attempt();
399 if state.stopped_after_current_attempt() {
400 return Poll::Ready(Err(ClientError::OpenTimeout));
401 }
402 continue;
403 }
404
405 let reply = match self
406 .machine
407 .inspect_open_datagram(&state.buffer[..datagram.len])
408 {
409 Ok(OpenDatagramDisposition::Ignore) => continue,
410 Ok(OpenDatagramDisposition::Trusted(reply)) => reply,
411 Err(error) => return Poll::Ready(Err(error)),
412 };
413 let machine = match self
414 .machine
415 .prepare_open_acceptance(reply, datagram.received_at)
416 {
417 Ok(machine) => machine,
418 Err(failure) => {
419 state.cleanup = Some(AsyncOpenCleanup::Acceptance {
420 primary: failure.primary,
421 packet: failure.cleanup_close,
422 });
423 continue;
424 }
425 };
426 let prepared = match self.prepare_async_open(machine) {
427 Ok(prepared) => prepared,
428 Err(failure) => {
429 state.cleanup = Some(AsyncOpenCleanup::Adapter(failure));
430 continue;
431 }
432 };
433 return Poll::Ready(Ok(self.commit_async_open(prepared)));
434 }
435
436 cx.waker().wake_by_ref();
439 Poll::Pending
440 }
441
442 pub(crate) fn poll_send_probe(
443 &mut self,
444 cx: &mut Context<'_>,
445 ) -> Poll<Result<SendReceipt, SendProbeError>> {
446 if let Err(error) = self.machine.ensure_open() {
447 return Poll::Ready(Err(SendProbeError::NotCommitted(error)));
448 }
449 if self.prepared_probe.is_none() {
450 match self.machine.prepare_probe() {
451 Ok(prepared) => self.prepared_probe = Some(prepared),
452 Err(error) => return Poll::Ready(Err(SendProbeError::NotCommitted(error))),
453 }
454 }
455 loop {
456 match self.socket.poll_send_ready(cx) {
457 Poll::Pending => return Poll::Pending,
458 Poll::Ready(Ok(())) => {}
459 Poll::Ready(Err(error)) => {
460 return Poll::Ready(Err(SendProbeError::NotCommitted(ClientError::Socket(
461 error,
462 ))));
463 }
464 }
465
466 let prepared = self
467 .prepared_probe
468 .as_ref()
469 .expect("prepared probe was retained across readiness");
470 let machine_preflight = match self.machine.preflight_probe_commit(prepared) {
471 Ok(preflight) => preflight,
472 Err(error) => return Poll::Ready(Err(SendProbeError::NotCommitted(error))),
473 };
474 let expected_bytes = prepared.bytes.len();
475
476 let send_anchor = ClientTimestamp::now();
482 let machine_commit = match self
483 .machine
484 .finalize_probe_commit(machine_preflight, send_anchor)
485 {
486 Ok(commit) => commit,
487 Err(error) => return Poll::Ready(Err(SendProbeError::NotCommitted(error))),
488 };
489 let send_call_start = Instant::now();
490 let send_result = self.socket.try_send(&prepared.bytes);
491 let bytes = match send_result {
492 Ok(bytes) => bytes,
493 Err(error) if error.kind() == io::ErrorKind::WouldBlock => {
496 self.machine.invalidate_kernel_tx_correlation();
497 continue;
498 }
499 Err(error) => {
500 self.machine.invalidate_kernel_tx_correlation();
501 return Poll::Ready(Err(SendProbeError::NotCommitted(ClientError::Socket(
502 error,
503 ))));
504 }
505 };
506 let send_finished_at = Instant::now();
507
508 let sent_at = ClientTimestamp::now();
512 let sent = self
513 .machine
514 .commit_probe_sent(machine_commit, sent_at, bytes);
515 self.prepared_probe = None;
516 let send_call = send_finished_at.saturating_duration_since(send_call_start);
517
518 let receipt = SendReceipt {
519 seq: sent.seq,
520 remote: self.remote,
521 sent_at: sent.sent_at,
522 bytes: sent.bytes,
523 send_call,
524 };
525 if let Err(source) = validate_datagram_length(expected_bytes, bytes) {
526 return Poll::Ready(Err(SendProbeError::AfterCommit {
527 receipt,
528 source: Box::new(source),
529 }));
530 }
531 if let Err(source) = self.drain_tx_timestamps() {
532 return Poll::Ready(Err(SendProbeError::AfterCommit {
533 receipt,
534 source: Box::new(source),
535 }));
536 }
537 return Poll::Ready(Ok(receipt));
538 }
539 }
540
541 pub(crate) fn poll_recv(
542 &mut self,
543 cx: &mut Context<'_>,
544 ) -> Poll<Result<Vec<ClientEvent>, ClientError>> {
545 if let Err(error) = self.machine.ensure_open() {
546 return Poll::Ready(Err(error));
547 }
548
549 for _ in 0..RECV_POLL_WORK_BUDGET {
550 match self.socket.poll_recv_ready(cx) {
551 Poll::Pending => return Poll::Pending,
552 Poll::Ready(Ok(())) => {}
553 Poll::Ready(Err(error)) => {
554 return Poll::Ready(Err(ClientError::Socket(error)));
555 }
556 }
557
558 let datagram = match try_recv_tokio_datagram(&self.socket, &mut self.recv_buffer) {
559 Ok(datagram) => datagram,
560 Err(error)
564 if matches!(
565 error.kind(),
566 io::ErrorKind::WouldBlock | io::ErrorKind::Interrupted
567 ) =>
568 {
569 continue
570 }
571 Err(error) => return Poll::Ready(Err(ClientError::Socket(error))),
572 };
573
574 if let Err(error) = self.drain_tx_timestamps() {
579 return Poll::Ready(Err(error));
580 }
581
582 let events = match self.machine.process_received_echo_packet(
583 &self.recv_buffer[..datagram.len],
584 datagram.received_at,
585 datagram.meta,
586 ) {
587 Ok(events) => events,
588 Err(error) => return Poll::Ready(Err(error)),
589 };
590 if self.machine.is_peer_closed() {
591 self.prepared_probe = None;
592 if clear_dscp_on_tokio_socket(&self.socket, self.remote).is_ok() {
593 self.applied_traffic_class = None;
594 }
595 }
596 return Poll::Ready(Ok(events));
597 }
598
599 cx.waker().wake_by_ref();
603 Poll::Pending
604 }
605
606 pub(crate) fn poll_close(
607 &mut self,
608 cx: &mut Context<'_>,
609 ) -> Poll<Result<Vec<ClientEvent>, ClientError>> {
610 loop {
611 let prepared = match self.machine.prepare_close() {
612 Ok(prepared) => prepared,
613 Err(error) => return Poll::Ready(Err(error)),
614 };
615 let mut events = Vec::new();
616 if let Err(source) = events.try_reserve(1) {
617 return Poll::Ready(Err(ClientError::AllocationFailed {
618 operation: "close event result",
619 source,
620 }));
621 }
622 let previous_traffic_class = self.applied_traffic_class;
623 let expected_bytes = prepared.bytes.len();
624
625 match self.socket.poll_send_ready(cx) {
626 Poll::Pending => return Poll::Pending,
627 Poll::Ready(Ok(())) => {}
628 Poll::Ready(Err(error)) => {
629 return Poll::Ready(Err(ClientError::Socket(error)));
630 }
631 }
632 if let Err(error) = clear_dscp_on_tokio_socket(&self.socket, self.remote) {
633 return Poll::Ready(Err(error));
634 }
635 let mut rollback =
636 DscpRollback::armed(&self.socket, self.remote, previous_traffic_class);
637 let send_result = self.socket.try_send(prepared.bytes);
638 let bytes = match send_result {
639 Ok(bytes) => bytes,
640 Err(error) if error.kind() == io::ErrorKind::WouldBlock => {
641 if let Err(error) = rollback.restore() {
642 return Poll::Ready(Err(error));
643 }
644
645 continue;
646 }
647 Err(error) => return Poll::Ready(Err(ClientError::Socket(error))),
648 };
649
650 let close_sent_at = ClientTimestamp::now();
651
652 let event = self
653 .machine
654 .commit_local_close(prepared.commit, close_sent_at);
655 rollback.disarm();
656 self.prepared_probe = None;
657 self.applied_traffic_class = None;
658
659 if let Err(error) = validate_datagram_length(expected_bytes, bytes) {
660 return Poll::Ready(Err(error));
661 }
662 events.push(event);
663 return Poll::Ready(Ok(events));
664 }
665 }
666
667 pub fn probe_timeout(&self) -> Duration {
669 self.machine.probe_timeout()
670 }
671
672 pub fn negotiation(&self) -> Option<&crate::NegotiationResult> {
676 self.machine.negotiation()
677 }
678
679 pub fn has_pending_probes(&self) -> bool {
681 !self.machine.pending_is_empty()
682 }
683
684 pub fn is_peer_closed(&self) -> bool {
686 self.machine.is_peer_closed()
687 }
688
689 pub(crate) fn remote_addr(&self) -> SocketAddr {
690 self.remote
691 }
692
693 pub(crate) fn packets_sent(&self) -> u64 {
694 self.machine.packets_sent()
695 }
696
697 pub fn next_probe_timeout_deadline(&self) -> Option<Instant> {
699 self.machine.next_probe_timeout_deadline()
700 }
701
702 pub(crate) fn latest_probe_timeout_deadline(&self) -> Option<Instant> {
703 self.machine.latest_probe_timeout_deadline()
704 }
705
706 pub(crate) fn discard_prepared_probe(&mut self) {
707 self.prepared_probe = None;
708 }
709
710 fn prepare_async_open(
711 &mut self,
712 machine: PreparedOpenAcceptance,
713 ) -> Result<PreparedAsyncOpen, Box<PreparedAsyncOpenFailure>> {
714 let Some(negotiated) = machine.normal_negotiated() else {
715 return Ok(PreparedAsyncOpen {
716 machine,
717 recv_buffer_len: None,
718 negotiated_traffic_class: None,
719 });
720 };
721 let recv_buffer_len = match recv_buffer_size(self.machine.has_hmac(), Some(negotiated)) {
722 Ok(size) => size,
723 Err(primary) => {
724 return Err(Box::new(PreparedAsyncOpenFailure { primary, machine }));
725 }
726 };
727 let negotiated_traffic_class = negotiated.accepted.dscp << 2;
728 let additional = recv_buffer_len.saturating_sub(self.recv_buffer.len());
729 if let Err(source) = self.recv_buffer.try_reserve(additional) {
730 return Err(Box::new(PreparedAsyncOpenFailure {
731 primary: ClientError::AllocationFailed {
732 operation: "negotiated receive buffer",
733 source,
734 },
735 machine,
736 }));
737 }
738
739 let dscp_result = apply_traffic_class_to_tokio_socket(
740 &self.socket,
741 self.remote,
742 negotiated_traffic_class,
743 );
744 if let Err(primary) = dscp_result {
745 self.restore_dscp_best_effort(self.applied_traffic_class);
746 return Err(Box::new(PreparedAsyncOpenFailure { primary, machine }));
747 }
748
749 Ok(PreparedAsyncOpen {
750 machine,
751 recv_buffer_len: Some(recv_buffer_len),
752 negotiated_traffic_class: Some(negotiated_traffic_class),
753 })
754 }
755
756 fn commit_async_open(&mut self, prepared: PreparedAsyncOpen) -> OpenOutcome {
757 if let Some(recv_buffer_len) = prepared.recv_buffer_len {
758 self.recv_buffer.resize(recv_buffer_len, 0);
759 }
760 let outcome = self.machine.commit_open(prepared.machine);
761
762 self.applied_traffic_class = prepared.negotiated_traffic_class;
763 self.prepared_open = None;
764 self.tx_timestamping_enabled = try_enable_tx_timestamping(&self.socket);
765 outcome
766 }
767
768 fn drain_tx_timestamps(&mut self) -> Result<(), ClientError> {
771 if !self.tx_timestamping_enabled {
772 return Ok(());
773 }
774 let machine = &mut self.machine;
775 drain_tx_timestamps(&self.socket, |id, timestamp| {
776 machine.record_kernel_tx_timestamp(id, timestamp);
777 })
778 .map_err(ClientError::Socket)
779 }
780
781 fn poll_open_cleanup(
782 &self,
783 state: &mut AsyncOpenState,
784 cx: &mut Context<'_>,
785 ) -> Poll<Result<OpenOutcome, ClientError>> {
786 let Some(packet) = state
787 .cleanup
788 .as_ref()
789 .expect("cleanup polling requires a retained primary error")
790 .packet()
791 else {
792 return Poll::Ready(Err(state.cleanup.take().unwrap().into_primary()));
793 };
794 let send_result = self.socket.try_send(packet);
795 match send_result {
796 Ok(_) => return Poll::Ready(Err(state.cleanup.take().unwrap().into_primary())),
797 Err(error) if error.kind() == io::ErrorKind::WouldBlock => {}
798 Err(_) => return Poll::Ready(Err(state.cleanup.take().unwrap().into_primary())),
799 }
800
801 match poll_writable_until(&self.socket, state.deadline_timer(), cx) {
802 Poll::Pending => Poll::Pending,
803 Poll::Ready(Ok(true)) => {
804 cx.waker().wake_by_ref();
807 Poll::Pending
808 }
809 Poll::Ready(Ok(false) | Err(_)) => {
810 Poll::Ready(Err(state.cleanup.take().unwrap().into_primary()))
811 }
812 }
813 }
814
815 fn restore_dscp_best_effort(&self, previous_traffic_class: Option<u8>) {
816 let _ = match previous_traffic_class {
817 Some(traffic_class) => {
818 apply_traffic_class_to_tokio_socket(&self.socket, self.remote, traffic_class)
819 }
820 None => clear_dscp_on_tokio_socket(&self.socket, self.remote),
821 };
822 }
823}
824
825struct DscpRollback<'a> {
826 socket: &'a tokio::net::UdpSocket,
827 remote: SocketAddr,
828 previous_traffic_class: Option<u8>,
829 armed: bool,
830}
831
832impl<'a> DscpRollback<'a> {
833 fn armed(
834 socket: &'a tokio::net::UdpSocket,
835 remote: SocketAddr,
836 previous_traffic_class: Option<u8>,
837 ) -> Self {
838 Self {
839 socket,
840 remote,
841 previous_traffic_class,
842 armed: true,
843 }
844 }
845
846 fn restore(&mut self) -> Result<(), ClientError> {
847 if !self.armed {
848 return Ok(());
849 }
850
851 match self.previous_traffic_class {
852 Some(traffic_class) => {
853 apply_traffic_class_to_tokio_socket(self.socket, self.remote, traffic_class)?
854 }
855 None => clear_dscp_on_tokio_socket(self.socket, self.remote)?,
856 }
857 self.armed = false;
858 Ok(())
859 }
860
861 fn disarm(&mut self) {
862 self.armed = false;
863 }
864}
865
866impl Drop for DscpRollback<'_> {
867 fn drop(&mut self) {
868 if self.armed {
869 let _ = self.restore();
870 }
871 }
872}
873
874fn poll_writable_until(
875 socket: &tokio::net::UdpSocket,
876 deadline: &mut Pin<Box<time::Sleep>>,
877 cx: &mut Context<'_>,
878) -> Poll<Result<bool, ClientError>> {
879 match socket.poll_send_ready(cx) {
880 Poll::Ready(Ok(())) => Poll::Ready(Ok(true)),
881 Poll::Ready(Err(error)) => Poll::Ready(Err(ClientError::Socket(error))),
882 Poll::Pending => match deadline.as_mut().poll(cx) {
883 Poll::Ready(()) => Poll::Ready(Ok(false)),
884 Poll::Pending => Poll::Pending,
885 },
886 }
887}
888
889fn poll_readable_until(
890 socket: &tokio::net::UdpSocket,
891 deadline: &mut Pin<Box<time::Sleep>>,
892 cx: &mut Context<'_>,
893) -> Poll<Result<bool, ClientError>> {
894 match socket.poll_recv_ready(cx) {
895 Poll::Ready(Ok(())) => Poll::Ready(Ok(true)),
896 Poll::Ready(Err(error)) => Poll::Ready(Err(ClientError::Socket(error))),
897 Poll::Pending => match deadline.as_mut().poll(cx) {
898 Poll::Ready(()) => Poll::Ready(Ok(false)),
899 Poll::Pending => Poll::Pending,
900 },
901 }
902}