Skip to main content

sccp_protocol/server/
transport.rs

1//! Transport-neutral station admission.
2//!
3//! Listener owners establish the underlying connection and its security
4//! policy, then hand the ready byte stream to the protocol server. Session
5//! framing and lifecycle remain independent of the transport implementation.
6
7use std::fmt;
8use std::net::SocketAddr;
9
10use tokio::io::{AsyncRead, AsyncWrite};
11use tokio::sync::mpsc;
12
13use super::ServerError;
14use super::qos::StationSocketQos;
15use crate::types::{SignalingQos, StationTransport};
16
17/// Bidirectional asynchronous byte stream accepted by a station session.
18///
19/// The protocol server owns the stream after admission and applies identical
20/// framing, backpressure, registration, and shutdown behavior regardless of
21/// the underlying transport. A transport adapter may implement this trait with
22/// a plain socket, a decrypted secure stream, or an in-memory test stream.
23pub trait StationIo: AsyncRead + AsyncWrite + Unpin + Send {}
24
25impl<T> StationIo for T where T: AsyncRead + AsyncWrite + Unpin + Send {}
26
27pub(super) type BoxedStationIo = Box<dyn StationIo>;
28
29pub(super) struct AcceptedStation {
30    pub stream: BoxedStationIo,
31    pub peer: SocketAddr,
32    pub local: SocketAddr,
33    pub transport: StationTransport,
34    pub socket_qos: Option<Box<dyn StationSocketQos>>,
35}
36
37impl fmt::Debug for AcceptedStation {
38    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
39        formatter
40            .debug_struct("AcceptedStation")
41            .field("stream", &"<station I/O>")
42            .field("peer", &self.peer)
43            .field("local", &self.local)
44            .field("transport", &self.transport)
45            .field(
46                "socket_qos",
47                &self.socket_qos.as_ref().map(|_| "<socket QoS control>"),
48            )
49            .finish()
50    }
51}
52
53#[derive(Clone, Debug)]
54/// Cloneable admission endpoint returned by [`super::Server::with_ingress`].
55///
56/// A listener owner performs transport-specific setup first, then submits the
57/// ready byte stream with its actual peer address, accepted local address, and
58/// transport classification. Clones share one bounded queue, so awaiting
59/// [`Self::accept`] propagates server backpressure instead of creating
60/// unbounded session work.
61pub struct ServerIngress {
62    sender: mpsc::Sender<AcceptedStation>,
63    signaling_qos: SignalingQos,
64}
65
66impl ServerIngress {
67    pub(super) fn channel(
68        capacity: usize,
69        signaling_qos: SignalingQos,
70    ) -> (Self, mpsc::Receiver<AcceptedStation>) {
71        let (sender, receiver) = mpsc::channel(capacity);
72        (
73            Self {
74                sender,
75                signaling_qos,
76            },
77            receiver,
78        )
79    }
80
81    /// Transfer ownership of an accepted stream to the server run loop.
82    ///
83    /// `peer` identifies the remote station for events and address policy;
84    /// `local` is the concrete local endpoint used for server-list responses.
85    /// `transport` must describe the already-established stream because it is
86    /// checked against the device definition during registration. For secure
87    /// admission, complete the handshake and any certificate policy before
88    /// calling this method.
89    ///
90    /// The method waits for capacity in the ingress queue. It returns
91    /// [`ServerError::Stopped`] without starting a session if the run loop has
92    /// ended.
93    pub async fn accept<S>(
94        &self,
95        stream: S,
96        peer: SocketAddr,
97        local: SocketAddr,
98        transport: StationTransport,
99    ) -> Result<(), ServerError>
100    where
101        S: StationIo + 'static,
102    {
103        self.admit(Box::new(stream), peer, local, transport, None)
104            .await
105    }
106
107    /// Admit a stream while retaining control of its underlying TCP markings.
108    ///
109    /// The server reapplies the selected station's signaling policy after the
110    /// registration message identifies it. Marking failures are logged while
111    /// registration and subsequent protocol traffic continue normally.
112    pub async fn accept_with_socket_qos<S, Q>(
113        &self,
114        stream: S,
115        peer: SocketAddr,
116        local: SocketAddr,
117        transport: StationTransport,
118        socket_qos: Q,
119    ) -> Result<(), ServerError>
120    where
121        S: StationIo + 'static,
122        Q: StationSocketQos + 'static,
123    {
124        super::report_socket_qos(None, peer, socket_qos.apply(self.signaling_qos));
125        self.admit(
126            Box::new(stream),
127            peer,
128            local,
129            transport,
130            Some(Box::new(socket_qos)),
131        )
132        .await
133    }
134
135    async fn admit(
136        &self,
137        stream: BoxedStationIo,
138        peer: SocketAddr,
139        local: SocketAddr,
140        transport: StationTransport,
141        socket_qos: Option<Box<dyn StationSocketQos>>,
142    ) -> Result<(), ServerError> {
143        self.sender
144            .send(AcceptedStation {
145                stream,
146                peer,
147                local,
148                transport,
149                socket_qos,
150            })
151            .await
152            .map_err(|_| ServerError::Stopped)
153    }
154}