1use socket2::{Domain, SockAddr, SockRef, Socket, Type};
5use std::{
6 collections::{HashMap, HashSet},
7 net::{IpAddr, SocketAddr},
8 os::fd::{AsFd, FromRawFd},
9 sync::Arc,
10 time::Duration,
11};
12use tokio::io::{AsyncRead, AsyncWrite};
13use tokio::time::Instant;
14use tokio_rustls::TlsAcceptor;
15
16const TOMBSTONE_TTL: Duration = Duration::from_secs(5);
21
22const HANDSHAKE_CANCEL_CLEANUP_TIMEOUT: Duration = Duration::from_secs(1);
24
25use bytes::Bytes;
26use derive_builder::Builder;
27use futures::{SinkExt, StreamExt};
28use parking_lot::Mutex;
29
30use serde::{Deserialize, Serialize};
31use tokio::{
32 io::AsyncWriteExt,
33 sync::{mpsc, oneshot},
34 time,
35};
36use tokio_util::codec::{FramedRead, FramedWrite};
37
38use super::{
39 CallHomeHandshake, ControlMessage, PendingConnections, RegisteredStream, StreamOptions,
40 StreamReceiver, StreamSender, TcpStreamConnectionInfo, TwoPartCodec, bind_listener,
41};
42use crate::discovery::EndpointInstanceId;
43use crate::engine::AsyncEngineContext;
44use crate::error::{DynamoError, ErrorType};
45use crate::pipeline::{
46 PipelineError,
47 network::{
48 ResponseService, ResponseStreamPrologue, StreamPrologueError,
49 codec::{TwoPartMessage, TwoPartMessageType},
50 tcp::StreamType,
51 },
52};
53use crate::utils::ip_resolver::{resolve_host_or_interface, resolve_local_host};
54use anyhow::{Result, anyhow as error};
55
56pub use crate::utils::ip_resolver::{DefaultIpResolver, IpResolver};
57
58#[allow(dead_code)]
59type ResponseType = TwoPartMessage;
60
61#[derive(Debug, Serialize, Deserialize, Clone, Builder, Default)]
62pub struct ServerOptions {
63 #[builder(default = "0")]
64 pub port: u16,
65
66 #[builder(default)]
70 pub interface: Option<String>,
71}
72
73impl ServerOptions {
74 pub fn builder() -> ServerOptionsBuilder {
75 ServerOptionsBuilder::default()
76 }
77}
78
79pub struct TcpStreamServer {
83 address: String,
84 state: Arc<Mutex<State>>,
85}
86
87#[allow(dead_code)]
94struct RequestedSendConnection {
95 context: Arc<dyn AsyncEngineContext>,
96 connection: oneshot::Sender<Result<StreamSender, StreamPrologueError>>,
97 send_buffer_count: usize,
100}
101
102struct RequestedRecvConnection {
103 context: Arc<dyn AsyncEngineContext>,
104 connection: oneshot::Sender<Result<StreamReceiver, StreamPrologueError>>,
105 cancellation: tokio_util::sync::CancellationToken,
108 send_buffer_count: usize,
111 defer_cancellation_until_prologue: bool,
112}
113
114fn data_plane_channel<T>(send_buffer_count: usize) -> (mpsc::Sender<T>, mpsc::Receiver<T>) {
120 mpsc::channel(send_buffer_count.max(1))
125}
126
127#[derive(Default)]
144struct State {
145 tx_subjects: HashMap<String, RequestedSendConnection>,
146 rx_subjects: HashMap<String, RequestedRecvConnection>,
147 rx_cancellations: HashMap<String, tokio_util::sync::CancellationToken>,
150 subject_instance: HashMap<String, EndpointInstanceId>,
153 instance_subjects: HashMap<EndpointInstanceId, HashSet<(StreamType, String)>>,
158 removed_instances: HashMap<EndpointInstanceId, Instant>,
162 handle: Option<tokio::task::JoinHandle<Result<()>>>,
163}
164
165struct ResponseRegistrationGuard {
166 state: Arc<Mutex<State>>,
167 subject: String,
168 is_active: bool,
169}
170
171impl ResponseRegistrationGuard {
172 fn accept(mut self) -> bool {
175 self.is_active = false;
176 TcpStreamServer::finish_response_stream(&self.state, &self.subject)
177 }
178}
179
180impl Drop for ResponseRegistrationGuard {
181 fn drop(&mut self) {
182 if self.is_active {
183 TcpStreamServer::finish_response_stream(&self.state, &self.subject);
184 }
185 }
186}
187
188fn prune_tombstones(tombstones: &mut HashMap<EndpointInstanceId, Instant>, now: Instant) {
191 tombstones.retain(|_, ts| now.saturating_duration_since(*ts) < TOMBSTONE_TTL);
192}
193
194impl TcpStreamServer {
195 pub fn local_address(&self) -> Result<SocketAddr> {
196 Ok(self.address.parse()?)
197 }
198
199 pub fn options_builder() -> ServerOptionsBuilder {
200 ServerOptionsBuilder::default()
201 }
202
203 pub async fn new(options: ServerOptions) -> Result<Arc<Self>, PipelineError> {
204 Self::new_with_resolver(options, DefaultIpResolver).await
205 }
206
207 pub async fn new_with_resolver<R: IpResolver>(
208 options: ServerOptions,
209 resolver: R,
210 ) -> Result<Arc<Self>, PipelineError> {
211 let resolved_host = if let Some(host_or_interface) = options.interface.as_deref() {
212 resolve_host_or_interface(host_or_interface, &resolver).map_err(|error| {
213 PipelineError::Generic(format!(
214 "Failed to resolve configured TCP host '{host_or_interface}': {error}"
215 ))
216 })?
217 } else {
218 resolve_local_host(&resolver).map_err(|error| {
219 PipelineError::Generic(format!("Failed to resolve local IP address: {error}"))
220 })?
221 };
222
223 if resolved_host.used_loopback_fallback() {
224 tracing::warn!(
225 bind_ip = %resolved_host.bind_ip(),
226 advertise_ip = %resolved_host.advertise_ip(),
227 "No usable local IP address found; advertising loopback"
228 );
229 }
230
231 let state = Arc::new(Mutex::new(State::default()));
232
233 let tls_acceptor = Self::build_tls_acceptor().map_err(|e| {
235 PipelineError::Generic(format!("Failed to build TCP TLS acceptor: {}", e))
236 })?;
237
238 let bind_address = SocketAddr::new(resolved_host.bind_ip(), options.port);
239 let local_address = Self::start(bind_address, state.clone(), tls_acceptor)
240 .await
241 .map_err(|error| {
242 PipelineError::Generic(format!("Failed to start TcpStreamServer: {error}"))
243 })?;
244 let advertised_address =
245 SocketAddr::new(resolved_host.advertise_ip(), local_address.port());
246 let address = advertised_address.to_string();
247
248 tracing::debug!(%local_address, %advertised_address, "tcp transport service started");
249
250 Ok(Arc::new(Self { address, state }))
251 }
252
253 pub async fn associate_instance(
266 &self,
267 recv_subject: &str,
268 send_subject: Option<&str>,
269 id: &EndpointInstanceId,
270 ) -> bool {
271 let mut state = self.state.lock();
272 let now = Instant::now();
273 prune_tombstones(&mut state.removed_instances, now);
274 if state.removed_instances.contains_key(id) {
275 tracing::warn!(
277 recv_subject,
278 send_subject,
279 namespace = %id.namespace,
280 component = %id.component,
281 endpoint = %id.endpoint,
282 instance_id = id.instance_id,
283 "Cancelling subject immediately: instance already removed (tombstoned)"
284 );
285 state.rx_subjects.remove(recv_subject);
286 if let Some(token) = state.rx_cancellations.remove(recv_subject) {
287 token.cancel();
288 }
289 if let Some(s) = send_subject {
290 state.tx_subjects.remove(s);
291 }
292 return false;
293 }
294 state
295 .subject_instance
296 .insert(recv_subject.to_string(), id.clone());
297 if let Some(s) = send_subject {
298 state.subject_instance.insert(s.to_string(), id.clone());
299 }
300 let entry = state.instance_subjects.entry(id.clone()).or_default();
301 entry.insert((StreamType::Response, recv_subject.to_string()));
302 if let Some(s) = send_subject {
303 entry.insert((StreamType::Request, s.to_string()));
304 }
305 true
306 }
307
308 pub async fn associate_request_instance(&self, subject: &str, id: &EndpointInstanceId) -> bool {
311 let mut state = self.state.lock();
312 let now = Instant::now();
313 prune_tombstones(&mut state.removed_instances, now);
314 if state.removed_instances.contains_key(id) {
315 state.tx_subjects.remove(subject);
316 return false;
317 }
318 state
319 .subject_instance
320 .insert(subject.to_string(), id.clone());
321 state
322 .instance_subjects
323 .entry(id.clone())
324 .or_default()
325 .insert((StreamType::Request, subject.to_string()));
326 true
327 }
328
329 pub async fn cancel_recv_stream(&self, subject: &str) {
332 let mut state = self.state.lock();
333 state.rx_subjects.remove(subject);
334 if let Some(token) = state.rx_cancellations.remove(subject) {
335 token.cancel();
336 }
337 if let Some(key) = state.subject_instance.remove(subject)
338 && let Some(subjects) = state.instance_subjects.get_mut(&key)
339 {
340 subjects.remove(&(StreamType::Response, subject.to_string()));
341 if subjects.is_empty() {
342 state.instance_subjects.remove(&key);
343 }
344 }
345 }
346
347 pub async fn cancel_send_stream(&self, subject: &str) {
353 let mut state = self.state.lock();
354 state.tx_subjects.remove(subject);
355 if let Some(key) = state.subject_instance.remove(subject)
356 && let Some(subjects) = state.instance_subjects.get_mut(&key)
357 {
358 subjects.remove(&(StreamType::Request, subject.to_string()));
359 if subjects.is_empty() {
360 state.instance_subjects.remove(&key);
361 }
362 }
363 }
364
365 pub async fn cancel_instance_streams(&self, id: &EndpointInstanceId) -> usize {
370 let mut state = self.state.lock();
371 let now = Instant::now();
372 prune_tombstones(&mut state.removed_instances, now);
373 state.removed_instances.insert(id.clone(), now);
374 let subjects = match state.instance_subjects.remove(id) {
375 Some(subjects) => subjects,
376 None => return 0,
377 };
378 let count = subjects.len();
379 for (kind, subject) in &subjects {
380 match kind {
381 StreamType::Response => {
382 state.rx_subjects.remove(subject);
383 if let Some(token) = state.rx_cancellations.remove(subject) {
384 token.cancel();
385 }
386 }
387 StreamType::Request => {
388 state.tx_subjects.remove(subject);
389 }
390 }
391 state.subject_instance.remove(subject);
392 }
393 count
394 }
395
396 pub async fn clear_instance_tombstone(&self, id: &EndpointInstanceId) {
399 let mut state = self.state.lock();
400 state.removed_instances.remove(id);
401 }
402
403 fn build_tls_acceptor() -> anyhow::Result<Option<TlsAcceptor>> {
408 use crate::config::environment_names::tcp_response_stream::tls as env;
409 let cert_path = std::env::var(env::DYN_TCP_TLS_CERT_PATH).ok();
410 let key_path = std::env::var(env::DYN_TCP_TLS_KEY_PATH).ok();
411 let client_ca = std::env::var(env::DYN_TCP_TLS_CLIENT_CA_CERT_PATH).ok();
412 Ok(crate::tls_utils::server_tls_acceptor_config(
413 "TCP server",
414 cert_path.as_deref().map(std::path::Path::new),
415 key_path.as_deref().map(std::path::Path::new),
416 client_ca.as_deref().map(std::path::Path::new),
417 )?
418 .map(|config| TlsAcceptor::from(Arc::new(config))))
419 }
420
421 async fn start(
422 bind_address: SocketAddr,
423 state: Arc<Mutex<State>>,
424 tls_acceptor: Option<TlsAcceptor>,
425 ) -> Result<SocketAddr> {
426 let state_clone = state.clone();
427 let (ready_tx, ready_rx) = tokio::sync::oneshot::channel::<Result<SocketAddr>>();
428 {
429 let mut guard = state.lock();
430 if guard.handle.is_some() {
431 panic!("TcpStreamServer already started");
432 }
433 guard.handle = Some(tokio::spawn(tcp_listener(
434 bind_address,
435 state_clone,
436 tls_acceptor,
437 ready_tx,
438 )));
439 }
440 ready_rx.await?
441 }
442
443 fn insert_request_stream(&self, subject: String, connection: RequestedSendConnection) {
444 self.state.lock().tx_subjects.insert(subject, connection);
445 }
446
447 fn insert_response_stream(&self, subject: String, connection: RequestedRecvConnection) {
448 let cancellation = connection.cancellation.clone();
449 let mut state = self.state.lock();
450 state.rx_subjects.insert(subject.clone(), connection);
451 state.rx_cancellations.insert(subject, cancellation);
452 }
453
454 fn take_request_stream(state: &Mutex<State>, subject: &str) -> Option<RequestedSendConnection> {
455 let mut state = state.lock();
456 let connection = state.tx_subjects.remove(subject);
457 if let Some(key) = state.subject_instance.remove(subject)
458 && let Some(subjects) = state.instance_subjects.get_mut(&key)
459 {
460 subjects.remove(&(StreamType::Request, subject.to_string()));
461 if subjects.is_empty() {
462 state.instance_subjects.remove(&key);
463 }
464 }
465 connection
466 }
467
468 fn take_response_stream(
469 state: &Mutex<State>,
470 subject: &str,
471 ) -> Option<RequestedRecvConnection> {
472 state.lock().rx_subjects.remove(subject)
473 }
474
475 fn finish_response_stream(state: &Mutex<State>, subject: &str) -> bool {
479 let mut state = state.lock();
480 let is_pending = state.rx_cancellations.remove(subject).is_some();
481 if let Some(key) = state.subject_instance.remove(subject)
482 && let Some(subjects) = state.instance_subjects.get_mut(&key)
483 {
484 subjects.remove(&(StreamType::Response, subject.to_string()));
485 if subjects.is_empty() {
486 state.instance_subjects.remove(&key);
487 }
488 }
489 is_pending
490 }
491}
492
493#[async_trait::async_trait]
495impl ResponseService for TcpStreamServer {
496 async fn register(&self, options: StreamOptions) -> PendingConnections {
517 let address = self.address.clone();
520 tracing::debug!("Registering new TcpStream on {address}");
521
522 let send_stream = if options.enable_request_stream {
523 let sender_subject = uuid::Uuid::new_v4().to_string();
524 let registry_subject = sender_subject.clone();
525
526 let (pending_sender_tx, pending_sender_rx) = oneshot::channel();
527
528 let connection_info = RequestedSendConnection {
529 context: options.context.clone(),
530 connection: pending_sender_tx,
531 send_buffer_count: options.send_buffer_count,
532 };
533
534 let cleanup_subject = sender_subject.clone();
535 let cleanup_state = self.state.clone();
536 let registered_stream = RegisteredStream::new(
537 TcpStreamConnectionInfo {
538 address: address.clone(),
539 subject: sender_subject,
540 context: options.context.id().to_string(),
541 stream_type: StreamType::Request,
542 }
543 .into(),
544 pending_sender_rx,
545 )
546 .with_cleanup(move || {
547 tokio::spawn(async move {
549 let mut state = cleanup_state.lock();
550 state.tx_subjects.remove(&cleanup_subject);
551 if let Some(key) = state.subject_instance.remove(&cleanup_subject)
552 && let Some(subjects) = state.instance_subjects.get_mut(&key)
553 {
554 subjects.remove(&(StreamType::Request, cleanup_subject.clone()));
555 if subjects.is_empty() {
556 state.instance_subjects.remove(&key);
557 }
558 }
559 });
560 });
561
562 self.insert_request_stream(registry_subject, connection_info);
563
564 Some(registered_stream)
565 } else {
566 None
567 };
568
569 let recv_stream = if options.enable_response_stream {
570 let (pending_recver_tx, pending_recver_rx) = oneshot::channel();
571 let receiver_subject = uuid::Uuid::new_v4().to_string();
572 let registry_subject = receiver_subject.clone();
573
574 let connection_info = RequestedRecvConnection {
575 context: options.context.clone(),
576 connection: pending_recver_tx,
577 cancellation: tokio_util::sync::CancellationToken::new(),
578 send_buffer_count: options.send_buffer_count,
579 defer_cancellation_until_prologue: options.defer_cancellation_until_prologue,
580 };
581
582 let cleanup_subject = receiver_subject.clone();
583 let cleanup_state = self.state.clone();
584 let registered_stream = RegisteredStream::new(
585 TcpStreamConnectionInfo {
586 address: address.clone(),
587 subject: receiver_subject,
588 context: options.context.id().to_string(),
589 stream_type: StreamType::Response,
590 }
591 .into(),
592 pending_recver_rx,
593 )
594 .with_cleanup(move || {
595 tokio::spawn(async move {
597 let mut state = cleanup_state.lock();
598 state.rx_subjects.remove(&cleanup_subject);
599 if let Some(token) = state.rx_cancellations.remove(&cleanup_subject) {
600 token.cancel();
601 }
602 if let Some(key) = state.subject_instance.remove(&cleanup_subject)
603 && let Some(subjects) = state.instance_subjects.get_mut(&key)
604 {
605 subjects.remove(&(StreamType::Response, cleanup_subject.clone()));
606 if subjects.is_empty() {
607 state.instance_subjects.remove(&key);
608 }
609 }
610 });
611 });
612
613 self.insert_response_stream(registry_subject, connection_info);
614
615 Some(registered_stream)
616 } else {
617 None
618 };
619
620 PendingConnections {
621 send_stream,
622 recv_stream,
623 }
624 }
625}
626
627const ACCEPT_BACKOFF_INITIAL_DELAY: Duration = Duration::from_millis(5);
629const ACCEPT_BACKOFF_MAX_DELAY: Duration = Duration::from_secs(1);
632const ACCEPT_BACKOFF_LOG_INTERVAL: Duration = Duration::from_secs(5);
634
635#[derive(Debug, Clone, Copy, PartialEq, Eq)]
637enum AcceptFailure {
638 Exhaustion,
642 Ordinary,
645}
646
647#[derive(Debug, Clone, Copy, PartialEq, Eq)]
649struct AcceptBackoffAction {
650 delay: Duration,
652 log_suppressed: Option<u64>,
656}
657
658#[derive(Debug)]
665struct AcceptBackoff {
666 initial_delay: Duration,
667 max_delay: Duration,
668 log_interval: Duration,
669 current_delay: Duration,
670 suppressed: u64,
671 last_log_at: Option<std::time::Instant>,
672 in_backoff: bool,
676}
677
678impl Default for AcceptBackoff {
679 fn default() -> Self {
680 Self {
681 initial_delay: ACCEPT_BACKOFF_INITIAL_DELAY,
682 max_delay: ACCEPT_BACKOFF_MAX_DELAY,
683 log_interval: ACCEPT_BACKOFF_LOG_INTERVAL,
684 current_delay: ACCEPT_BACKOFF_INITIAL_DELAY,
685 suppressed: 0,
686 last_log_at: None,
687 in_backoff: false,
688 }
689 }
690}
691
692impl AcceptBackoff {
693 fn classify(err: &std::io::Error) -> AcceptFailure {
697 #[cfg(unix)]
698 {
699 if matches!(
705 err.raw_os_error(),
706 Some(libc::EMFILE) | Some(libc::ENFILE) | Some(libc::ENOBUFS) | Some(libc::ENOMEM)
707 ) {
708 return AcceptFailure::Exhaustion;
709 }
710 }
711 #[cfg(not(unix))]
712 {
713 let _ = err;
714 }
715 AcceptFailure::Ordinary
716 }
717
718 fn record_exhaustion(&mut self, now: std::time::Instant) -> AcceptBackoffAction {
722 self.in_backoff = true;
723
724 let delay = self.current_delay;
725 self.current_delay = self.current_delay.saturating_mul(2).min(self.max_delay);
726
727 let due = match self.last_log_at {
728 None => true,
729 Some(prev) => now.saturating_duration_since(prev) >= self.log_interval,
730 };
731
732 let log_suppressed = if due {
733 self.last_log_at = Some(now);
734 Some(std::mem::take(&mut self.suppressed))
735 } else {
736 self.suppressed += 1;
737 None
738 };
739
740 AcceptBackoffAction {
741 delay,
742 log_suppressed,
743 }
744 }
745
746 fn record_success(&mut self, now: impl FnOnce() -> std::time::Instant) -> Option<u64> {
755 if !self.in_backoff {
756 return None;
757 }
758 self.current_delay = self.initial_delay;
759 self.in_backoff = false;
760 let now = now();
761
762 let due = match self.last_log_at {
763 None => true,
764 Some(prev) => now.saturating_duration_since(prev) >= self.log_interval,
765 };
766 if !due {
767 return None;
768 }
769 self.last_log_at = Some(now);
770 Some(std::mem::take(&mut self.suppressed))
771 }
772}
773
774async fn handle_accept_error(err: &std::io::Error, backoff: &mut AcceptBackoff) -> Duration {
778 match AcceptBackoff::classify(err) {
779 AcceptFailure::Ordinary => {
780 tracing::warn!(error = %err, "failed to accept tcp connection");
782 #[cfg(debug_assertions)]
785 eprintln!("failed to accept tcp connection: {}", err);
786 Duration::ZERO
787 }
788 AcceptFailure::Exhaustion => {
789 crate::metrics::transport_metrics::TCP_ACCEPT_BACKOFF_TOTAL.inc();
790 let action = backoff.record_exhaustion(std::time::Instant::now());
791 if let Some(suppressed) = action.log_suppressed {
792 tracing::warn!(
793 error = %err,
794 retry_delay_ms = action.delay.as_millis() as u64,
795 suppressed_failures = suppressed,
796 "tcp accept failed: out of file descriptors or kernel memory; backing off before retry"
797 );
798 }
799 time::sleep(action.delay).await;
800 action.delay
801 }
802 }
803}
804
805type BoxRead = Box<dyn tokio::io::AsyncRead + Unpin + Send>;
807type BoxWrite = Box<dyn tokio::io::AsyncWrite + Unpin + Send>;
808
809async fn cancel_response_handshake(
812 connection: oneshot::Sender<Result<StreamReceiver, StreamPrologueError>>,
813 registration_guard: Option<ResponseRegistrationGuard>,
814 mut writer: FramedWrite<BoxWrite, TwoPartCodec>,
815) {
816 drop(registration_guard);
817 drop(connection);
818 let cleanup = async move {
819 if let Ok(bytes) = serde_json::to_vec(&ControlMessage::Kill) {
820 let _ = writer.send(TwoPartMessage::from_header(bytes.into())).await;
821 }
822 let _ = writer.into_inner().shutdown().await;
823 };
824 if time::timeout(HANDSHAKE_CANCEL_CLEANUP_TIMEOUT, cleanup)
825 .await
826 .is_err()
827 {
828 tracing::debug!("timed out notifying worker of cancelled response handshake");
829 }
830}
831
832async fn tcp_listener(
839 addr: SocketAddr,
840 state: Arc<Mutex<State>>,
841 tls_acceptor: Option<TlsAcceptor>,
842 read_tx: tokio::sync::oneshot::Sender<Result<SocketAddr>>,
843) -> Result<()> {
844 let listener = bind_listener(addr)
845 .map_err(|e| anyhow::anyhow!("Failed to start TcpListender on {}: {}", addr, e));
846
847 let listener = match listener {
848 Ok(listener) => {
849 let addr = listener
850 .local_addr()
851 .map_err(|e| anyhow::anyhow!("Failed get SocketAddr: {:?}", e))
852 .unwrap();
853
854 read_tx.send(Ok(addr)).expect("Failed to send ready signal");
855
856 listener
857 }
858 Err(e) => {
859 read_tx.send(Err(e)).expect("Failed to send ready signal");
860 return Err(anyhow::anyhow!("Failed to start TcpListender on {}", addr));
861 }
862 };
863
864 let mut accept_backoff = AcceptBackoff::default();
865
866 loop {
867 let (stream, _addr) = match listener.accept().await {
873 Ok((stream, _addr)) => {
874 if let Some(suppressed) = accept_backoff.record_success(std::time::Instant::now) {
875 tracing::warn!(
876 suppressed_failures = suppressed,
877 "tcp accept recovered from resource exhaustion"
878 );
879 }
880 (stream, _addr)
881 }
882 Err(e) => {
883 handle_accept_error(&e, &mut accept_backoff).await;
884 continue;
885 }
886 };
887
888 match stream.set_nodelay(true) {
889 Ok(_) => (),
890 Err(e) => {
891 tracing::warn!("failed to set tcp stream to nodelay: {e}");
892 }
893 }
894
895 match SockRef::from(&stream).set_linger(Some(std::time::Duration::from_secs(0))) {
896 Ok(_) => (),
897 Err(e) => {
898 tracing::warn!("failed to set tcp stream to linger: {e}");
899 }
900 }
901
902 let state_clone = state.clone();
905 let tls_acceptor_clone = tls_acceptor.clone();
906 tokio::spawn(async move {
907 let (reader, writer) = if let Some(ref tls) = tls_acceptor_clone {
908 match tokio::time::timeout(
909 crate::tls_utils::handshake_timeout(),
910 tls.accept(stream),
911 )
912 .await
913 {
914 Ok(Ok(tls_stream)) => {
915 let (r, w) = tokio::io::split(tls_stream);
916 (Box::new(r) as BoxRead, Box::new(w) as BoxWrite)
917 }
918 Ok(Err(e)) => {
919 tracing::warn!("TLS handshake failed: {e}");
920 return;
921 }
922 Err(_) => {
923 tracing::warn!("TLS handshake timed out");
924 return;
925 }
926 }
927 } else {
928 let (r, w) = tokio::io::split(stream);
929 (Box::new(r) as BoxRead, Box::new(w) as BoxWrite)
930 };
931 handle_connection(reader, writer, state_clone).await;
932 });
933 }
934
935 async fn handle_connection(reader: BoxRead, writer: BoxWrite, state: Arc<Mutex<State>>) {
938 let result = process_stream(reader, writer, state).await;
939 match result {
940 Ok(_) => tracing::trace!("successfully processed tcp connection"),
941 Err(e) => {
942 tracing::warn!("failed to handle tcp connection: {e}");
943 #[cfg(debug_assertions)]
944 eprintln!("failed to handle tcp connection: {}", e);
945 }
946 }
947 }
948
949 async fn process_stream(
952 read_half: BoxRead,
953 write_half: BoxWrite,
954 state: Arc<Mutex<State>>,
955 ) -> Result<()> {
956 let mut framed_reader = FramedRead::new(read_half, TwoPartCodec::default());
958 let framed_writer = FramedWrite::new(write_half, TwoPartCodec::default());
959
960 let first_message =
963 tokio::time::timeout(std::time::Duration::from_secs(10), framed_reader.next())
964 .await
965 .map_err(|_| error!("Timed out waiting for CallHomeHandshake"))?
966 .ok_or(error!("Connection closed without a ControlMessage"))??;
967
968 let handshake: CallHomeHandshake = match first_message.header() {
971 Some(header) => serde_json::from_slice(header).map_err(|e| {
972 error!(
973 "Failed to deserialize the first message as a valid `CallHomeHandshake`: {e}",
974 )
975 })?,
976 None => {
977 return Err(error!("Expected ControlMessage, got DataMessage"));
978 }
979 };
980
981 match handshake.stream_type {
983 StreamType::Request => {
984 process_request_stream(handshake.subject, state, framed_reader, framed_writer).await
985 }
986 StreamType::Response => {
987 process_response_stream(handshake.subject, state, framed_reader, framed_writer)
988 .await
989 }
990 }
991 }
992
993 async fn process_request_stream(
1004 subject: String,
1005 state: Arc<Mutex<State>>,
1006 reader: FramedRead<BoxRead, TwoPartCodec>,
1007 writer: FramedWrite<BoxWrite, TwoPartCodec>,
1008 ) -> Result<()> {
1009 drop(reader);
1011
1012 let request_stream = TcpStreamServer::take_request_stream(&state, &subject).ok_or_else(|| {
1013 error!(
1014 "Subject not found: {}; downstream subscriber specified a subject unknown to the upstream publisher",
1015 subject
1016 )
1017 })?;
1018
1019 let RequestedSendConnection {
1020 context,
1021 connection,
1022 send_buffer_count,
1023 } = request_stream;
1024
1025 let (request_tx, request_rx) = data_plane_channel(send_buffer_count);
1029
1030 if connection
1031 .send(Ok(crate::pipeline::network::StreamSender {
1032 tx: request_tx,
1033 prologue: None,
1036 }))
1037 .is_err()
1038 {
1039 return Err(error!(
1040 "The requester of the request stream has been dropped before the connection was established"
1041 ));
1042 }
1043
1044 request_stream_send_handler(writer, request_rx, context).await;
1045 Ok(())
1046 }
1047
1048 async fn request_stream_send_handler(
1058 mut framed_writer: FramedWrite<BoxWrite, TwoPartCodec>,
1059 mut request_rx: mpsc::Receiver<TwoPartMessage>,
1060 context: Arc<dyn AsyncEngineContext>,
1061 ) {
1062 let killed = context.killed();
1065 let stopped = context.stopped();
1066 tokio::pin!(killed, stopped);
1067
1068 let closing_msg: Option<ControlMessage> = loop {
1069 tokio::select! {
1070 biased;
1071
1072 _ = &mut killed => {
1073 tracing::trace!("context kill received in request-stream send handler");
1074 break Some(ControlMessage::Kill);
1075 }
1076
1077 _ = &mut stopped => {
1078 tracing::trace!("context stop received in request-stream send handler");
1079 break Some(ControlMessage::Stop);
1080 }
1081
1082 msg = request_rx.recv() => {
1083 match msg {
1084 Some(msg) => {
1085 if let Err(e) = framed_writer.send(msg).await {
1086 tracing::trace!(
1087 "failed to send request-stream frame to downstream: {:?}",
1088 e
1089 );
1090 break None;
1091 }
1092 }
1093 None => {
1094 tracing::trace!("upstream request-stream sender closed; sending sentinel");
1095 break Some(ControlMessage::Sentinel);
1096 }
1097 }
1098 }
1099 }
1100 };
1101
1102 if let Some(ctrl) = closing_msg
1103 && let Ok(bytes) = serde_json::to_vec(&ctrl)
1104 && let Err(err) = framed_writer
1105 .send(TwoPartMessage::from_header(bytes.into()))
1106 .await
1107 {
1108 tracing::trace!(?err, ?ctrl, "request-stream closing-frame send failed");
1109 }
1110
1111 let mut inner = framed_writer.into_inner();
1112 if let Err(err) = inner.flush().await {
1113 tracing::trace!(?err, "request-stream socket flush failed");
1114 }
1115 if let Err(err) = inner.shutdown().await {
1116 tracing::trace!(?err, "request-stream socket shutdown failed");
1117 }
1118 }
1119
1120 async fn process_response_stream(
1121 subject: String,
1122 state: Arc<Mutex<State>>,
1123 mut reader: FramedRead<BoxRead, TwoPartCodec>,
1124 mut writer: FramedWrite<BoxWrite, TwoPartCodec>,
1125 ) -> Result<()> {
1126 let response_stream = TcpStreamServer::take_response_stream(&state, &subject).ok_or_else(|| {
1127 error!("Subject not found: {}; upstream publisher specified a subject unknown to the downsteam subscriber", subject)
1128 })?;
1129 let registration_guard = ResponseRegistrationGuard {
1130 state,
1131 subject,
1132 is_active: true,
1133 };
1134
1135 let RequestedRecvConnection {
1137 context,
1138 mut connection,
1139 cancellation,
1140 send_buffer_count,
1141 defer_cancellation_until_prologue,
1142 } = response_stream;
1143
1144 let prologue = tokio::select! {
1149 biased;
1150 _ = cancellation.cancelled() => {
1151 cancel_response_handshake(connection, Some(registration_guard), writer).await;
1152 return Ok(());
1153 }
1154 _ = context.killed(), if !defer_cancellation_until_prologue => Err(ControlMessage::Kill),
1155 _ = connection.closed() => Err(ControlMessage::Kill),
1156 _ = context.stopped(), if !defer_cancellation_until_prologue => Err(ControlMessage::Stop),
1157 prologue = reader.next() => Ok(prologue),
1158 };
1159 let prologue = match prologue {
1160 Ok(prologue) => {
1161 prologue.ok_or(error!("Connection closed without a ControlMessage"))??
1162 }
1163 Err(control) => {
1164 drop(registration_guard);
1165 let message = "Request cancelled before response stream was established";
1166 let error = DynamoError::builder()
1167 .error_type(ErrorType::Cancelled)
1168 .message(message)
1169 .build();
1170 let _ = connection.send(Err(StreamPrologueError::new(message, error)));
1171 writer
1172 .send(TwoPartMessage::from_header(
1173 serde_json::to_vec(&control)?.into(),
1174 ))
1175 .await?;
1176 writer.close().await?;
1177 return Ok(());
1178 }
1179 };
1180
1181 let prologue = match prologue.into_message_type() {
1183 TwoPartMessageType::HeaderOnly(header) => {
1184 match serde_json::from_slice::<ResponseStreamPrologue>(&header) {
1185 Ok(prologue) => prologue,
1186 Err(e) => {
1187 let msg = format!("malformed prologue: {e}");
1192 let _ =
1193 connection.send(Err(StreamPrologueError::from_message(msg.clone())));
1194 return Err(error!(msg));
1195 }
1196 }
1197 }
1198 _ => {
1199 let msg = "malformed prologue: expected HeaderOnly ControlMessage";
1204 let _ = connection.send(Err(StreamPrologueError::from_message(msg)));
1205 return Err(error!(msg));
1206 }
1207 };
1208
1209 if let Some(error) = prologue.error {
1216 let returned = error!("Received error prologue: {error}");
1217 let _ = connection.send(Err(StreamPrologueError {
1220 message: error,
1221 typed_error: prologue.typed_error,
1222 }));
1223 return Err(returned);
1224 }
1225
1226 if !registration_guard.accept() {
1230 cancel_response_handshake(connection, None, writer).await;
1231 return Ok(());
1232 }
1233
1234 let (response_tx, response_rx) = data_plane_channel(send_buffer_count);
1238
1239 if connection
1240 .send(Ok(crate::pipeline::network::StreamReceiver {
1241 rx: response_rx,
1242 }))
1243 .is_err()
1244 {
1245 return Err(error!(
1246 "The requester of the stream has been dropped before the connection was established"
1247 ));
1248 }
1249
1250 let (control_tx, control_rx) = mpsc::channel::<ControlMessage>(1);
1251
1252 let send_task = tokio::spawn(network_send_handler(writer, control_rx));
1256
1257 let recv_task = tokio::spawn(network_receive_handler(
1259 reader,
1260 response_tx,
1261 control_tx,
1262 context.clone(),
1263 ));
1264
1265 let (monitor_result, forward_result) = tokio::join!(send_task, recv_task);
1267
1268 monitor_result?;
1269 forward_result?;
1270
1271 Ok(())
1272 }
1273
1274 async fn network_receive_handler(
1275 mut framed_reader: FramedRead<BoxRead, TwoPartCodec>,
1276 response_tx: mpsc::Sender<Bytes>,
1277 control_tx: mpsc::Sender<ControlMessage>,
1278 context: Arc<dyn AsyncEngineContext>,
1279 ) {
1280 let response_closed = response_tx.closed();
1283 let killed = context.killed();
1284 let stopped = context.stopped();
1285 tokio::pin!(response_closed, killed, stopped);
1286
1287 let mut can_stop = true;
1289 loop {
1290 tokio::select! {
1291 biased;
1292
1293 _ = &mut response_closed => {
1294 tracing::trace!("response channel closed before the client finished writing data");
1295 let _ = control_tx.send(ControlMessage::Kill).await;
1296 break;
1297 }
1298
1299 _ = &mut killed => {
1300 tracing::trace!("context kill signal received; shutting down");
1301 let _ = control_tx.send(ControlMessage::Kill).await;
1302 break;
1303 }
1304
1305 _ = &mut stopped, if can_stop => {
1306 tracing::trace!("context stop signal received; shutting down");
1307 can_stop = false;
1310 let _ = control_tx.send(ControlMessage::Stop).await;
1311 }
1312
1313 msg = framed_reader.next() => {
1314 match msg {
1315 Some(Ok(msg)) => {
1316 let (header, data) = msg.into_parts();
1317
1318 if !header.is_empty() {
1320 match process_control_message(header) {
1321 Ok(ControlAction::Continue) => {}
1322 Ok(ControlAction::Shutdown) => {
1323 if !data.is_empty() {
1324 tracing::warn!(
1328 data_len = data.len(),
1329 "client sent Sentinel with data (protocol violation); killing stream"
1330 );
1331 let _ = control_tx.send(ControlMessage::Kill).await;
1332 break;
1333 }
1334 tracing::trace!("received sentinel message; shutting down");
1335 break;
1336 }
1337 Err(e) => {
1338 tracing::warn!(err = ?e, "malformed control message, closing connection");
1341 let _ = control_tx.send(ControlMessage::Kill).await;
1342 break;
1343 }
1344 }
1345 }
1346
1347 if !data.is_empty()
1348 && let Err(err) = response_tx.send(data).await {
1349 tracing::debug!(?err, "forwarding body/data to response channel failed");
1350 let _ = control_tx.send(ControlMessage::Kill).await;
1351 break;
1352 };
1353 }
1354 Some(Err(e)) => {
1355 tracing::warn!(err = ?e, "tcp stream read error from worker, closing connection");
1358 let _ = control_tx.send(ControlMessage::Kill).await;
1359 break;
1360 }
1361 None => {
1362 tracing::trace!("tcp stream was closed by client");
1368 break;
1369 }
1370 }
1371 }
1372
1373 }
1374 }
1375 }
1376
1377 async fn network_send_handler(
1378 socket_tx: FramedWrite<BoxWrite, TwoPartCodec>,
1379 control_rx: mpsc::Receiver<ControlMessage>,
1380 ) {
1381 let mut socket_tx = socket_tx;
1382 let mut control_rx = control_rx;
1383
1384 while let Some(control_msg) = control_rx.recv().await {
1385 if matches!(control_msg, ControlMessage::Sentinel) {
1389 tracing::warn!("received sentinel on send-side control channel; dropping");
1390 continue;
1391 }
1392 let bytes = match serde_json::to_vec(&control_msg) {
1393 Ok(b) => b,
1394 Err(e) => {
1395 tracing::warn!(err = ?e, ?control_msg, "failed to serialize control message");
1398 continue;
1399 }
1400 };
1401 let message = TwoPartMessage::from_header(bytes.into());
1402 match socket_tx.send(message).await {
1403 Ok(_) => tracing::debug!(?control_msg, "issued control message"),
1404 Err(e) => {
1405 tracing::debug!(err = ?e, ?control_msg, "failed to send control message")
1406 }
1407 }
1408 }
1409
1410 let mut inner = socket_tx.into_inner();
1411 if let Err(e) = inner.flush().await {
1412 tracing::debug!("failed to flush socket: {e}");
1413 }
1414 if let Err(e) = inner.shutdown().await {
1415 tracing::debug!("failed to shutdown socket: {e}");
1416 }
1417 }
1418}
1419
1420enum ControlAction {
1421 Continue,
1422 Shutdown,
1423}
1424
1425fn process_control_message(message: Bytes) -> Result<ControlAction> {
1426 match serde_json::from_slice::<ControlMessage>(&message)? {
1427 ControlMessage::Sentinel => {
1428 tracing::trace!("sentinel received; shutting down");
1431 Ok(ControlAction::Shutdown)
1432 }
1433 ControlMessage::Kill | ControlMessage::Stop => {
1434 anyhow::bail!("unexpected control message on response stream");
1438 }
1439 }
1440}
1441
1442#[cfg(test)]
1443mod tests {
1444 use super::*;
1445 use crate::engine::AsyncEngineContextProvider;
1446 use crate::error::{BackendError, DynamoError, ErrorType};
1447 use crate::pipeline::Context;
1448 use crate::pipeline::network::DEFAULT_SEND_BUFFER_COUNT;
1449 use crate::pipeline::network::tcp::client::TcpClient;
1450 use crate::tls_utils::test_certs::self_signed_pair;
1451 use crate::utils::ip_resolver::test_support::{ProbeOutcome, StubResolver};
1452 use tokio::io::{AsyncWriteExt, ReadHalf, WriteHalf};
1453 use tokio::net::TcpStream;
1454
1455 #[test]
1456 fn build_tls_acceptor_no_env_vars_is_plaintext() {
1457 if crate::test_utils::run_isolated(
1458 concat!(
1459 module_path!(),
1460 "::build_tls_acceptor_no_env_vars_is_plaintext"
1461 ),
1462 &[],
1463 ) {
1464 return;
1465 }
1466
1467 temp_env::with_vars_unset(
1470 [
1471 "DYN_TCP_TLS_CERT_PATH",
1472 "DYN_TCP_TLS_KEY_PATH",
1473 "DYN_TCP_TLS_CLIENT_CA_CERT_PATH",
1474 ],
1475 || {
1476 assert!(TcpStreamServer::build_tls_acceptor().unwrap().is_none());
1477 },
1478 );
1479 }
1480
1481 #[test]
1482 fn build_tls_acceptor_partial_config_errors() {
1483 if crate::test_utils::run_isolated(
1484 concat!(module_path!(), "::build_tls_acceptor_partial_config_errors"),
1485 &[],
1486 ) {
1487 return;
1488 }
1489
1490 let (cert, key) = self_signed_pair();
1491 let cert_str = cert.path().to_str().unwrap();
1492 let key_str = key.path().to_str().unwrap();
1493 temp_env::with_vars(
1495 [
1496 ("DYN_TCP_TLS_CERT_PATH", Some(cert_str)),
1497 ("DYN_TCP_TLS_KEY_PATH", None),
1498 ],
1499 || assert!(TcpStreamServer::build_tls_acceptor().is_err()),
1500 );
1501 temp_env::with_vars(
1503 [
1504 ("DYN_TCP_TLS_CERT_PATH", None),
1505 ("DYN_TCP_TLS_KEY_PATH", Some(key_str)),
1506 ],
1507 || assert!(TcpStreamServer::build_tls_acceptor().is_err()),
1508 );
1509 }
1510
1511 #[test]
1512 fn build_tls_acceptor_both_paths_is_tls() {
1513 if crate::test_utils::run_isolated(
1514 concat!(module_path!(), "::build_tls_acceptor_both_paths_is_tls"),
1515 &[],
1516 ) {
1517 return;
1518 }
1519
1520 let (cert, key) = self_signed_pair();
1521 temp_env::with_vars(
1522 [
1523 ("DYN_TCP_TLS_CERT_PATH", Some(cert.path().to_str().unwrap())),
1524 ("DYN_TCP_TLS_KEY_PATH", Some(key.path().to_str().unwrap())),
1525 ],
1526 || assert!(TcpStreamServer::build_tls_acceptor().unwrap().is_some()),
1527 );
1528 }
1529
1530 #[test]
1531 fn build_tls_acceptor_with_client_ca_is_mtls() {
1532 if crate::test_utils::run_isolated(
1533 concat!(
1534 module_path!(),
1535 "::build_tls_acceptor_with_client_ca_is_mtls"
1536 ),
1537 &[],
1538 ) {
1539 return;
1540 }
1541
1542 let (cert, key) = self_signed_pair();
1544 temp_env::with_vars(
1545 [
1546 ("DYN_TCP_TLS_CERT_PATH", Some(cert.path().to_str().unwrap())),
1547 ("DYN_TCP_TLS_KEY_PATH", Some(key.path().to_str().unwrap())),
1548 (
1549 "DYN_TCP_TLS_CLIENT_CA_CERT_PATH",
1550 Some(cert.path().to_str().unwrap()),
1551 ),
1552 ],
1553 || assert!(TcpStreamServer::build_tls_acceptor().unwrap().is_some()),
1554 );
1555 }
1556
1557 #[test]
1558 fn build_tls_acceptor_client_ca_without_server_identity_errors() {
1559 if crate::test_utils::run_isolated(
1560 concat!(
1561 module_path!(),
1562 "::build_tls_acceptor_client_ca_without_server_identity_errors"
1563 ),
1564 &[],
1565 ) {
1566 return;
1567 }
1568
1569 let (cert, _key) = self_signed_pair();
1570 temp_env::with_vars(
1571 [
1572 ("DYN_TCP_TLS_CERT_PATH", None),
1573 ("DYN_TCP_TLS_KEY_PATH", None),
1574 (
1575 "DYN_TCP_TLS_CLIENT_CA_CERT_PATH",
1576 Some(cert.path().to_str().unwrap()),
1577 ),
1578 ],
1579 || assert!(TcpStreamServer::build_tls_acceptor().is_err()),
1580 );
1581 }
1582
1583 async fn registered_tcp_info(server: &TcpStreamServer) -> TcpStreamConnectionInfo {
1584 let context = Context::new(());
1585 let stream_options = StreamOptions::builder()
1586 .context(context.context())
1587 .enable_request_stream(false)
1588 .enable_response_stream(true)
1589 .build()
1590 .unwrap();
1591
1592 let pending_connection = server.register(stream_options).await;
1593 let connection_info = pending_connection
1594 .recv_stream
1595 .as_ref()
1596 .unwrap()
1597 .connection_info
1598 .clone();
1599 connection_info.try_into().unwrap()
1600 }
1601
1602 #[tokio::test]
1603 async fn wildcard_bind_advertises_concrete_ipv4_with_bound_port() {
1604 for host in ["0.0.0.0", "::ffff:0.0.0.0", " [::ffff:0.0.0.0] "] {
1605 let mut resolver = StubResolver::not_found();
1606 resolver
1607 .interfaces
1608 .push(("eth0", "192.0.2.20".parse().unwrap()));
1609 let options = ServerOptions::builder()
1610 .port(0)
1611 .interface(Some(host.to_string()))
1612 .build()
1613 .unwrap();
1614 let server = TcpStreamServer::new_with_resolver(options, resolver)
1615 .await
1616 .unwrap();
1617
1618 let tcp_info = registered_tcp_info(&server).await;
1619 let socket_addr: SocketAddr = tcp_info.address.parse().unwrap();
1620 assert_eq!(socket_addr.ip(), "192.0.2.20".parse::<IpAddr>().unwrap());
1621 assert_ne!(socket_addr.port(), 0);
1622 assert!(!socket_addr.ip().is_unspecified());
1623 let _connection = tokio::net::TcpStream::connect(SocketAddr::new(
1625 std::net::Ipv4Addr::LOCALHOST.into(),
1626 socket_addr.port(),
1627 ))
1628 .await
1629 .unwrap();
1630 }
1631 }
1632
1633 #[tokio::test]
1634 async fn real_bracketed_ipv6_host_binds_and_registers() {
1635 if let Err(error) = std::net::TcpListener::bind("[::1]:0") {
1636 eprintln!("Skipping IPv6 loopback bind test: {error}");
1637 return;
1638 }
1639
1640 let options = ServerOptions::builder()
1641 .port(0)
1642 .interface(Some("[::1]".to_string()))
1643 .build()
1644 .unwrap();
1645 let server = TcpStreamServer::new_with_resolver(options, StubResolver::not_found())
1646 .await
1647 .unwrap();
1648
1649 let tcp_info = registered_tcp_info(&server).await;
1650 let socket_addr: SocketAddr = tcp_info.address.parse().unwrap();
1651 assert_eq!(socket_addr.ip(), IpAddr::V6(std::net::Ipv6Addr::LOCALHOST));
1652 assert_ne!(socket_addr.port(), 0);
1653 assert!(
1654 tcp_info.address.starts_with("[::1]:"),
1655 "{}",
1656 tcp_info.address
1657 );
1658 }
1659
1660 #[tokio::test]
1661 async fn tcp_server_preserves_interface_enumeration_error() {
1662 let mut resolver = StubResolver::not_found();
1663 resolver.interface_error = Some(ProbeOutcome::Platform("test-platform"));
1664 let options = ServerOptions::builder().port(0).build().unwrap();
1665 let error = TcpStreamServer::new_with_resolver(options, resolver)
1666 .await
1667 .err()
1668 .expect("an interface enumeration failure must be returned");
1669
1670 let error = error.to_string();
1671 assert!(
1672 error.contains("failed to enumerate network interfaces"),
1673 "{error}"
1674 );
1675 assert!(error.contains("test-platform"), "{error}");
1676 }
1677
1678 #[tokio::test]
1679 async fn test_tcp_stream_server_default_behavior() {
1680 let options = ServerOptions::default();
1683 let result = TcpStreamServer::new(options).await;
1684
1685 assert!(
1686 result.is_ok(),
1687 "TcpStreamServer::new should succeed with default options"
1688 );
1689
1690 let server = result.unwrap();
1691
1692 let tcp_info = registered_tcp_info(&server).await;
1693 let socket_addr = tcp_info.address.parse::<std::net::SocketAddr>().unwrap();
1694
1695 assert!(
1697 socket_addr.port() > 0,
1698 "Server should be assigned a valid port number"
1699 );
1700
1701 println!(
1702 "Server created successfully with address: {}",
1703 tcp_info.address
1704 );
1705 }
1706
1707 #[test]
1713 fn data_plane_channel_capacity_matches_send_buffer_count() {
1714 let (tx, _rx) = data_plane_channel::<()>(7);
1715 assert_eq!(tx.max_capacity(), 7);
1716
1717 let (tx, _rx) = data_plane_channel::<()>(DEFAULT_SEND_BUFFER_COUNT);
1718 assert_eq!(tx.max_capacity(), 64);
1719
1720 let (tx, _rx) = data_plane_channel::<()>(0);
1722 assert_eq!(tx.max_capacity(), 1);
1723 }
1724
1725 #[tokio::test]
1730 async fn register_threads_send_buffer_count_into_connection_structs() {
1731 let server = TcpStreamServer::new(ServerOptions::default())
1732 .await
1733 .expect("server");
1734 let context = Context::new(());
1735 let options = StreamOptions::builder()
1736 .context(context.context())
1737 .enable_request_stream(true)
1738 .enable_response_stream(true)
1739 .send_buffer_count(7)
1740 .build()
1741 .unwrap();
1742
1743 let _pending = server.register(options).await;
1744
1745 let state = server.state.lock();
1746 assert_eq!(state.tx_subjects.len(), 1, "one request stream registered");
1747 assert_eq!(state.rx_subjects.len(), 1, "one response stream registered");
1748 assert!(
1749 state.tx_subjects.values().all(|c| c.send_buffer_count == 7),
1750 "send_buffer_count must reach RequestedSendConnection"
1751 );
1752 assert!(
1753 state.rx_subjects.values().all(|c| c.send_buffer_count == 7),
1754 "send_buffer_count must reach RequestedRecvConnection"
1755 );
1756 }
1757
1758 #[tokio::test]
1759 async fn test_tcp_stream_server_fallback_to_loopback() {
1760 let options = ServerOptions::builder().port(0).build().unwrap();
1764
1765 let result = TcpStreamServer::new_with_resolver(options, StubResolver::not_found()).await;
1767 assert!(
1768 result.is_ok(),
1769 "Server creation should succeed with fallback even when IP detection fails"
1770 );
1771
1772 let server = result.unwrap();
1773
1774 let tcp_info = registered_tcp_info(&server).await;
1775 let socket_addr = tcp_info.address.parse::<std::net::SocketAddr>().unwrap();
1776
1777 let ip = socket_addr.ip();
1779 assert!(
1780 ip.is_loopback(),
1781 "Should use loopback when IP detection fails"
1782 );
1783
1784 assert_eq!(
1786 ip,
1787 std::net::IpAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 1)),
1788 "Fallback should use exactly 127.0.0.1, got: {}",
1789 ip
1790 );
1791
1792 println!("SUCCESS: Fallback to 127.0.0.1 was confirmed: {}", ip);
1793
1794 assert!(socket_addr.port() > 0, "Server should have a valid port");
1796 }
1797
1798 #[tokio::test]
1799 async fn configured_ip_literals_bind_and_format_addresses() {
1800 let ipv6_available = std::net::TcpListener::bind("[::1]:0").is_ok();
1801
1802 for (host, expected_ip) in [
1803 ("127.0.0.1", "127.0.0.1".parse::<IpAddr>().unwrap()),
1804 ("::1", "::1".parse::<IpAddr>().unwrap()),
1805 (" 127.0.0.1 ", "127.0.0.1".parse::<IpAddr>().unwrap()),
1806 (" [::1]\t", "::1".parse::<IpAddr>().unwrap()),
1807 ("::ffff:127.0.0.1", "127.0.0.1".parse::<IpAddr>().unwrap()),
1808 (" lo ", "127.0.0.1".parse::<IpAddr>().unwrap()),
1809 (" lo:1 ", "127.0.0.1".parse::<IpAddr>().unwrap()),
1810 ] {
1811 if expected_ip.is_ipv6() && !ipv6_available {
1812 eprintln!("skipping IPv6 bind because this host does not support IPv6 loopback");
1813 continue;
1814 }
1815
1816 let mut resolver = StubResolver::not_found();
1817 resolver
1818 .interfaces
1819 .push(("lo:1", "127.0.0.1".parse().unwrap()));
1820 let server = TcpStreamServer::new_with_resolver(
1821 ServerOptions {
1822 port: 0,
1823 interface: Some(host.to_string()),
1824 },
1825 resolver,
1826 )
1827 .await
1828 .unwrap();
1829 let context = Context::new(());
1830 let pending = server
1831 .register(
1832 StreamOptions::builder()
1833 .context(context.context())
1834 .enable_request_stream(false)
1835 .enable_response_stream(true)
1836 .build()
1837 .unwrap(),
1838 )
1839 .await;
1840 let connection_info = pending.recv_stream.unwrap().connection_info;
1841 let tcp_info: TcpStreamConnectionInfo = connection_info.try_into().unwrap();
1842 let address = tcp_info.address.parse::<SocketAddr>().unwrap();
1843
1844 assert_eq!(address, server.local_address().unwrap());
1845 assert_eq!(address.ip(), expected_ip);
1846 assert_ne!(address.port(), 0);
1847 if expected_ip.is_ipv6() {
1848 assert!(tcp_info.address.starts_with('['));
1849 }
1850 }
1851 }
1852
1853 async fn test_server() -> Arc<TcpStreamServer> {
1855 TcpStreamServer::new_with_resolver(
1856 ServerOptions::builder().port(0).build().unwrap(),
1857 StubResolver::not_found(),
1858 )
1859 .await
1860 .unwrap()
1861 }
1862
1863 async fn register_and_get_subject(
1865 server: &TcpStreamServer,
1866 ) -> (
1867 String,
1868 tokio::sync::oneshot::Receiver<Result<super::StreamReceiver, StreamPrologueError>>,
1869 ) {
1870 let context = Context::new(());
1871 let options = StreamOptions::builder()
1872 .context(context.context())
1873 .enable_request_stream(false)
1874 .enable_response_stream(true)
1875 .build()
1876 .unwrap();
1877
1878 let pending = server.register(options).await;
1879 let recv_stream = pending.recv_stream.unwrap();
1880 let (conn_info, provider) = recv_stream.into_parts();
1881 let tcp_info: TcpStreamConnectionInfo = conn_info.try_into().unwrap();
1882 (tcp_info.subject, provider)
1883 }
1884
1885 fn make_eid(
1887 namespace: &str,
1888 component: &str,
1889 endpoint: &str,
1890 instance_id: u64,
1891 ) -> EndpointInstanceId {
1892 EndpointInstanceId {
1893 namespace: namespace.to_string(),
1894 component: component.to_string(),
1895 endpoint: endpoint.to_string(),
1896 instance_id,
1897 }
1898 }
1899
1900 async fn register_and_get_bidi_subjects(
1903 server: &TcpStreamServer,
1904 ) -> (
1905 String,
1906 tokio::sync::oneshot::Receiver<Result<super::StreamSender, StreamPrologueError>>,
1907 String,
1908 tokio::sync::oneshot::Receiver<Result<super::StreamReceiver, StreamPrologueError>>,
1909 ) {
1910 let context = Context::new(());
1911 let options = StreamOptions::builder()
1912 .context(context.context())
1913 .enable_request_stream(true)
1914 .enable_response_stream(true)
1915 .build()
1916 .unwrap();
1917
1918 let pending = server.register(options).await;
1919 let send_stream = pending.send_stream.unwrap();
1920 let recv_stream = pending.recv_stream.unwrap();
1921 let (send_info, send_provider) = send_stream.into_parts();
1922 let (recv_info, recv_provider) = recv_stream.into_parts();
1923 let send_tcp_info: TcpStreamConnectionInfo = send_info.try_into().unwrap();
1924 let recv_tcp_info: TcpStreamConnectionInfo = recv_info.try_into().unwrap();
1925 (
1926 send_tcp_info.subject,
1927 send_provider,
1928 recv_tcp_info.subject,
1929 recv_provider,
1930 )
1931 }
1932
1933 #[tokio::test]
1938 async fn test_cancel_instance_streams_drops_both_bidi_halves() {
1939 let server = test_server().await;
1940 let (send_subj, send_provider, recv_subj, recv_provider) =
1941 register_and_get_bidi_subjects(&server).await;
1942
1943 let id = make_eid("ns", "comp", "generate", 7);
1944 assert!(
1945 server
1946 .associate_instance(&recv_subj, Some(&send_subj), &id)
1947 .await,
1948 "fresh instance must not be tombstoned"
1949 );
1950
1951 let cancelled = server.cancel_instance_streams(&id).await;
1952 assert_eq!(cancelled, 2, "both request + response halves must count");
1953
1954 assert!(
1955 recv_provider.await.is_err(),
1956 "recv provider should resolve with RecvError"
1957 );
1958 assert!(
1959 send_provider.await.is_err(),
1960 "send provider should resolve with RecvError after instance cancellation"
1961 );
1962 }
1963
1964 #[tokio::test]
1967 async fn test_associate_instance_tombstone_cancels_both_bidi_halves() {
1968 let server = test_server().await;
1969 let id = make_eid("ns", "comp", "generate", 8);
1970 server.cancel_instance_streams(&id).await;
1972
1973 let (send_subj, send_provider, recv_subj, recv_provider) =
1974 register_and_get_bidi_subjects(&server).await;
1975
1976 assert!(
1977 !server
1978 .associate_instance(&recv_subj, Some(&send_subj), &id)
1979 .await,
1980 "tombstoned instance must reject association"
1981 );
1982
1983 assert!(recv_provider.await.is_err());
1984 assert!(send_provider.await.is_err());
1985 }
1986
1987 #[tokio::test]
1988 async fn test_cancel_instance_streams_unblocks_receiver() {
1989 let server = test_server().await;
1990
1991 let (subject, provider) = register_and_get_subject(&server).await;
1992
1993 let id = make_eid("ns", "comp", "generate", 42);
1994 assert!(server.associate_instance(&subject, None, &id).await);
1995
1996 let cancelled = server.cancel_instance_streams(&id).await;
1997 assert_eq!(cancelled, 1);
1998
1999 let result = provider.await;
2001 assert!(result.is_err(), "Expected RecvError after cancellation");
2002 }
2003
2004 #[tokio::test]
2005 async fn test_cancel_instance_streams_multiple_subjects() {
2006 let server = test_server().await;
2007
2008 let (subj1, prov1) = register_and_get_subject(&server).await;
2009 let (subj2, prov2) = register_and_get_subject(&server).await;
2010 let (subj3, prov3) = register_and_get_subject(&server).await;
2011
2012 let id10 = make_eid("ns", "comp", "generate", 10);
2013 let id20 = make_eid("ns", "comp", "generate", 20);
2014
2015 assert!(server.associate_instance(&subj1, None, &id10).await);
2017 assert!(server.associate_instance(&subj2, None, &id10).await);
2018 assert!(server.associate_instance(&subj3, None, &id20).await);
2019
2020 let cancelled = server.cancel_instance_streams(&id10).await;
2022 assert_eq!(cancelled, 2);
2023
2024 assert!(prov1.await.is_err());
2025 assert!(prov2.await.is_err());
2026
2027 let cancelled = server.cancel_instance_streams(&id20).await;
2029 assert_eq!(cancelled, 1);
2030 assert!(prov3.await.is_err());
2031 }
2032
2033 #[tokio::test]
2034 async fn test_cancel_instance_streams_nonexistent_instance() {
2035 let server = test_server().await;
2036
2037 let id = make_eid("ns", "comp", "generate", 999);
2038 let cancelled = server.cancel_instance_streams(&id).await;
2039 assert_eq!(cancelled, 0);
2040 }
2041
2042 #[tokio::test]
2043 async fn test_cancel_recv_stream_cleans_up_instance_tracking() {
2044 let server = test_server().await;
2045
2046 let (subject, _provider) = register_and_get_subject(&server).await;
2047 let id = make_eid("ns", "comp", "generate", 42);
2048 assert!(server.associate_instance(&subject, None, &id).await);
2049
2050 server.cancel_recv_stream(&subject).await;
2052
2053 let cancelled = server.cancel_instance_streams(&id).await;
2055 assert_eq!(
2056 cancelled, 0,
2057 "Instance tracking should have been cleaned up"
2058 );
2059 }
2060
2061 #[tokio::test]
2062 async fn test_registered_stream_drop_runs_cleanup() {
2063 let server = test_server().await;
2064
2065 let context = Context::new(());
2067 let options = StreamOptions::builder()
2068 .context(context.context())
2069 .enable_request_stream(false)
2070 .enable_response_stream(true)
2071 .build()
2072 .unwrap();
2073
2074 let pending = server.register(options).await;
2075 let recv_stream = pending.recv_stream.unwrap();
2076
2077 let tcp_info: TcpStreamConnectionInfo =
2079 recv_stream.connection_info.clone().try_into().unwrap();
2080 let subject = tcp_info.subject.clone();
2081
2082 {
2084 let state = server.state.lock();
2085 assert!(state.rx_subjects.contains_key(&subject));
2086 }
2087
2088 drop(recv_stream);
2090
2091 tokio::time::sleep(std::time::Duration::from_millis(50)).await;
2093
2094 {
2096 let state = server.state.lock();
2097 assert!(
2098 !state.rx_subjects.contains_key(&subject),
2099 "RAII cleanup should have removed the rx_subjects entry"
2100 );
2101 }
2102 }
2103
2104 #[tokio::test]
2105 async fn test_registered_stream_into_parts_disarms_cleanup() {
2106 let server = test_server().await;
2107
2108 let context = Context::new(());
2109 let options = StreamOptions::builder()
2110 .context(context.context())
2111 .enable_request_stream(false)
2112 .enable_response_stream(true)
2113 .build()
2114 .unwrap();
2115
2116 let pending = server.register(options).await;
2117 let recv_stream = pending.recv_stream.unwrap();
2118
2119 let tcp_info: TcpStreamConnectionInfo =
2120 recv_stream.connection_info.clone().try_into().unwrap();
2121 let subject = tcp_info.subject.clone();
2122
2123 let (_conn_info, _provider) = recv_stream.into_parts();
2125
2126 tokio::time::sleep(std::time::Duration::from_millis(50)).await;
2128
2129 {
2131 let state = server.state.lock();
2132 assert!(
2133 state.rx_subjects.contains_key(&subject),
2134 "into_parts() should disarm the RAII cleanup"
2135 );
2136 }
2137 }
2138
2139 #[tokio::test]
2140 async fn test_dropped_registered_stream_wait_cleans_instance_indexes() {
2141 let server = test_server().await;
2142 let context = Context::new(());
2143 let options = StreamOptions::builder()
2144 .context(context.context())
2145 .enable_request_stream(false)
2146 .enable_response_stream(true)
2147 .build()
2148 .unwrap();
2149
2150 let pending = server.register(options).await;
2151 let recv_stream = pending.recv_stream.unwrap();
2152 let tcp_info: TcpStreamConnectionInfo =
2153 recv_stream.connection_info.clone().try_into().unwrap();
2154 let subject = tcp_info.subject;
2155 let instance = make_eid("ns", "comp", "generate", 42);
2156 assert!(server.associate_instance(&subject, None, &instance).await);
2157
2158 let mut wait = Box::pin(recv_stream.wait());
2159 assert!(futures::poll!(wait.as_mut()).is_pending());
2160 drop(wait);
2161
2162 tokio::time::timeout(std::time::Duration::from_secs(1), async {
2163 loop {
2164 let cleaned = {
2165 let state = server.state.lock();
2166 !state.rx_subjects.contains_key(&subject)
2167 && !state.rx_cancellations.contains_key(&subject)
2168 && !state.subject_instance.contains_key(&subject)
2169 && !state.instance_subjects.contains_key(&instance)
2170 };
2171 if cleaned {
2172 break;
2173 }
2174 tokio::task::yield_now().await;
2175 }
2176 })
2177 .await
2178 .expect("cancelled wait did not clean response registration indexes");
2179 }
2180
2181 #[tokio::test]
2182 async fn test_associate_after_cancel_is_immediately_cancelled() {
2183 let server = test_server().await;
2185
2186 let id = make_eid("ns", "comp", "generate", 42);
2187
2188 let cancelled = server.cancel_instance_streams(&id).await;
2190 assert_eq!(cancelled, 0);
2191
2192 let (subject, provider) = register_and_get_subject(&server).await;
2194 let associated = server.associate_instance(&subject, None, &id).await;
2195
2196 assert!(
2198 !associated,
2199 "associate_instance on a tombstoned instance should return false"
2200 );
2201
2202 let result = provider.await;
2205 assert!(
2206 result.is_err(),
2207 "Late associate_instance on a tombstoned instance should immediately cancel"
2208 );
2209 }
2210
2211 #[tokio::test]
2212 async fn test_clear_tombstone_allows_new_associations() {
2213 let server = test_server().await;
2214
2215 let id = make_eid("ns", "comp", "generate", 42);
2216
2217 server.cancel_instance_streams(&id).await;
2218 server.clear_instance_tombstone(&id).await;
2219
2220 let (subject, _provider) = register_and_get_subject(&server).await;
2222 assert!(server.associate_instance(&subject, None, &id).await);
2223
2224 let cancelled = server.cancel_instance_streams(&id).await;
2226 assert_eq!(
2227 cancelled, 1,
2228 "After clearing tombstone, subjects should be tracked normally"
2229 );
2230 }
2231
2232 #[tokio::test]
2233 async fn test_cancel_does_not_affect_sibling_endpoint() {
2234 let server = test_server().await;
2237
2238 let (gen_subj, gen_prov) = register_and_get_subject(&server).await;
2239 let (pre_subj, pre_prov) = register_and_get_subject(&server).await;
2240
2241 let gen_id = make_eid("ns", "comp", "generate", 42);
2242 let pre_id = make_eid("ns", "comp", "prefill", 42);
2243
2244 assert!(server.associate_instance(&gen_subj, None, &gen_id).await);
2245 assert!(server.associate_instance(&pre_subj, None, &pre_id).await);
2246
2247 let cancelled = server.cancel_instance_streams(&gen_id).await;
2249 assert_eq!(
2250 cancelled, 1,
2251 "Only the generate subject should be cancelled"
2252 );
2253 assert!(gen_prov.await.is_err());
2254
2255 let still_pending = server.cancel_instance_streams(&pre_id).await;
2257 assert_eq!(still_pending, 1, "prefill subject should still be tracked");
2258 assert!(pre_prov.await.is_err());
2259 }
2260
2261 #[tokio::test]
2262 async fn test_tombstone_is_endpoint_scoped() {
2263 let server = test_server().await;
2266
2267 let gen_id = make_eid("ns", "comp", "generate", 42);
2268 let pre_id = make_eid("ns", "comp", "prefill", 42);
2269
2270 server.cancel_instance_streams(&gen_id).await;
2271
2272 let (gen_subj, gen_prov) = register_and_get_subject(&server).await;
2274 assert!(
2275 !server.associate_instance(&gen_subj, None, &gen_id).await,
2276 "generate should be tombstoned"
2277 );
2278 assert!(gen_prov.await.is_err());
2279
2280 let (pre_subj, _pre_prov) = register_and_get_subject(&server).await;
2282 assert!(
2283 server.associate_instance(&pre_subj, None, &pre_id).await,
2284 "prefill tombstone is independent; subject should be tracked"
2285 );
2286 let count = server.cancel_instance_streams(&pre_id).await;
2287 assert_eq!(count, 1, "prefill subject should be tracked normally");
2288 }
2289
2290 #[tokio::test]
2291 async fn test_cancel_does_not_affect_different_component() {
2292 let server = test_server().await;
2296
2297 let (subj_a, prov_a) = register_and_get_subject(&server).await;
2298 let (subj_b, prov_b) = register_and_get_subject(&server).await;
2299
2300 let id_a = make_eid("ns-a", "comp-a", "generate", 42);
2302 let id_b = make_eid("ns-b", "comp-b", "generate", 42);
2303
2304 assert!(server.associate_instance(&subj_a, None, &id_a).await);
2305 assert!(server.associate_instance(&subj_b, None, &id_b).await);
2306
2307 let cancelled = server.cancel_instance_streams(&id_a).await;
2309 assert_eq!(cancelled, 1, "Only service-A subject should be cancelled");
2310 assert!(prov_a.await.is_err());
2311
2312 let still_tracked = server.cancel_instance_streams(&id_b).await;
2314 assert_eq!(still_tracked, 1, "Service-B subject should be unaffected");
2315 assert!(prov_b.await.is_err());
2316 }
2317
2318 #[tokio::test(start_paused = true)]
2319 async fn test_tombstone_expires_after_ttl() {
2320 let server = test_server().await;
2324
2325 let id = make_eid("ns", "comp", "generate", 42);
2326
2327 server.cancel_instance_streams(&id).await;
2329 {
2330 let state = server.state.lock();
2331 assert!(state.removed_instances.contains_key(&id));
2332 }
2333
2334 tokio::time::advance(TOMBSTONE_TTL + Duration::from_secs(1)).await;
2336
2337 let (subject, _provider) = register_and_get_subject(&server).await;
2340 assert!(
2341 server.associate_instance(&subject, None, &id).await,
2342 "tombstone older than TTL should not block association"
2343 );
2344
2345 {
2348 let state = server.state.lock();
2349 assert!(
2350 !state.removed_instances.contains_key(&id),
2351 "expired tombstone should be pruned, not retained"
2352 );
2353 }
2354 }
2355
2356 #[tokio::test(start_paused = true)]
2357 async fn test_tombstone_within_ttl_blocks_associate() {
2358 let server = test_server().await;
2361
2362 let id = make_eid("ns", "comp", "generate", 42);
2363 server.cancel_instance_streams(&id).await;
2364
2365 tokio::time::advance(Duration::from_secs(1)).await;
2367
2368 let (subject, provider) = register_and_get_subject(&server).await;
2369 assert!(
2370 !server.associate_instance(&subject, None, &id).await,
2371 "tombstone within TTL must still block association"
2372 );
2373 assert!(provider.await.is_err());
2374 }
2375
2376 #[tokio::test(start_paused = true)]
2377 async fn test_tombstone_lazy_prune_on_cancel() {
2378 let server = test_server().await;
2381
2382 let id_old = make_eid("ns", "comp", "generate", 1);
2383 let id_new = make_eid("ns", "comp", "generate", 2);
2384
2385 server.cancel_instance_streams(&id_old).await;
2386 tokio::time::advance(TOMBSTONE_TTL + Duration::from_secs(1)).await;
2387 server.cancel_instance_streams(&id_new).await;
2388
2389 let state = server.state.lock();
2390 assert!(
2391 !state.removed_instances.contains_key(&id_old),
2392 "old tombstone should be pruned by the next cancel_instance_streams call"
2393 );
2394 assert!(
2395 state.removed_instances.contains_key(&id_new),
2396 "fresh tombstone should be retained"
2397 );
2398 assert_eq!(state.removed_instances.len(), 1);
2399 }
2400
2401 #[tokio::test]
2402 async fn test_clear_tombstone_only_affects_named_identity() {
2403 let server = test_server().await;
2408
2409 let id_a = make_eid("ns", "comp", "generate", 1);
2410 let id_b = make_eid("ns", "comp", "generate", 2);
2411
2412 server.cancel_instance_streams(&id_a).await;
2413 server.clear_instance_tombstone(&id_b).await;
2414
2415 let state = server.state.lock();
2416 assert!(
2417 state.removed_instances.contains_key(&id_a),
2418 "clearing a different identity must not remove id_a's tombstone"
2419 );
2420 }
2421
2422 #[tokio::test]
2423 async fn test_tombstone_scoped_to_full_identity() {
2424 let server = test_server().await;
2427
2428 let id_a = make_eid("ns-a", "comp-a", "generate", 42);
2429 let id_b = make_eid("ns-b", "comp-b", "generate", 42);
2430
2431 server.cancel_instance_streams(&id_a).await;
2433
2434 let (subj_a, prov_a) = register_and_get_subject(&server).await;
2436 assert!(!server.associate_instance(&subj_a, None, &id_a).await);
2437 assert!(prov_a.await.is_err());
2438
2439 let (subj_b, _prov_b) = register_and_get_subject(&server).await;
2441 assert!(
2442 server.associate_instance(&subj_b, None, &id_b).await,
2443 "Different namespace/component must not be tombstoned"
2444 );
2445 assert_eq!(server.cancel_instance_streams(&id_b).await, 1);
2446 }
2447
2448 type TestFramedRead = FramedRead<ReadHalf<TcpStream>, TwoPartCodec>;
2449 type TestFramedWrite = FramedWrite<WriteHalf<TcpStream>, TwoPartCodec>;
2450 type TestResponseStream = (TestFramedRead, TestFramedWrite, StreamReceiver);
2451
2452 async fn open_registered_response_stream() -> TestResponseStream {
2456 let options = ServerOptions::builder().port(0).build().unwrap();
2457 let server = TcpStreamServer::new_with_resolver(options, StubResolver::not_found())
2458 .await
2459 .unwrap();
2460 let context = Context::new(());
2461 let stream_options = StreamOptions::builder()
2462 .context(context.context())
2463 .enable_request_stream(false)
2464 .enable_response_stream(true)
2465 .build()
2466 .unwrap();
2467 let pending_connection = server.register(stream_options).await;
2468 let registered_stream = pending_connection.recv_stream.unwrap();
2469 let (connection_info, stream_provider) = registered_stream.into_parts();
2470 let tcp_info: TcpStreamConnectionInfo = connection_info.try_into().unwrap();
2471
2472 let stream = TcpStream::connect(&tcp_info.address).await.unwrap();
2473 let (read_half, write_half) = tokio::io::split(stream);
2474 let framed_reader = FramedRead::new(read_half, TwoPartCodec::default());
2475 let mut framed_writer = FramedWrite::new(write_half, TwoPartCodec::default());
2476
2477 let handshake = CallHomeHandshake {
2478 subject: tcp_info.subject,
2479 stream_type: StreamType::Response,
2480 };
2481 framed_writer
2482 .send(TwoPartMessage::from_header(
2483 serde_json::to_vec(&handshake).unwrap().into(),
2484 ))
2485 .await
2486 .unwrap();
2487 framed_writer
2488 .send(TwoPartMessage::from_header(
2489 serde_json::to_vec(&ResponseStreamPrologue {
2490 error: None,
2491 typed_error: None,
2492 })
2493 .unwrap()
2494 .into(),
2495 ))
2496 .await
2497 .unwrap();
2498
2499 let receiver = tokio::time::timeout(std::time::Duration::from_secs(1), stream_provider)
2502 .await
2503 .expect("server should establish response stream within timeout")
2504 .expect("stream provider should not be dropped")
2505 .expect("response stream should be accepted");
2506
2507 (framed_reader, framed_writer, receiver)
2508 }
2509
2510 async fn recv_control_message(framed_reader: &mut TestFramedRead) -> ControlMessage {
2511 let message = tokio::time::timeout(std::time::Duration::from_secs(1), framed_reader.next())
2514 .await
2515 .expect("server should send a control message within timeout")
2516 .expect("server should not close before sending control")
2517 .expect("control message should decode");
2518 let (header, data) = message.optional_parts();
2519 assert!(data.is_none(), "control message should not contain data");
2520 serde_json::from_slice(header.expect("control header missing").as_ref()).unwrap()
2521 }
2522
2523 #[tokio::test]
2528 async fn test_tcp_stream_server_sends_kill_on_unexpected_control_message() {
2529 let (mut framed_reader, mut framed_writer, _receiver) =
2530 open_registered_response_stream().await;
2531
2532 framed_writer
2533 .send(TwoPartMessage::from_header(
2534 serde_json::to_vec(&ControlMessage::Stop).unwrap().into(),
2535 ))
2536 .await
2537 .unwrap();
2538
2539 assert_eq!(
2540 recv_control_message(&mut framed_reader).await,
2541 ControlMessage::Kill,
2542 "unexpected control message should kill only this stream"
2543 );
2544 }
2545
2546 #[tokio::test]
2547 async fn test_tcp_stream_server_sends_kill_when_prologue_wait_is_cancelled() {
2548 let server = test_server().await;
2549 let context = Context::new(());
2550 let options = StreamOptions::builder()
2551 .context(context.context())
2552 .enable_request_stream(false)
2553 .enable_response_stream(true)
2554 .build()
2555 .unwrap();
2556 let pending = server.register(options).await;
2557 let registered_stream = pending.recv_stream.unwrap();
2558 let (connection_info, stream_provider) = registered_stream.into_parts();
2559 let tcp_info: TcpStreamConnectionInfo = connection_info.try_into().unwrap();
2560 let subject = tcp_info.subject.clone();
2561 let instance = make_eid("ns", "comp", "generate", 42);
2562 assert!(server.associate_instance(&subject, None, &instance).await);
2563
2564 let stream = TcpStream::connect(&tcp_info.address).await.unwrap();
2565 let (read_half, write_half) = tokio::io::split(stream);
2566 let mut framed_reader = FramedRead::new(read_half, TwoPartCodec::default());
2567 let mut framed_writer = FramedWrite::new(write_half, TwoPartCodec::default());
2568 let handshake = CallHomeHandshake {
2569 subject: subject.clone(),
2570 stream_type: StreamType::Response,
2571 };
2572 framed_writer
2573 .send(TwoPartMessage::from_header(
2574 serde_json::to_vec(&handshake).unwrap().into(),
2575 ))
2576 .await
2577 .unwrap();
2578
2579 tokio::time::timeout(std::time::Duration::from_secs(1), async {
2580 while server.state.lock().rx_subjects.contains_key(&subject) {
2581 tokio::task::yield_now().await;
2582 }
2583 })
2584 .await
2585 .expect("server did not accept response call-home");
2586
2587 assert_eq!(server.cancel_instance_streams(&instance).await, 1);
2588 assert!(
2589 time::timeout(Duration::from_secs(1), stream_provider)
2590 .await
2591 .expect("worker removal must unblock the response handshake")
2592 .is_err()
2593 );
2594 assert_eq!(
2595 recv_control_message(&mut framed_reader).await,
2596 ControlMessage::Kill,
2597 "cancelling the prologue wait should stop worker generation"
2598 );
2599 let state = server.state.lock();
2600 assert!(!state.rx_subjects.contains_key(&subject));
2601 assert!(!state.rx_cancellations.contains_key(&subject));
2602 assert!(!state.subject_instance.contains_key(&subject));
2603 assert!(!state.instance_subjects.contains_key(&instance));
2604 }
2605
2606 #[tokio::test]
2609 async fn response_prologue_acceptance_is_ordered_with_worker_removal() {
2610 let server = test_server().await;
2611 for removal_first in [true, false] {
2612 let instance = make_eid("ns", "comp", "generate", u64::from(removal_first));
2613 let (subject, _provider) = register_and_get_subject(&server).await;
2614 assert!(server.associate_instance(&subject, None, &instance).await);
2615 let pending = TcpStreamServer::take_response_stream(&server.state, &subject).unwrap();
2616 let guard = ResponseRegistrationGuard {
2617 state: server.state.clone(),
2618 subject: subject.clone(),
2619 is_active: true,
2620 };
2621
2622 if removal_first {
2623 assert_eq!(server.cancel_instance_streams(&instance).await, 1);
2624 assert!(
2625 !guard.accept(),
2626 "a cancelled handshake must not be accepted"
2627 );
2628 assert!(pending.cancellation.is_cancelled());
2629 } else {
2630 assert!(guard.accept());
2631 assert_eq!(server.cancel_instance_streams(&instance).await, 0);
2632 assert!(!pending.cancellation.is_cancelled());
2633 }
2634
2635 let state = server.state.lock();
2636 assert!(!state.rx_cancellations.contains_key(&subject));
2637 assert!(!state.subject_instance.contains_key(&subject));
2638 assert!(!state.instance_subjects.contains_key(&instance));
2639 }
2640 }
2641
2642 #[tokio::test(start_paused = true)]
2645 async fn cancelled_handshake_unblocks_before_stalled_cleanup() {
2646 let server = test_server().await;
2647 let instance = make_eid("ns", "comp", "generate", 42);
2648 let (subject, provider) = register_and_get_subject(&server).await;
2649 assert!(server.associate_instance(&subject, None, &instance).await);
2650 let pending = TcpStreamServer::take_response_stream(&server.state, &subject).unwrap();
2651 let guard = ResponseRegistrationGuard {
2652 state: server.state.clone(),
2653 subject: subject.clone(),
2654 is_active: true,
2655 };
2656 let (socket, _peer) = tokio::io::duplex(1);
2658 let writer = FramedWrite::new(Box::new(socket) as BoxWrite, TwoPartCodec::default());
2659 let cleanup = tokio::spawn(cancel_response_handshake(
2660 pending.connection,
2661 Some(guard),
2662 writer,
2663 ));
2664
2665 assert!(
2666 time::timeout(Duration::from_millis(50), provider)
2667 .await
2668 .expect("requester must be released before network cleanup")
2669 .is_err()
2670 );
2671 assert!(
2672 !cleanup.is_finished(),
2673 "peer should still be blocking the Kill write"
2674 );
2675 {
2676 let state = server.state.lock();
2677 assert!(!state.rx_cancellations.contains_key(&subject));
2678 assert!(!state.subject_instance.contains_key(&subject));
2679 assert!(!state.instance_subjects.contains_key(&instance));
2680 }
2681 time::advance(HANDSHAKE_CANCEL_CLEANUP_TIMEOUT).await;
2682 time::timeout(Duration::from_millis(50), cleanup)
2683 .await
2684 .expect("network cleanup must stop at its deadline")
2685 .unwrap();
2686 }
2687
2688 #[tokio::test]
2692 async fn test_tcp_stream_server_sends_kill_on_read_error() {
2693 let (mut framed_reader, framed_writer, _receiver) = open_registered_response_stream().await;
2694
2695 let mut raw_writer = framed_writer.into_inner();
2696 raw_writer.write_all(&[0u8; 8]).await.unwrap();
2697 raw_writer.shutdown().await.unwrap();
2698
2699 assert_eq!(
2700 recv_control_message(&mut framed_reader).await,
2701 ControlMessage::Kill,
2702 "framing read error should kill only this stream"
2703 );
2704 }
2705
2706 #[tokio::test]
2709 async fn test_tcp_stream_server_sends_kill_on_sentinel_with_data() {
2710 let (mut framed_reader, mut framed_writer, _receiver) =
2711 open_registered_response_stream().await;
2712
2713 let header = serde_json::to_vec(&ControlMessage::Sentinel)
2714 .unwrap()
2715 .into();
2716 framed_writer
2717 .send(TwoPartMessage::from_parts(
2718 header,
2719 Bytes::from_static(b"unexpected payload"),
2720 ))
2721 .await
2722 .unwrap();
2723
2724 assert_eq!(
2725 recv_control_message(&mut framed_reader).await,
2726 ControlMessage::Kill,
2727 "Sentinel with data should kill only this stream"
2728 );
2729 }
2730
2731 #[tokio::test]
2735 async fn test_tcp_stream_server_returns_error_on_invalid_prologue() {
2736 let options = ServerOptions::builder().port(0).build().unwrap();
2737 let server = TcpStreamServer::new_with_resolver(options, StubResolver::not_found())
2738 .await
2739 .unwrap();
2740 let context = Context::new(());
2741 let stream_options = StreamOptions::builder()
2742 .context(context.context())
2743 .enable_request_stream(false)
2744 .enable_response_stream(true)
2745 .build()
2746 .unwrap();
2747 let pending_connection = server.register(stream_options).await;
2748 let registered_stream = pending_connection.recv_stream.unwrap();
2749 let (connection_info, stream_provider) = registered_stream.into_parts();
2750 let tcp_info: TcpStreamConnectionInfo = connection_info.try_into().unwrap();
2751
2752 let stream = TcpStream::connect(&tcp_info.address).await.unwrap();
2753 let (_read_half, write_half) = tokio::io::split(stream);
2754 let mut framed_writer = FramedWrite::new(write_half, TwoPartCodec::default());
2755
2756 let handshake = CallHomeHandshake {
2757 subject: tcp_info.subject,
2758 stream_type: StreamType::Response,
2759 };
2760 framed_writer
2761 .send(TwoPartMessage::from_header(
2762 serde_json::to_vec(&handshake).unwrap().into(),
2763 ))
2764 .await
2765 .unwrap();
2766
2767 framed_writer
2769 .send(TwoPartMessage::from_data(Bytes::from_static(
2770 b"not a prologue",
2771 )))
2772 .await
2773 .unwrap();
2774
2775 let outcome = tokio::time::timeout(std::time::Duration::from_secs(1), stream_provider)
2776 .await
2777 .expect("stream provider should resolve quickly")
2778 .expect("stream provider channel should not be dropped");
2779 match outcome {
2781 Err(err) => assert!(
2782 err.contains("malformed prologue"),
2783 "expected malformed-prologue error, got: {err}"
2784 ),
2785 Ok(_) => panic!("invalid prologue should produce an error, but got Ok"),
2786 }
2787 }
2788
2789 #[tokio::test]
2792 async fn test_unknown_typed_error_preserves_the_legacy_prologue_error() {
2793 let options = ServerOptions::builder().port(0).build().unwrap();
2794 let server = TcpStreamServer::new_with_resolver(options, StubResolver::not_found())
2795 .await
2796 .unwrap();
2797 let context = Context::new(());
2798 let stream_options = StreamOptions::builder()
2799 .context(context.context())
2800 .enable_request_stream(false)
2801 .enable_response_stream(true)
2802 .build()
2803 .unwrap();
2804 let pending_connection = server.register(stream_options).await;
2805 let (connection_info, stream_provider) =
2806 pending_connection.recv_stream.unwrap().into_parts();
2807 let tcp_info: TcpStreamConnectionInfo = connection_info.try_into().unwrap();
2808
2809 let stream = TcpStream::connect(&tcp_info.address).await.unwrap();
2810 let (_read_half, write_half) = tokio::io::split(stream);
2811 let mut framed_writer = FramedWrite::new(write_half, TwoPartCodec::default());
2812
2813 let handshake = CallHomeHandshake {
2814 subject: tcp_info.subject,
2815 stream_type: StreamType::Response,
2816 };
2817 framed_writer
2818 .send(TwoPartMessage::from_header(
2819 serde_json::to_vec(&handshake).unwrap().into(),
2820 ))
2821 .await
2822 .unwrap();
2823
2824 framed_writer
2827 .send(TwoPartMessage::from_header(Bytes::from_static(
2828 br#"{"error":"Generate Error: boom","typed_error":{"error_type":"VariantFromTheFuture","message":"boom"}}"#,
2829 )))
2830 .await
2831 .unwrap();
2832
2833 let outcome = tokio::time::timeout(std::time::Duration::from_secs(5), stream_provider)
2834 .await
2835 .expect("stream provider should resolve quickly")
2836 .expect("the oneshot must be notified, not dropped");
2837
2838 match outcome {
2840 Err(err) => {
2841 assert_eq!(err.message, "Generate Error: boom");
2842 assert!(err.typed_error.is_none());
2843 }
2844 Ok(_) => panic!("an error prologue must not yield a usable stream"),
2845 }
2846 }
2847
2848 #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
2849 async fn test_concurrent_response_registration_and_call_home() {
2850 const STREAMS: usize = 128;
2851
2852 let result = time::timeout(Duration::from_secs(20), async {
2853 let server = test_server().await;
2854 let mut pending_streams = Vec::with_capacity(STREAMS);
2855 let mut client_tasks = Vec::with_capacity(STREAMS);
2856
2857 for idx in 0..STREAMS {
2858 let context = Context::new(());
2859 let options = StreamOptions::builder()
2860 .context(context.context())
2861 .enable_request_stream(false)
2862 .enable_response_stream(true)
2863 .build()
2864 .unwrap();
2865
2866 let pending = server.register(options).await;
2867 let registered_stream = pending.recv_stream.unwrap();
2868 let (connection_info, stream_provider) = registered_stream.into_parts();
2869 let client_context =
2870 Context::with_id_and_metadata((), context.id().to_string(), Default::default());
2871 let payload = Bytes::from(format!("payload-{idx}"));
2872
2873 pending_streams.push((idx, payload.clone(), stream_provider));
2874 client_tasks.push(tokio::spawn(async move {
2875 let mut sender = TcpClient::create_response_stream(
2876 client_context.context(),
2877 connection_info,
2878 None,
2879 )
2880 .await
2881 .unwrap();
2882 sender.send_prologue(None).await.unwrap();
2883 sender.send(payload).await.unwrap();
2884 }));
2885 }
2886
2887 for task in client_tasks {
2888 task.await.unwrap();
2889 }
2890
2891 for (idx, expected, stream_provider) in pending_streams {
2892 let mut stream = stream_provider.await.unwrap().unwrap();
2893 let actual = stream.rx.recv().await.unwrap();
2894 assert_eq!(actual, expected, "payload mismatch for stream {idx}");
2895 }
2896 })
2897 .await;
2898
2899 assert!(
2900 result.is_ok(),
2901 "concurrent response registration and call-home timed out"
2902 );
2903 }
2904
2905 #[tokio::test]
2906 async fn test_response_stream_cancellation_before_prologue() {
2907 temp_env::async_with_vars(
2908 [
2909 ("DYN_TCP_TLS_CERT_PATH", None::<&str>),
2910 ("DYN_TCP_TLS_KEY_PATH", None),
2911 ("DYN_TCP_TLS_CLIENT_CA_CERT_PATH", None),
2912 ("DYN_TCP_TLS_CA_CERT_PATH", None),
2913 ("DYN_TCP_TLS_INSECURE", None),
2914 ("DYN_TCP_TLS_CLIENT_CERT_PATH", None),
2915 ("DYN_TCP_TLS_CLIENT_KEY_PATH", None),
2916 ],
2917 async {
2918 let server = test_server().await;
2919 for action in [Some(ControlMessage::Stop), Some(ControlMessage::Kill), None] {
2920 tokio::time::timeout(Duration::from_secs(5), async {
2921 let context = Context::new(()).context();
2922 let options = StreamOptions::builder()
2923 .context(context.clone())
2924 .enable_request_stream(false)
2925 .enable_response_stream(true)
2926 .build()
2927 .unwrap();
2928 let pending = server.register(options).await;
2929 let (connection_info, stream_provider) =
2930 pending.recv_stream.unwrap().into_parts();
2931 let worker_context = Context::with_id_and_metadata(
2932 (),
2933 context.id().to_string(),
2934 Default::default(),
2935 )
2936 .context();
2937 let sender = TcpClient::create_response_stream(
2938 worker_context.clone(),
2939 connection_info,
2940 None,
2941 )
2942 .await
2943 .unwrap();
2944
2945 let stream_provider = match action {
2946 Some(ControlMessage::Stop) => {
2947 context.stop_generating();
2948 Some(stream_provider)
2949 }
2950 Some(ControlMessage::Kill) => {
2951 context.kill();
2952 Some(stream_provider)
2953 }
2954 None => {
2955 drop(stream_provider);
2956 assert!(!context.is_stopped());
2957 None
2958 }
2959 Some(ControlMessage::Sentinel) => unreachable!(),
2960 };
2961 if let Some(stream_provider) = stream_provider {
2962 let failure = match stream_provider.await.unwrap() {
2963 Err(failure) => failure,
2964 Ok(_) => {
2965 panic!("cancellation must not establish a response stream")
2966 }
2967 };
2968 assert_eq!(
2969 failure.typed_error.as_ref().map(|error| error.error_type()),
2970 Some(ErrorType::Cancelled)
2971 );
2972 }
2973 worker_context.stopped().await;
2974 sender.tx.closed().await;
2975 assert_eq!(
2976 worker_context.is_killed(),
2977 action != Some(ControlMessage::Stop)
2978 );
2979 })
2980 .await
2981 .unwrap_or_else(|_| {
2982 panic!("cancellation before prologue timed out: {action:?}")
2983 });
2984 }
2985 },
2986 )
2987 .await;
2988 }
2989
2990 #[tokio::test]
2993 async fn test_typed_prologue_error_survives_to_requester() {
2994 let server = test_server().await;
2995 let context = Context::new(());
2996 let options = StreamOptions::builder()
2997 .context(context.context())
2998 .enable_request_stream(false)
2999 .enable_response_stream(true)
3000 .build()
3001 .unwrap();
3002
3003 let pending = server.register(options).await;
3004 let (connection_info, stream_provider) = pending.recv_stream.unwrap().into_parts();
3005 let client_context =
3006 Context::with_id_and_metadata((), context.id().to_string(), Default::default());
3007
3008 let worker_error = DynamoError::builder()
3009 .error_type(ErrorType::Backend(BackendError::InvalidArgument))
3010 .message("multimodal input is not supported by this backend")
3011 .build();
3012
3013 let mut sender =
3014 TcpClient::create_response_stream(client_context.context(), connection_info, None)
3015 .await
3016 .unwrap();
3017 sender
3018 .send_prologue_typed(Some(StreamPrologueError::new(
3019 "Generate Error: multimodal input is not supported by this backend",
3020 worker_error,
3021 )))
3022 .await
3023 .unwrap();
3024
3025 let outcome = tokio::time::timeout(std::time::Duration::from_secs(5), stream_provider)
3026 .await
3027 .expect("stream provider should resolve quickly")
3028 .expect("stream provider channel should not be dropped");
3029
3030 let prologue_error = match outcome {
3032 Err(err) => err,
3033 Ok(_) => panic!("an error prologue must not yield a usable stream"),
3034 };
3035 assert_eq!(
3036 prologue_error.typed_error.as_ref().map(|e| e.error_type()),
3037 Some(ErrorType::Backend(BackendError::InvalidArgument)),
3038 "the worker's error type must survive the prologue round trip"
3039 );
3040 }
3041
3042 use futures::SinkExt;
3050
3051 async fn register_and_dial_request_stream(
3056 server: &TcpStreamServer,
3057 ) -> (
3058 FramedRead<tokio::io::ReadHalf<TcpStream>, TwoPartCodec>,
3059 super::StreamSender,
3060 Arc<dyn AsyncEngineContext>,
3061 ) {
3062 let upstream_ctx = Context::new(()).context();
3063 let options = StreamOptions::builder()
3064 .context(upstream_ctx.clone())
3065 .enable_request_stream(true)
3066 .enable_response_stream(false)
3067 .build()
3068 .unwrap();
3069
3070 let pending = server.register(options).await;
3071 let send_stream = pending.send_stream.unwrap();
3072 let (conn_info, send_provider) = send_stream.into_parts();
3073 let tcp_info: TcpStreamConnectionInfo = conn_info.try_into().unwrap();
3074
3075 let raw = TcpStream::connect(&tcp_info.address).await.unwrap();
3076 let (read_half, write_half) = tokio::io::split(raw);
3077 let framed_reader = FramedRead::new(read_half, TwoPartCodec::default());
3078 let mut framed_writer = FramedWrite::new(write_half, TwoPartCodec::default());
3079
3080 let handshake = super::CallHomeHandshake {
3081 subject: tcp_info.subject.clone(),
3082 stream_type: StreamType::Request,
3083 };
3084 let handshake_bytes = serde_json::to_vec(&handshake).unwrap();
3085 framed_writer
3086 .send(TwoPartMessage::from_header(handshake_bytes.into()))
3087 .await
3088 .unwrap();
3089 drop(framed_writer);
3090
3091 let sender = send_provider.await.unwrap().unwrap();
3092 (framed_reader, sender, upstream_ctx)
3093 }
3094
3095 async fn next_control_message(
3098 reader: &mut FramedRead<tokio::io::ReadHalf<TcpStream>, TwoPartCodec>,
3099 ) -> ControlMessage {
3100 loop {
3101 let frame = reader
3102 .next()
3103 .await
3104 .expect("socket closed before control message arrived")
3105 .expect("decode error");
3106 if let Some(header) = frame.header() {
3107 return serde_json::from_slice::<ControlMessage>(header)
3108 .expect("invalid control message bytes");
3109 }
3110 }
3112 }
3113
3114 #[tokio::test]
3117 async fn test_request_stream_sends_sentinel_on_clean_drop() {
3118 let server = test_server().await;
3119 let (mut reader, sender, _ctx) = register_and_dial_request_stream(&server).await;
3120
3121 drop(sender);
3122
3123 let ctrl = next_control_message(&mut reader).await;
3124 assert!(
3125 matches!(ctrl, ControlMessage::Sentinel),
3126 "clean drain should emit Sentinel, got {ctrl:?}"
3127 );
3128 }
3129
3130 #[tokio::test]
3133 async fn test_request_stream_sends_kill_on_context_killed() {
3134 let server = test_server().await;
3135 let (mut reader, _sender, ctx) = register_and_dial_request_stream(&server).await;
3136
3137 ctx.kill();
3138
3139 let ctrl = next_control_message(&mut reader).await;
3140 assert!(
3141 matches!(ctrl, ControlMessage::Kill),
3142 "context.kill() should emit Kill, got {ctrl:?}"
3143 );
3144 }
3145
3146 #[tokio::test]
3149 async fn test_request_stream_sends_stop_on_context_stopped() {
3150 let server = test_server().await;
3151 let (mut reader, _sender, ctx) = register_and_dial_request_stream(&server).await;
3152
3153 ctx.stop();
3154
3155 let ctrl = next_control_message(&mut reader).await;
3156 assert!(
3157 matches!(ctrl, ControlMessage::Stop),
3158 "context.stop() should emit Stop, got {ctrl:?}"
3159 );
3160 }
3161
3162 #[cfg(unix)]
3165 fn emfile_error() -> std::io::Error {
3166 std::io::Error::from_raw_os_error(libc::EMFILE)
3167 }
3168
3169 #[cfg(unix)]
3170 #[test]
3171 fn accept_backoff_classifies_all_exhaustion_errnos() {
3172 for errno in [libc::EMFILE, libc::ENFILE, libc::ENOBUFS, libc::ENOMEM] {
3173 let err = std::io::Error::from_raw_os_error(errno);
3174 assert_eq!(
3175 AcceptBackoff::classify(&err),
3176 AcceptFailure::Exhaustion,
3177 "errno {errno} must classify as exhaustion"
3178 );
3179 }
3180 let ordinary = std::io::Error::from(std::io::ErrorKind::ConnectionAborted);
3182 assert_eq!(AcceptBackoff::classify(&ordinary), AcceptFailure::Ordinary,);
3183 }
3184
3185 #[cfg(unix)]
3186 #[test]
3187 fn accept_backoff_grows_resets_and_rate_limits() {
3188 let mut backoff = AcceptBackoff::default();
3189 let now = std::time::Instant::now();
3190
3191 let delays: Vec<Duration> = (0..12)
3193 .map(|_| backoff.record_exhaustion(now).delay)
3194 .collect();
3195 assert!(delays[0] > Duration::ZERO);
3196 let sat = delays
3197 .iter()
3198 .position(|d| *d == ACCEPT_BACKOFF_MAX_DELAY)
3199 .expect("delay reaches the ceiling");
3200 for pair in delays[..=sat].windows(2) {
3201 assert!(
3202 pair[1] > pair[0],
3203 "delay must grow: {:?} -> {:?}",
3204 pair[0],
3205 pair[1]
3206 );
3207 }
3208 assert!(delays[sat..].iter().all(|d| *d == ACCEPT_BACKOFF_MAX_DELAY));
3209
3210 backoff.record_success(|| now);
3212 assert_eq!(
3213 backoff.record_exhaustion(now).delay,
3214 ACCEPT_BACKOFF_INITIAL_DELAY,
3215 );
3216
3217 let t0 = std::time::Instant::now();
3220 let mut backoff = AcceptBackoff::default();
3221 let emitted: Vec<Option<u64>> = (0..50)
3222 .map(|_| backoff.record_exhaustion(t0).log_suppressed)
3223 .collect();
3224 assert_eq!(emitted.iter().filter(|e| e.is_some()).count(), 1);
3225 assert_eq!(emitted[0], Some(0));
3226
3227 let t1 = t0 + ACCEPT_BACKOFF_LOG_INTERVAL + Duration::from_millis(1);
3228 assert_eq!(
3229 backoff.record_exhaustion(t1).log_suppressed,
3230 Some(49),
3231 "next emission reports every failure suppressed since the last one",
3232 );
3233
3234 let mut backoff = AcceptBackoff::default();
3237 let t0 = std::time::Instant::now();
3238 assert_eq!(backoff.record_exhaustion(t0).log_suppressed, Some(0));
3239 backoff.record_exhaustion(t0);
3240 backoff.record_exhaustion(t0);
3241 assert_eq!(
3242 backoff.record_success(|| t0),
3243 None,
3244 "recovery inside the log window must not emit",
3245 );
3246 backoff.record_exhaustion(t0);
3247 assert_eq!(
3248 backoff.record_success(|| t0 + ACCEPT_BACKOFF_LOG_INTERVAL),
3249 Some(3),
3250 "after the window elapses the recovery emits with the rolled-forward count",
3251 );
3252 }
3253
3254 #[cfg(unix)]
3255 #[tokio::test]
3256 async fn accept_backoff_socket_recovers_after_injected_exhaustion() {
3257 use crate::metrics::transport_metrics::TCP_ACCEPT_BACKOFF_TOTAL;
3258
3259 let listener = tokio::net::TcpListener::bind("127.0.0.1:0")
3260 .await
3261 .expect("bind ephemeral listener");
3262 let addr = listener.local_addr().expect("local_addr");
3263
3264 let mut backoff = AcceptBackoff::default();
3265 let counter_before = TCP_ACCEPT_BACKOFF_TOTAL.get();
3266
3267 let started = std::time::Instant::now();
3268 let mut expected_total = Duration::ZERO;
3269 for _ in 0..3 {
3270 expected_total += handle_accept_error(&emfile_error(), &mut backoff).await;
3271 }
3272 assert!(expected_total >= ACCEPT_BACKOFF_INITIAL_DELAY * 3);
3273 assert!(
3274 started.elapsed() >= expected_total,
3275 "the accept loop must sleep, not spin; elapsed {:?}",
3276 started.elapsed(),
3277 );
3278 assert!(TCP_ACCEPT_BACKOFF_TOTAL.get() >= counter_before + 3.0);
3279
3280 let client = tokio::spawn(async move { tokio::net::TcpStream::connect(addr).await });
3281 let (accepted, _peer) = tokio::time::timeout(Duration::from_secs(5), listener.accept())
3282 .await
3283 .expect("listener should still accept after backing off")
3284 .expect("accept should succeed");
3285 let _client = client.await.expect("client task").expect("client connect");
3286 drop(accepted);
3287
3288 backoff.record_success(std::time::Instant::now);
3289 assert_eq!(
3290 backoff.record_exhaustion(std::time::Instant::now()).delay,
3291 ACCEPT_BACKOFF_INITIAL_DELAY,
3292 );
3293 }
3294}