tonic/transport/server/
incoming.rs1use std::{
26 net::{SocketAddr, TcpListener as StdTcpListener},
27 pin::Pin,
28 task::{Context, Poll},
29 time::Duration,
30};
31
32use socket2::TcpKeepalive;
33use tokio::net::{TcpListener, TcpStream};
34use tokio_stream::{Stream, wrappers::TcpListenerStream};
35use tracing::warn;
36
37#[derive(Debug)]
42pub struct TcpIncoming {
43 inner: TcpListenerStream,
44 nodelay: Option<bool>,
45 keepalive: Option<TcpKeepalive>,
46 keepalive_time: Option<Duration>,
47 keepalive_interval: Option<Duration>,
48 keepalive_retries: Option<u32>,
49}
50
51impl TcpIncoming {
52 pub fn bind(addr: SocketAddr) -> std::io::Result<Self> {
84 let std_listener = StdTcpListener::bind(addr)?;
85 std_listener.set_nonblocking(true)?;
86
87 Ok(TcpListener::from_std(std_listener)?.into())
88 }
89
90 pub fn with_nodelay(self, nodelay: Option<bool>) -> Self {
92 Self { nodelay, ..self }
93 }
94
95 pub fn with_keepalive(self, keepalive_time: Option<Duration>) -> Self {
97 Self {
98 keepalive_time,
99 keepalive: make_keepalive(
100 keepalive_time,
101 self.keepalive_interval,
102 self.keepalive_retries,
103 ),
104 ..self
105 }
106 }
107
108 pub fn with_keepalive_interval(self, keepalive_interval: Option<Duration>) -> Self {
110 Self {
111 keepalive_interval,
112 keepalive: make_keepalive(
113 self.keepalive_time,
114 keepalive_interval,
115 self.keepalive_retries,
116 ),
117 ..self
118 }
119 }
120
121 pub fn with_keepalive_retries(self, keepalive_retries: Option<u32>) -> Self {
123 Self {
124 keepalive_retries,
125 keepalive: make_keepalive(
126 self.keepalive_time,
127 self.keepalive_interval,
128 keepalive_retries,
129 ),
130 ..self
131 }
132 }
133
134 pub fn local_addr(&self) -> std::io::Result<SocketAddr> {
136 self.inner.as_ref().local_addr()
137 }
138}
139
140impl From<TcpListener> for TcpIncoming {
141 fn from(listener: TcpListener) -> Self {
142 Self {
143 inner: TcpListenerStream::new(listener),
144 nodelay: None,
145 keepalive: None,
146 keepalive_time: None,
147 keepalive_interval: None,
148 keepalive_retries: None,
149 }
150 }
151}
152
153impl Stream for TcpIncoming {
154 type Item = std::io::Result<TcpStream>;
155
156 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
157 let polled = Pin::new(&mut self.inner).poll_next(cx);
158
159 if let Poll::Ready(Some(Ok(stream))) = &polled {
160 set_accepted_socket_options(stream, self.nodelay, &self.keepalive);
161 }
162
163 polled
164 }
165}
166
167fn set_accepted_socket_options(
169 stream: &TcpStream,
170 nodelay: Option<bool>,
171 keepalive: &Option<TcpKeepalive>,
172) {
173 if let Some(nodelay) = nodelay
174 && let Err(e) = stream.set_nodelay(nodelay)
175 {
176 warn!("error trying to set TCP_NODELAY: {e}");
177 }
178
179 if let Some(keepalive) = keepalive {
180 let sock_ref = socket2::SockRef::from(&stream);
181 if let Err(e) = sock_ref.set_tcp_keepalive(keepalive) {
182 warn!("error trying to set TCP_KEEPALIVE: {e}");
183 }
184 }
185}
186
187fn make_keepalive(
188 keepalive_time: Option<Duration>,
189 keepalive_interval: Option<Duration>,
190 keepalive_retries: Option<u32>,
191) -> Option<TcpKeepalive> {
192 let mut dirty = false;
193 let mut keepalive = TcpKeepalive::new();
194 if let Some(t) = keepalive_time {
195 keepalive = keepalive.with_time(t);
196 dirty = true;
197 }
198
199 #[cfg(
200 any(
202 target_os = "android",
203 target_os = "dragonfly",
204 target_os = "freebsd",
205 target_os = "fuchsia",
206 target_os = "illumos",
207 target_os = "ios",
208 target_os = "visionos",
209 target_os = "linux",
210 target_os = "macos",
211 target_os = "netbsd",
212 target_os = "tvos",
213 target_os = "watchos",
214 target_os = "windows",
215 )
216 )]
217 if let Some(t) = keepalive_interval {
218 keepalive = keepalive.with_interval(t);
219 dirty = true;
220 }
221
222 #[cfg(
223 any(
225 target_os = "android",
226 target_os = "dragonfly",
227 target_os = "freebsd",
228 target_os = "fuchsia",
229 target_os = "illumos",
230 target_os = "ios",
231 target_os = "visionos",
232 target_os = "linux",
233 target_os = "macos",
234 target_os = "netbsd",
235 target_os = "tvos",
236 target_os = "watchos",
237 )
238 )]
239 if let Some(r) = keepalive_retries {
240 keepalive = keepalive.with_retries(r);
241 dirty = true;
242 }
243
244 let _ = keepalive_retries;
246 let _ = keepalive_interval;
247
248 dirty.then_some(keepalive)
249}
250
251#[cfg(test)]
252mod tests {
253 use crate::transport::server::TcpIncoming;
254 #[tokio::test]
255 async fn one_tcpincoming_at_a_time() {
256 let addr = "127.0.0.1:1322".parse().unwrap();
257 {
258 let _t1 = TcpIncoming::bind(addr).unwrap();
259 let _t2 = TcpIncoming::bind(addr).unwrap_err();
260 }
261 let _t3 = TcpIncoming::bind(addr).unwrap();
262 }
263}