The `Accept` trait is used by the server types to asynchronously accept
incoming connections. This replaces the previous usage of `Stream`.
BREAKING CHANGE: Passing a `Stream` to `Server::builder` or
`Http::serve_incoming` must be changed to pass an `Accept` instead. The
`stream` optional feature can be enabled, and the a stream can be
converted using `hyper::server:🉑:from_stream`.
285 lines
9.7 KiB
Rust
285 lines
9.7 KiB
Rust
use std::fmt;
|
|
use std::io;
|
|
use std::net::{SocketAddr, TcpListener as StdTcpListener};
|
|
use std::time::Duration;
|
|
|
|
use futures_util::FutureExt as _;
|
|
use tokio_net::driver::Handle;
|
|
use tokio_net::tcp::TcpListener;
|
|
use tokio_timer::Delay;
|
|
|
|
use crate::common::{Future, Pin, Poll, task};
|
|
|
|
use super::Accept;
|
|
pub use self::addr_stream::AddrStream;
|
|
|
|
/// A stream of connections from binding to an address.
|
|
#[must_use = "streams do nothing unless polled"]
|
|
pub struct AddrIncoming {
|
|
addr: SocketAddr,
|
|
listener: TcpListener,
|
|
sleep_on_errors: bool,
|
|
tcp_keepalive_timeout: Option<Duration>,
|
|
tcp_nodelay: bool,
|
|
timeout: Option<Delay>,
|
|
}
|
|
|
|
impl AddrIncoming {
|
|
pub(super) fn new(addr: &SocketAddr, handle: Option<&Handle>) -> crate::Result<Self> {
|
|
let std_listener = StdTcpListener::bind(addr)
|
|
.map_err(crate::Error::new_listen)?;
|
|
|
|
if let Some(handle) = handle {
|
|
AddrIncoming::from_std(std_listener, handle)
|
|
} else {
|
|
let handle = Handle::default();
|
|
AddrIncoming::from_std(std_listener, &handle)
|
|
}
|
|
}
|
|
|
|
pub(super) fn from_std(std_listener: StdTcpListener, handle: &Handle) -> crate::Result<Self> {
|
|
let listener = TcpListener::from_std(std_listener, &handle)
|
|
.map_err(crate::Error::new_listen)?;
|
|
let addr = listener.local_addr().map_err(crate::Error::new_listen)?;
|
|
Ok(AddrIncoming {
|
|
listener,
|
|
addr: addr,
|
|
sleep_on_errors: true,
|
|
tcp_keepalive_timeout: None,
|
|
tcp_nodelay: false,
|
|
timeout: None,
|
|
})
|
|
}
|
|
|
|
/// Creates a new `AddrIncoming` binding to provided socket address.
|
|
pub fn bind(addr: &SocketAddr) -> crate::Result<Self> {
|
|
AddrIncoming::new(addr, None)
|
|
}
|
|
|
|
/// Get the local address bound to this listener.
|
|
pub fn local_addr(&self) -> SocketAddr {
|
|
self.addr
|
|
}
|
|
|
|
/// Set whether TCP keepalive messages are enabled on accepted connections.
|
|
///
|
|
/// If `None` is specified, keepalive is disabled, otherwise the duration
|
|
/// specified will be the time to remain idle before sending TCP keepalive
|
|
/// probes.
|
|
pub fn set_keepalive(&mut self, keepalive: Option<Duration>) -> &mut Self {
|
|
self.tcp_keepalive_timeout = keepalive;
|
|
self
|
|
}
|
|
|
|
/// Set the value of `TCP_NODELAY` option for accepted connections.
|
|
pub fn set_nodelay(&mut self, enabled: bool) -> &mut Self {
|
|
self.tcp_nodelay = enabled;
|
|
self
|
|
}
|
|
|
|
/// Set whether to sleep on accept errors.
|
|
///
|
|
/// A possible scenario is that the process has hit the max open files
|
|
/// allowed, and so trying to accept a new connection will fail with
|
|
/// `EMFILE`. In some cases, it's preferable to just wait for some time, if
|
|
/// the application will likely close some files (or connections), and try
|
|
/// to accept the connection again. If this option is `true`, the error
|
|
/// will be logged at the `error` level, since it is still a big deal,
|
|
/// and then the listener will sleep for 1 second.
|
|
///
|
|
/// In other cases, hitting the max open files should be treat similarly
|
|
/// to being out-of-memory, and simply error (and shutdown). Setting
|
|
/// this option to `false` will allow that.
|
|
///
|
|
/// Default is `true`.
|
|
pub fn set_sleep_on_errors(&mut self, val: bool) {
|
|
self.sleep_on_errors = val;
|
|
}
|
|
|
|
fn poll_next_(&mut self, cx: &mut task::Context<'_>) -> Poll<io::Result<AddrStream>> {
|
|
// Check if a previous timeout is active that was set by IO errors.
|
|
if let Some(ref mut to) = self.timeout {
|
|
match Pin::new(to).poll(cx) {
|
|
Poll::Ready(()) => {}
|
|
Poll::Pending => return Poll::Pending,
|
|
}
|
|
}
|
|
self.timeout = None;
|
|
|
|
let accept = self.listener.accept();
|
|
futures_util::pin_mut!(accept);
|
|
|
|
loop {
|
|
match accept.poll_unpin(cx) {
|
|
Poll::Ready(Ok((socket, addr))) => {
|
|
if let Some(dur) = self.tcp_keepalive_timeout {
|
|
if let Err(e) = socket.set_keepalive(Some(dur)) {
|
|
trace!("error trying to set TCP keepalive: {}", e);
|
|
}
|
|
}
|
|
if let Err(e) = socket.set_nodelay(self.tcp_nodelay) {
|
|
trace!("error trying to set TCP nodelay: {}", e);
|
|
}
|
|
return Poll::Ready(Ok(AddrStream::new(socket, addr)));
|
|
},
|
|
Poll::Pending => return Poll::Pending,
|
|
Poll::Ready(Err(e)) => {
|
|
// Connection errors can be ignored directly, continue by
|
|
// accepting the next request.
|
|
if is_connection_error(&e) {
|
|
debug!("accepted connection already errored: {}", e);
|
|
continue;
|
|
}
|
|
|
|
if self.sleep_on_errors {
|
|
error!("accept error: {}", e);
|
|
|
|
// Sleep 1s.
|
|
let mut timeout = tokio_timer::sleep(Duration::from_secs(1));
|
|
|
|
match Pin::new(&mut timeout).poll(cx) {
|
|
Poll::Ready(()) => {
|
|
// Wow, it's been a second already? Ok then...
|
|
continue
|
|
},
|
|
Poll::Pending => {
|
|
self.timeout = Some(timeout);
|
|
return Poll::Pending;
|
|
},
|
|
}
|
|
} else {
|
|
return Poll::Ready(Err(e));
|
|
}
|
|
},
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
impl Stream for AddrIncoming {
|
|
type Item = io::Result<AddrStream>;
|
|
|
|
fn poll_next(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Option<Self::Item>> {
|
|
let result = ready!(self.poll_next_(cx));
|
|
Poll::Ready(Some(result))
|
|
}
|
|
}
|
|
*/
|
|
|
|
impl Accept for AddrIncoming {
|
|
type Conn = AddrStream;
|
|
type Error = io::Error;
|
|
|
|
fn poll_accept(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Option<Result<Self::Conn, Self::Error>>> {
|
|
let result = ready!(self.poll_next_(cx));
|
|
Poll::Ready(Some(result))
|
|
}
|
|
}
|
|
|
|
/// This function defines errors that are per-connection. Which basically
|
|
/// means that if we get this error from `accept()` system call it means
|
|
/// next connection might be ready to be accepted.
|
|
///
|
|
/// All other errors will incur a timeout before next `accept()` is performed.
|
|
/// The timeout is useful to handle resource exhaustion errors like ENFILE
|
|
/// and EMFILE. Otherwise, could enter into tight loop.
|
|
fn is_connection_error(e: &io::Error) -> bool {
|
|
match e.kind() {
|
|
io::ErrorKind::ConnectionRefused |
|
|
io::ErrorKind::ConnectionAborted |
|
|
io::ErrorKind::ConnectionReset => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for AddrIncoming {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.debug_struct("AddrIncoming")
|
|
.field("addr", &self.addr)
|
|
.field("sleep_on_errors", &self.sleep_on_errors)
|
|
.field("tcp_keepalive_timeout", &self.tcp_keepalive_timeout)
|
|
.field("tcp_nodelay", &self.tcp_nodelay)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
mod addr_stream {
|
|
use std::io;
|
|
use std::net::SocketAddr;
|
|
use bytes::{Buf, BufMut};
|
|
use tokio_net::tcp::TcpStream;
|
|
use tokio_io::{AsyncRead, AsyncWrite};
|
|
|
|
use crate::common::{Pin, Poll, task};
|
|
|
|
|
|
/// A transport returned yieled by `AddrIncoming`.
|
|
#[derive(Debug)]
|
|
pub struct AddrStream {
|
|
inner: TcpStream,
|
|
pub(super) remote_addr: SocketAddr,
|
|
}
|
|
|
|
impl AddrStream {
|
|
pub(super) fn new(tcp: TcpStream, addr: SocketAddr) -> AddrStream {
|
|
AddrStream {
|
|
inner: tcp,
|
|
remote_addr: addr,
|
|
}
|
|
}
|
|
|
|
/// Returns the remote (peer) address of this connection.
|
|
#[inline]
|
|
pub fn remote_addr(&self) -> SocketAddr {
|
|
self.remote_addr
|
|
}
|
|
|
|
/// Consumes the AddrStream and returns the underlying IO object
|
|
#[inline]
|
|
pub fn into_inner(self) -> TcpStream {
|
|
self.inner
|
|
}
|
|
}
|
|
|
|
impl AsyncRead for AddrStream {
|
|
#[inline]
|
|
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
|
|
self.inner.prepare_uninitialized_buffer(buf)
|
|
}
|
|
|
|
#[inline]
|
|
fn poll_read(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>, buf: &mut [u8]) -> Poll<io::Result<usize>> {
|
|
Pin::new(&mut self.inner).poll_read(cx, buf)
|
|
}
|
|
|
|
#[inline]
|
|
fn poll_read_buf<B: BufMut>(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>, buf: &mut B) -> Poll<io::Result<usize>> {
|
|
Pin::new(&mut self.inner).poll_read_buf(cx, buf)
|
|
}
|
|
}
|
|
|
|
impl AsyncWrite for AddrStream {
|
|
#[inline]
|
|
fn poll_write(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>, buf: &[u8]) -> Poll<io::Result<usize>> {
|
|
Pin::new(&mut self.inner).poll_write(cx, buf)
|
|
}
|
|
|
|
#[inline]
|
|
fn poll_write_buf<B: Buf>(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>, buf: &mut B) -> Poll<io::Result<usize>> {
|
|
Pin::new(&mut self.inner).poll_write_buf(cx, buf)
|
|
}
|
|
|
|
#[inline]
|
|
fn poll_flush(self: Pin<&mut Self>, _cx: &mut task::Context<'_>) -> Poll<io::Result<()>> {
|
|
// TCP flush is a noop
|
|
Poll::Ready(Ok(()))
|
|
}
|
|
|
|
#[inline]
|
|
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<io::Result<()>> {
|
|
Pin::new(&mut self.inner).poll_shutdown(cx)
|
|
}
|
|
}
|
|
}
|