pavex/server/incoming.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
use std::net::SocketAddr;
use socket2::Domain;
use tokio::net::{TcpListener, TcpStream};
/// A stream of incoming connections.
///
/// [`IncomingStream::bind`] is the primary entrypoint for constructing a new [`IncomingStream`].
///
/// Incoming connections will be usually passed to a [`Server`](super::Server) instance to be handled.
/// Check out [`Server::bind`](super::Server::bind) or
/// [`Server::listen`](super::Server::listen) for more information.
pub struct IncomingStream {
listener: TcpListener,
}
impl IncomingStream {
/// Create a new [`IncomingStream`] by binding to a socket address.
/// The socket will be configured to be non-blocking and reuse the address.
///
/// # Example
///
/// ```rust
/// use std::net::SocketAddr;
/// use pavex::server::{IncomingStream, Server};
///
/// # async fn t() -> std::io::Result<()> {
/// let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
/// let incoming = IncomingStream::bind(addr).await?;
/// # Ok(())
/// # }
/// ````
///
/// # Custom configuration
///
/// If you want to customize the options set on the socket, you can build your own
/// [`TcpListener`](std::net::TcpListener) using [`socket2::Socket`] and then convert it
/// into an [`IncomingStream`] via [`TryFrom::try_from`].
/// There's only one option you can't change: the socket will always be set to non-blocking
/// mode.
///
/// ```rust
/// use std::net::SocketAddr;
/// use socket2::Domain;
/// use pavex::server::{IncomingStream, Server};
///
/// # async fn t() -> std::io::Result<()> {
/// let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
///
/// let socket = socket2::Socket::new(
/// Domain::for_address(addr),
/// socket2::Type::STREAM,
/// Some(socket2::Protocol::TCP),
/// )
/// .expect("Failed to create a socket");
/// socket.set_reuse_address(true)?;
/// socket.set_nonblocking(true)?;
/// socket.bind(&addr.into())?;
/// // We customize the backlog setting!
/// socket.listen(2048_i32)?;
///
/// let listener = std::net::TcpListener::from(socket);
/// let incoming: IncomingStream = listener.try_into()?;
/// # Ok(())
/// # }
/// ````
pub async fn bind(addr: SocketAddr) -> std::io::Result<Self> {
let socket = socket2::Socket::new(
Domain::for_address(addr),
socket2::Type::STREAM,
Some(socket2::Protocol::TCP),
)
.expect("Failed to create a socket");
socket.set_reuse_address(true)?;
socket.set_nonblocking(true)?;
socket.bind(&addr.into())?;
socket.listen(1024_i32)?;
let listener = std::net::TcpListener::from(socket);
Ok(Self {
listener: TcpListener::from_std(listener)?,
})
}
/// Returns the address that this [`IncomingStream`] is bound to.
pub fn local_addr(&self) -> std::io::Result<SocketAddr> {
// The address we bound to may not be the same as the one we requested.
// This happens, for example, when binding to port 0—this will cause the OS to pick a random
// port for us which we won't know unless we call `local_addr` on the listener.
self.listener.local_addr()
}
/// Accepts a new incoming connection from the underlying listener.
///
/// This function will yield once a new TCP connection is established. When
/// established, the corresponding [`TcpStream`] and the remote peer's
/// address will be returned.
///
/// # Example
///
/// ```no_run
/// use pavex::server::IncomingStream;
/// use std::net::SocketAddr;
///
/// # async fn t() -> std::io::Result<()> {
/// let address = SocketAddr::from(([127, 0, 0, 1], 8080));
/// let incoming = IncomingStream::bind(address).await?;
///
/// match incoming.accept().await {
/// Ok((_socket, addr)) => println!("new client: {:?}", addr),
/// Err(e) => println!("couldn't get client: {:?}", e),
/// }
/// # Ok(())
/// # }
/// ```
pub async fn accept(&self) -> std::io::Result<(TcpStream, SocketAddr)> {
self.listener.accept().await
}
}
impl TryFrom<std::net::TcpListener> for IncomingStream {
type Error = std::io::Error;
fn try_from(v: std::net::TcpListener) -> std::io::Result<Self> {
// This is *very* important!
// `tokio` won't automatically set the socket to non-blocking mode for us, so we have to do
// it ourselves.
// Forgetting to set the socket to non-blocking mode will likely result in
// mysterious server hangs.
v.set_nonblocking(true)?;
Ok(Self {
listener: TcpListener::from_std(v)?,
})
}
}
impl From<TcpListener> for IncomingStream {
fn from(v: TcpListener) -> Self {
Self { listener: v }
}
}