1use super::connection::{CloseReason, ConnectionInfo, ConnectionProtocol, ConnectionState};
15use super::origin::OriginKey;
16use super::partition::PartitionId;
17use super::stats::CellConnectionStats;
18use crate::sync::Arc as PoolArc;
19use aws_smithy_async::time::SharedTimeSource;
20use aws_smithy_runtime_api::client::result::ConnectorError;
21use std::error::Error;
22use std::fmt;
23use std::net::SocketAddr;
24use std::sync::atomic::{AtomicU64, Ordering};
25use std::sync::Arc;
26use std::time::{Duration, SystemTime};
27
28pub trait ConnectionEventListener: Send + Sync + 'static {
36 fn on_event(&self, event: &ConnectionEvent<'_>);
38}
39
40impl<F> ConnectionEventListener for F
41where
42 F: for<'a> Fn(&ConnectionEvent<'a>) + Send + Sync + 'static,
43{
44 fn on_event(&self, event: &ConnectionEvent<'_>) {
45 self(event);
46 }
47}
48
49#[derive(Clone)]
54pub struct SharedConnectionEventListener(Arc<dyn ConnectionEventListener>);
55
56impl SharedConnectionEventListener {
57 pub fn new(listener: impl ConnectionEventListener) -> Self {
59 Self(Arc::new(listener))
60 }
61
62 fn notify(&self, event: &ConnectionEvent<'_>) {
64 #[cfg(panic = "unwind")]
65 if std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| self.on_event(event))).is_err()
66 {
67 tracing::warn!("connection event listener panicked");
68 }
69
70 #[cfg(not(panic = "unwind"))]
71 self.on_event(event);
72 }
73
74 pub(super) fn logical_close(
76 &self,
77 connection: &PoolArc<ConnectionInfo>,
78 cause: LogicalCloseCause,
79 ) {
80 self.notify(&ConnectionEvent::LogicalClose(ConnectionLogicalClose {
81 connection,
82 cause,
83 }));
84 }
85
86 pub(super) fn physical_close(&self, connection: &PoolArc<ConnectionInfo>, reason: CloseReason) {
88 self.notify(&ConnectionEvent::PhysicalClose(ConnectionPhysicalClose {
89 connection,
90 reason,
91 }));
92 }
93}
94
95impl ConnectionEventListener for SharedConnectionEventListener {
96 fn on_event(&self, event: &ConnectionEvent<'_>) {
97 self.0.on_event(event);
98 }
99}
100
101impl fmt::Debug for SharedConnectionEventListener {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 f.write_str("SharedConnectionEventListener")
104 }
105}
106
107#[derive(Debug)]
112#[non_exhaustive]
113pub enum ConnectionEvent<'a> {
114 EstablishmentFailed(ConnectionEstablishmentFailed<'a>),
116 Opened(ConnectionOpened<'a>),
118 LogicalClose(ConnectionLogicalClose<'a>),
120 PhysicalClose(ConnectionPhysicalClose<'a>),
122}
123
124#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
129pub struct ConnectionEstablishmentId(u64);
130
131impl fmt::Display for ConnectionEstablishmentId {
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 self.0.fmt(f)
134 }
135}
136
137#[derive(Debug)]
142pub struct ConnectionEstablishmentInfo {
143 id: ConnectionEstablishmentId,
144 origin: OriginKey,
145 partition: PartitionId,
146}
147
148impl ConnectionEstablishmentInfo {
149 pub fn id(&self) -> ConnectionEstablishmentId {
151 self.id
152 }
153
154 pub fn origin(&self) -> &OriginKey {
156 &self.origin
157 }
158
159 pub fn partition(&self) -> PartitionId {
161 self.partition
162 }
163}
164
165#[derive(Clone, Copy, Debug)]
170#[non_exhaustive]
171pub struct ConnectionEstablishmentStats {
172 total_duration: Duration,
173 transport_duration: Duration,
174 protocol_handshake_duration: Option<Duration>,
175}
176
177impl ConnectionEstablishmentStats {
178 pub fn total_duration(&self) -> Duration {
183 self.total_duration
184 }
185
186 pub fn transport_duration(&self) -> Duration {
191 self.transport_duration
192 }
193
194 pub fn protocol_handshake_duration(&self) -> Option<Duration> {
198 self.protocol_handshake_duration
199 }
200
201 fn connection_metadata(
202 &self,
203 ) -> aws_smithy_runtime_api::client::connection::ConnectionEstablishmentMetadata {
204 let mut builder =
205 aws_smithy_runtime_api::client::connection::ConnectionEstablishmentMetadata::builder()
206 .total_duration(self.total_duration)
207 .transport_duration(self.transport_duration);
208 builder.set_protocol_handshake_duration(self.protocol_handshake_duration);
209 builder.build()
210 }
211}
212
213#[derive(Clone, Copy, Debug, Eq, PartialEq)]
215#[non_exhaustive]
216pub enum ConnectionEstablishmentStage {
217 Transport,
219 ProtocolSelection,
221 ProtocolHandshake,
223 PoolInstallation,
225}
226
227#[derive(Debug)]
229pub struct ConnectionEstablishmentFailed<'a> {
230 establishment: &'a ConnectionEstablishmentInfo,
231 stats: &'a ConnectionEstablishmentStats,
232 stage: ConnectionEstablishmentStage,
233 remote_addr: Option<SocketAddr>,
234 protocol: Option<ConnectionProtocol>,
235 error: &'a (dyn Error + Send + Sync),
236}
237
238impl<'a> ConnectionEstablishmentFailed<'a> {
239 pub fn establishment(&self) -> &'a ConnectionEstablishmentInfo {
241 self.establishment
242 }
243
244 pub fn stats(&self) -> &'a ConnectionEstablishmentStats {
246 self.stats
247 }
248
249 pub fn stage(&self) -> ConnectionEstablishmentStage {
251 self.stage
252 }
253
254 pub fn remote_addr(&self) -> Option<SocketAddr> {
257 self.remote_addr
258 }
259
260 pub fn protocol(&self) -> Option<ConnectionProtocol> {
262 self.protocol
263 }
264
265 pub fn error(&self) -> &'a (dyn Error + Send + Sync) {
267 self.error
268 }
269}
270
271#[derive(Debug)]
273pub struct ConnectionOpened<'a> {
274 establishment: &'a ConnectionEstablishmentInfo,
275 stats: &'a ConnectionEstablishmentStats,
276 connection: &'a PoolArc<ConnectionInfo>,
277}
278
279impl<'a> ConnectionOpened<'a> {
280 pub fn establishment(&self) -> &'a ConnectionEstablishmentInfo {
282 self.establishment
283 }
284
285 pub fn stats(&self) -> &'a ConnectionEstablishmentStats {
287 self.stats
288 }
289
290 pub fn connection(&self) -> &'a PoolArc<ConnectionInfo> {
292 self.connection
293 }
294}
295
296#[derive(Clone, Copy, Debug, Eq, PartialEq)]
300#[non_exhaustive]
301pub enum LogicalCloseCause {
302 IdleTimeout,
304 Poisoned,
306 ProtocolEnded,
308 IncompleteH1Exchange,
310 Reclaimed,
312 PoolDropped,
314 OwnerRuntimeShutdown,
316}
317
318impl LogicalCloseCause {
319 pub(super) fn from_reason(reason: CloseReason) -> Self {
321 match reason {
322 CloseReason::IdleTimeout => Self::IdleTimeout,
323 CloseReason::Poisoned => Self::Poisoned,
324 CloseReason::ProtocolClosed | CloseReason::Upgraded => Self::ProtocolEnded,
325 CloseReason::IncompleteH1Exchange => Self::IncompleteH1Exchange,
326 CloseReason::Reclaimed => Self::Reclaimed,
327 CloseReason::PoolDropped => Self::PoolDropped,
328 CloseReason::OwnerRuntimeShutdown => Self::OwnerRuntimeShutdown,
329 }
330 }
331}
332
333#[derive(Debug)]
338pub struct ConnectionLogicalClose<'a> {
339 connection: &'a PoolArc<ConnectionInfo>,
340 cause: LogicalCloseCause,
341}
342
343impl<'a> ConnectionLogicalClose<'a> {
344 pub fn connection(&self) -> &'a PoolArc<ConnectionInfo> {
346 self.connection
347 }
348
349 pub fn cause(&self) -> LogicalCloseCause {
351 self.cause
352 }
353}
354
355#[derive(Debug)]
360pub struct ConnectionPhysicalClose<'a> {
361 connection: &'a PoolArc<ConnectionInfo>,
362 reason: CloseReason,
363}
364
365impl<'a> ConnectionPhysicalClose<'a> {
366 pub fn connection(&self) -> &'a PoolArc<ConnectionInfo> {
368 self.connection
369 }
370
371 pub fn reason(&self) -> CloseReason {
373 self.reason
374 }
375}
376
377#[derive(Debug)]
383pub(super) struct ConnectionEvents {
384 listener: Option<SharedConnectionEventListener>,
385 time_source: SharedTimeSource,
386 next_establishment_id: AtomicU64,
387}
388
389impl ConnectionEvents {
390 pub(super) fn new(
392 listener: Option<SharedConnectionEventListener>,
393 time_source: SharedTimeSource,
394 ) -> Self {
395 Self {
396 listener,
397 time_source,
398 next_establishment_id: AtomicU64::new(0),
399 }
400 }
401
402 pub(super) fn establishment_started(
404 &self,
405 origin: &OriginKey,
406 partition: PartitionId,
407 connection_stats: PoolArc<CellConnectionStats>,
408 ) -> ConnectionEstablishment {
409 connection_stats.establishment_started();
410 let timing = EstablishmentTiming {
411 time_source: self.time_source.clone(),
412 started_at: self.time_source.now(),
413 protocol_handshake_started_at: None,
414 transport_duration: None,
415 protocol_handshake_duration: None,
416 };
417 let observation = self
418 .listener
419 .as_ref()
420 .map(|listener| EstablishmentObservation {
421 listener: listener.clone(),
422 info: ConnectionEstablishmentInfo {
423 id: ConnectionEstablishmentId(
424 self.next_establishment_id.fetch_add(1, Ordering::Relaxed),
425 ),
426 origin: origin.clone(),
427 partition,
428 },
429 stage: ConnectionEstablishmentStage::Transport,
430 remote_addr: None,
431 protocol: None,
432 });
433 ConnectionEstablishment {
434 timing,
435 observation,
436 successful_stats: None,
437 connection_stats: Some(connection_stats),
438 }
439 }
440}
441
442pub(super) struct ConnectionEstablishment {
444 timing: EstablishmentTiming,
445 observation: Option<EstablishmentObservation>,
446 successful_stats: Option<ConnectionEstablishmentStats>,
447 connection_stats: Option<PoolArc<CellConnectionStats>>,
448}
449
450struct EstablishmentTiming {
452 time_source: SharedTimeSource,
453 started_at: SystemTime,
454 protocol_handshake_started_at: Option<SystemTime>,
455 transport_duration: Option<Duration>,
456 protocol_handshake_duration: Option<Duration>,
457}
458
459struct EstablishmentObservation {
461 listener: SharedConnectionEventListener,
462 info: ConnectionEstablishmentInfo,
463 stage: ConnectionEstablishmentStage,
464 remote_addr: Option<SocketAddr>,
465 protocol: Option<ConnectionProtocol>,
466}
467
468impl ConnectionEstablishment {
469 pub(super) fn transport_completed(&mut self, remote_addr: Option<SocketAddr>) {
471 self.timing.transport_duration = Some(self.timing.elapsed_since(self.timing.started_at));
472 if let Some(observation) = &mut self.observation {
473 observation.remote_addr = remote_addr;
474 observation.stage = ConnectionEstablishmentStage::ProtocolSelection;
475 }
476 }
477
478 pub(super) fn protocol_selected(&mut self, protocol: ConnectionProtocol) {
480 if let Some(observation) = &mut self.observation {
481 observation.protocol = Some(protocol);
482 }
483 }
484
485 pub(super) fn protocol_handshake_started(&mut self) {
487 self.timing.protocol_handshake_started_at = Some(self.timing.time_source.now());
488 if let Some(observation) = &mut self.observation {
489 observation.stage = ConnectionEstablishmentStage::ProtocolHandshake;
490 }
491 }
492
493 pub(super) fn protocol_handshake_failed(&mut self) {
495 self.timing.finish_protocol_handshake();
496 }
497
498 pub(super) fn protocol_handshake_completed(&mut self) {
500 self.timing.finish_protocol_handshake();
501 if let Some(observation) = &mut self.observation {
502 observation.stage = ConnectionEstablishmentStage::PoolInstallation;
503 }
504 }
505
506 pub(super) fn installed(&mut self, connection: &PoolArc<ConnectionState>) {
508 let stats = self.timing.stats();
509 connection
510 .info()
511 .set_establishment(stats.connection_metadata());
512 assert!(
513 self.successful_stats.replace(stats).is_none(),
514 "connection establishment was installed more than once"
515 );
516 }
517
518 pub(super) fn failed(mut self, error: &ConnectorError) {
520 let stats = self.timing.stats();
521 self.finish_connection_stats();
522 let Some(observation) = self.observation.take() else {
523 return;
524 };
525 observation.notify_failure(error, &stats);
526 }
527
528 pub(super) fn opened(mut self, connection: &PoolArc<ConnectionState>) {
530 let stats = self.successful_stats.take().unwrap_or_else(|| {
531 let stats = self.timing.stats();
532 connection
533 .info()
534 .set_establishment(stats.connection_metadata());
535 stats
536 });
537 self.finish_connection_stats();
538 let listener = self.observation.take().map(|observation| {
539 observation
540 .listener
541 .notify(&ConnectionEvent::Opened(ConnectionOpened {
542 establishment: &observation.info,
543 stats: &stats,
544 connection: connection.info(),
545 }));
546 observation.listener
547 });
548 connection.complete_opened_event(listener.as_ref());
549 }
550
551 pub(super) fn superseded(mut self) {
553 self.finish_connection_stats();
554 self.observation.take();
555 }
556
557 fn finish_connection_stats(&mut self) {
558 if let Some(stats) = self.connection_stats.take() {
559 stats.establishment_finished();
560 }
561 }
562}
563
564impl Drop for ConnectionEstablishment {
565 fn drop(&mut self) {
566 let stats = self.successful_stats.unwrap_or_else(|| self.timing.stats());
567 self.finish_connection_stats();
568 let Some(observation) = self.observation.take() else {
569 return;
570 };
571 let error = ConnectorError::io("connection establishment task was dropped".into());
572 observation.notify_failure(&error, &stats);
573 }
574}
575
576impl EstablishmentObservation {
577 fn notify_failure(&self, error: &ConnectorError, stats: &ConnectionEstablishmentStats) {
578 self.listener.notify(&ConnectionEvent::EstablishmentFailed(
579 ConnectionEstablishmentFailed {
580 establishment: &self.info,
581 stats,
582 stage: self.stage,
583 remote_addr: self.remote_addr,
584 protocol: self.protocol,
585 error,
586 },
587 ));
588 }
589}
590
591impl EstablishmentTiming {
592 fn stats(&self) -> ConnectionEstablishmentStats {
593 let total_duration = self.elapsed_since(self.started_at);
594 ConnectionEstablishmentStats {
595 total_duration,
596 transport_duration: self.transport_duration.unwrap_or(total_duration),
597 protocol_handshake_duration: self.protocol_handshake_duration.or_else(|| {
598 self.protocol_handshake_started_at
599 .map(|started_at| self.elapsed_since(started_at))
600 }),
601 }
602 }
603
604 fn finish_protocol_handshake(&mut self) {
605 self.protocol_handshake_duration = self
606 .protocol_handshake_started_at
607 .take()
608 .map(|started_at| self.elapsed_since(started_at));
609 }
610
611 fn elapsed_since(&self, started_at: SystemTime) -> Duration {
612 match self.time_source.now().duration_since(started_at) {
613 Ok(duration) => duration,
614 Err(error) => {
615 tracing::warn!(?error, "connection establishment clock moved backwards");
616 Duration::ZERO
617 }
618 }
619 }
620}
621
622#[cfg(all(test, not(smithy_http_client_loom)))]
623mod tests {
624 use super::*;
625 use aws_smithy_async::{test_util::ManualTimeSource, time::StaticTimeSource};
626 use std::sync::atomic::AtomicUsize;
627 use std::sync::Mutex;
628 use std::time::UNIX_EPOCH;
629
630 fn origin() -> OriginKey {
631 OriginKey::from_parts(http_1x::uri::Scheme::HTTPS, "example.com", None).unwrap()
632 }
633
634 fn events(listener: Option<SharedConnectionEventListener>) -> ConnectionEvents {
635 ConnectionEvents::new(listener, SharedTimeSource::default())
636 }
637
638 fn establishment(events: &ConnectionEvents) -> ConnectionEstablishment {
639 events.establishment_started(
640 &origin(),
641 PartitionId::from_index(1),
642 PoolArc::new(CellConnectionStats::default()),
643 )
644 }
645
646 #[test]
647 fn disabled_callbacks_do_not_allocate_establishment_ids() {
648 let events = events(None);
649 events.next_establishment_id.store(7, Ordering::Relaxed);
650
651 establishment(&events).superseded();
652
653 assert_eq!(7, events.next_establishment_id.load(Ordering::Relaxed));
654 }
655
656 #[test]
657 fn listener_panic_is_isolated() {
658 let events = events(Some(SharedConnectionEventListener::new(
659 |_: &ConnectionEvent<'_>| panic!("listener failed"),
660 )));
661 let error = ConnectorError::io("synthetic transport failure".into());
662
663 establishment(&events).failed(&error);
664 }
665
666 #[test]
667 fn dropping_active_establishment_emits_transport_failure() {
668 let observed = Arc::new(Mutex::new(None));
669 let events = events(Some(SharedConnectionEventListener::new({
670 let observed = observed.clone();
671 move |event: &ConnectionEvent<'_>| {
672 let ConnectionEvent::EstablishmentFailed(failed) = event else {
673 panic!("unexpected event: {event:?}");
674 };
675 *observed.lock().unwrap() = Some((failed.establishment().id(), failed.stage()));
676 }
677 })));
678
679 drop(establishment(&events));
680
681 assert_eq!(
682 Some((
683 ConnectionEstablishmentId(0),
684 ConnectionEstablishmentStage::Transport
685 )),
686 *observed.lock().unwrap()
687 );
688 }
689
690 #[test]
691 fn failed_callback_observes_finished_establishment_count() {
692 let stats = PoolArc::new(CellConnectionStats::default());
693 let observed = Arc::new(Mutex::new(None));
694 let events = events(Some(SharedConnectionEventListener::new({
695 let stats = stats.clone();
696 let observed = observed.clone();
697 move |event: &ConnectionEvent<'_>| {
698 let ConnectionEvent::EstablishmentFailed(_) = event else {
699 panic!("unexpected event: {event:?}");
700 };
701 *observed.lock().unwrap() =
702 Some(stats.snapshot(0, 0, 0, 0, 0).establishing_connections());
703 }
704 })));
705 let establishment =
706 events.establishment_started(&origin(), PartitionId::from_index(1), stats.clone());
707 assert_eq!(1, stats.snapshot(0, 0, 0, 0, 0).establishing_connections());
708 let error = ConnectorError::io("synthetic transport failure".into());
709
710 establishment.failed(&error);
711
712 assert_eq!(Some(0), *observed.lock().unwrap());
713 }
714
715 #[test]
716 fn failed_event_carries_recorded_stage_and_protocol() {
717 let observed = Arc::new(Mutex::new(None));
718 let events = events(Some(SharedConnectionEventListener::new({
719 let observed = observed.clone();
720 move |event: &ConnectionEvent<'_>| {
721 let ConnectionEvent::EstablishmentFailed(failed) = event else {
722 panic!("unexpected event: {event:?}");
723 };
724 *observed.lock().unwrap() = Some((
725 failed.establishment().id(),
726 failed.stage(),
727 failed.protocol(),
728 failed.stats().protocol_handshake_duration().is_some(),
729 ));
730 }
731 })));
732 let mut establishment = establishment(&events);
733 establishment.transport_completed(None);
734 establishment.protocol_selected(ConnectionProtocol::Http2);
735 establishment.protocol_handshake_started();
736 establishment.protocol_handshake_failed();
737 let error = ConnectorError::io("synthetic handshake failure".into());
738
739 establishment.failed(&error);
740
741 assert_eq!(
742 Some((
743 ConnectionEstablishmentId(0),
744 ConnectionEstablishmentStage::ProtocolHandshake,
745 Some(ConnectionProtocol::Http2),
746 true,
747 )),
748 *observed.lock().unwrap()
749 );
750 }
751
752 #[test]
753 fn establishment_stats_measure_recorded_phases() {
754 let time = ManualTimeSource::new(UNIX_EPOCH);
755 let observed = Arc::new(Mutex::new(None));
756 let events = ConnectionEvents::new(
757 Some(SharedConnectionEventListener::new({
758 let observed = observed.clone();
759 move |event: &ConnectionEvent<'_>| {
760 let ConnectionEvent::EstablishmentFailed(failed) = event else {
761 panic!("unexpected event: {event:?}");
762 };
763 *observed.lock().unwrap() = Some(*failed.stats());
764 }
765 })),
766 SharedTimeSource::new(time.clone()),
767 );
768 let mut establishment = establishment(&events);
769
770 time.advance(Duration::from_secs(2));
771 establishment.transport_completed(None);
772 establishment.protocol_selected(ConnectionProtocol::Http2);
773 establishment.protocol_handshake_started();
774 time.advance(Duration::from_secs(3));
775 establishment.protocol_handshake_completed();
776 time.advance(Duration::from_secs(2));
777 establishment.failed(&ConnectorError::io("synthetic installation failure".into()));
778
779 let stats = observed.lock().unwrap().expect("establishment stats");
780 assert_eq!(stats.total_duration(), Duration::from_secs(7));
781 assert_eq!(stats.transport_duration(), Duration::from_secs(2));
782 assert_eq!(
783 stats.protocol_handshake_duration(),
784 Some(Duration::from_secs(3))
785 );
786 let metadata = stats.connection_metadata();
787 assert_eq!(metadata.total_duration(), Duration::from_secs(7));
788 assert_eq!(metadata.transport_duration(), Duration::from_secs(2));
789 assert_eq!(
790 metadata.protocol_handshake_duration(),
791 Some(Duration::from_secs(3))
792 );
793 }
794
795 #[test]
796 fn backwards_clock_saturates_establishment_durations() {
797 let timing = EstablishmentTiming {
798 time_source: StaticTimeSource::new(UNIX_EPOCH).into(),
799 started_at: UNIX_EPOCH + Duration::from_secs(1),
800 protocol_handshake_started_at: Some(UNIX_EPOCH + Duration::from_secs(1)),
801 transport_duration: None,
802 protocol_handshake_duration: None,
803 };
804
805 let stats = timing.stats();
806
807 assert_eq!(stats.total_duration(), Duration::ZERO);
808 assert_eq!(stats.transport_duration(), Duration::ZERO);
809 assert_eq!(stats.protocol_handshake_duration(), Some(Duration::ZERO));
810 }
811
812 #[test]
813 fn superseded_establishment_emits_no_public_event() {
814 let observed = Arc::new(AtomicUsize::new(0));
815 let events = events(Some(SharedConnectionEventListener::new({
816 let observed = observed.clone();
817 move |_: &ConnectionEvent<'_>| {
818 observed.fetch_add(1, Ordering::Relaxed);
819 }
820 })));
821
822 establishment(&events).superseded();
823
824 assert_eq!(0, observed.load(Ordering::Relaxed));
825 assert_eq!(1, events.next_establishment_id.load(Ordering::Relaxed));
826 }
827
828 #[test]
829 fn closure_listener_receives_one_terminal_event() {
830 let observed = Arc::new(AtomicUsize::new(0));
831 let events = events(Some(SharedConnectionEventListener::new({
832 let observed = observed.clone();
833 move |event: &ConnectionEvent<'_>| {
834 assert!(matches!(event, ConnectionEvent::EstablishmentFailed(_)));
835 observed.fetch_add(1, Ordering::Relaxed);
836 }
837 })));
838 let error = ConnectorError::io("synthetic transport failure".into());
839
840 establishment(&events).failed(&error);
841
842 assert_eq!(1, observed.load(Ordering::Relaxed));
843 }
844
845 #[test]
846 fn logical_close_causes_cover_every_close_reason() {
847 assert_eq!(
848 [
849 LogicalCloseCause::IdleTimeout,
850 LogicalCloseCause::Poisoned,
851 LogicalCloseCause::ProtocolEnded,
852 LogicalCloseCause::ProtocolEnded,
853 LogicalCloseCause::IncompleteH1Exchange,
854 LogicalCloseCause::Reclaimed,
855 LogicalCloseCause::PoolDropped,
856 LogicalCloseCause::OwnerRuntimeShutdown,
857 ],
858 [
859 CloseReason::IdleTimeout,
860 CloseReason::Poisoned,
861 CloseReason::ProtocolClosed,
862 CloseReason::Upgraded,
863 CloseReason::IncompleteH1Exchange,
864 CloseReason::Reclaimed,
865 CloseReason::PoolDropped,
866 CloseReason::OwnerRuntimeShutdown,
867 ]
868 .map(LogicalCloseCause::from_reason)
869 );
870 }
871}