confium_net_tcp/
listener.rs1use std::net::IpAddr;
10use std::net::Ipv4Addr;
11use std::net::Ipv6Addr;
12use std::net::SocketAddr;
13use std::net::TcpListener as StdTcpListener;
14
15use confium_net::Listener;
16use confium_net::Result;
17use confium_net::Transport;
18use confium_net::error::ClosedSnafu;
19
20use crate::TcpTransport;
21use crate::transport::address_family;
22
23pub struct TcpListener {
28 inner: Option<StdTcpListener>,
29}
30
31impl TcpListener {
32 pub fn bind(scheme: &str, host: &str, port: u16) -> std::io::Result<Self> {
37 let listener = match address_family(scheme) {
38 Some(false) => {
39 let ip: Ipv4Addr = parse_ipv4(host)?;
42 StdTcpListener::bind(SocketAddr::new(IpAddr::V4(ip), port))?
43 }
44 Some(true) => {
45 let ip: Ipv6Addr = parse_ipv6(host)?;
47 StdTcpListener::bind(SocketAddr::new(IpAddr::V6(ip), port))?
48 }
49 None => {
50 StdTcpListener::bind((host, port))?
54 }
55 };
56 listener.set_nonblocking(false).ok();
57 Ok(Self {
58 inner: Some(listener),
59 })
60 }
61
62 pub fn local_addr(&self) -> std::io::Result<SocketAddr> {
65 self.inner
66 .as_ref()
67 .ok_or_else(|| std::io::Error::from(std::io::ErrorKind::NotConnected))?
68 .local_addr()
69 }
70}
71
72impl Listener for TcpListener {
73 fn accept(&mut self) -> Result<Box<dyn Transport>> {
74 let listener = match &self.inner {
75 Some(l) => l,
76 None => return ClosedSnafu.fail(),
77 };
78 let (stream, _peer) = listener.accept().map_err(crate::transport::io_to_closed)?;
79 stream.set_nodelay(true).ok();
82 Ok(Box::new(TcpTransport::from_stream(stream)))
83 }
84}
85
86impl Drop for TcpListener {
87 fn drop(&mut self) {
88 self.inner.take();
91 }
92}
93
94fn parse_ipv4(host: &str) -> std::io::Result<Ipv4Addr> {
95 host.parse::<Ipv4Addr>().map_err(|_| {
96 std::io::Error::new(
97 std::io::ErrorKind::InvalidInput,
98 format!("invalid IPv4 bind address '{host}'"),
99 )
100 })
101}
102
103fn parse_ipv6(host: &str) -> std::io::Result<Ipv6Addr> {
104 host.parse::<Ipv6Addr>().map_err(|_| {
105 std::io::Error::new(
106 std::io::ErrorKind::InvalidInput,
107 format!("invalid IPv6 bind address '{host}'"),
108 )
109 })
110}