From f4744aeb55b87d4d972fcf58bc0a59305a742ed4 Mon Sep 17 00:00:00 2001 From: Jacob Rothstein Date: Thu, 22 Apr 2021 09:34:26 -0700 Subject: [PATCH] initial work on listener configuration --- src/listener/mod.rs | 4 +- src/listener/tcp_listener.rs | 97 ++++++++++++++++++++++++++++--- src/listener/to_listener_impls.rs | 20 ++++--- 3 files changed, 102 insertions(+), 19 deletions(-) diff --git a/src/listener/mod.rs b/src/listener/mod.rs index 2d469872d..a5ca30dcb 100644 --- a/src/listener/mod.rs +++ b/src/listener/mod.rs @@ -26,9 +26,9 @@ pub use to_listener::ToListener; #[cfg(feature = "h1-server")] pub(crate) use parsed_listener::ParsedListener; #[cfg(feature = "h1-server")] -pub(crate) use tcp_listener::TcpListener; +pub use tcp_listener::TcpListener; #[cfg(all(unix, feature = "h1-server"))] -pub(crate) use unix_listener::UnixListener; +pub use unix_listener::UnixListener; /// The Listener trait represents an implementation of http transport for a tide /// application. In order to provide a Listener to tide, you will also need to diff --git a/src/listener/tcp_listener.rs b/src/listener/tcp_listener.rs index 9ca3585f8..2912a4937 100644 --- a/src/listener/tcp_listener.rs +++ b/src/listener/tcp_listener.rs @@ -4,10 +4,14 @@ use crate::listener::Listener; use crate::{log, Server}; use std::fmt::{self, Display, Formatter}; +use std::str::FromStr; + +use std::net::ToSocketAddrs; use async_std::net::{self, SocketAddr, TcpStream}; use async_std::prelude::*; use async_std::{io, task}; +use http_types::Url; /// This represents a tide [Listener](crate::listener::Listener) that /// wraps an [async_std::net::TcpListener]. It is implemented as an @@ -15,33 +19,81 @@ use async_std::{io, task}; /// from a SocketAddr spec that has not yet been bound OR from a bound /// TcpListener. /// -/// This is currently crate-visible only, and tide users are expected -/// to create these through [ToListener](crate::ToListener) conversions. +/// ```rust,no_run +/// # async_std::task::block_on(async { +/// tide::new().listen( +/// TcpListener::new("localhost:8080") +/// .with_nodelay(true) +/// .with_ttl(100) +/// ).await +/// # }); +/// ``` pub struct TcpListener { addrs: Option>, listener: Option, server: Option>, info: Option, + tcp_nodelay: Option, + tcp_ttl: Option, } -impl TcpListener { - pub fn from_addrs(addrs: Vec) -> Self { - Self { - addrs: Some(addrs), +impl Default for TcpListener { + fn default() -> Self { + TcpListener { + addrs: None, listener: None, server: None, info: None, + tcp_nodelay: None, + tcp_ttl: None, } } +} + +impl TcpListener { + pub fn new(s: &str) -> crate::Result { + Self::from_str(s) + } + + pub fn from_addrs(addrs: impl std::net::ToSocketAddrs) -> crate::Result { + Ok(Self { + addrs: Some(addrs.to_socket_addrs()?.collect()), + ..Default::default() + }) + } pub fn from_listener(tcp_listener: impl Into) -> Self { Self { - addrs: None, listener: Some(tcp_listener.into()), - server: None, - info: None, + ..Default::default() } } + + pub fn set_nodelay(&mut self, nodelay: bool) { + self.tcp_nodelay = Some(nodelay); + } + + pub fn nodelay(&self) -> Option { + self.tcp_nodelay + } + + pub fn with_nodelay(mut self, nodelay: bool) -> Self { + self.set_nodelay(nodelay); + self + } + + pub fn set_ttl(&mut self, ttl: u32) { + self.tcp_ttl = Some(ttl); + } + + pub fn ttl(&self) -> Option { + self.tcp_ttl + } + + pub fn with_ttl(mut self, ttl: u32) -> Self { + self.set_ttl(ttl); + self + } } fn handle_tcp(app: Server, stream: TcpStream) { @@ -61,6 +113,25 @@ fn handle_tcp(app: Server, stream: }); } +impl FromStr for TcpListener { + type Err = crate::Error; + + fn from_str(s: &str) -> Result { + if let Ok(addrs) = s.to_socket_addrs() { + Self::from_addrs(addrs.collect::>().as_slice()) + } else { + let url = Url::parse(s)?; + if url.scheme() == "http" { + Self::from_addrs(url.socket_addrs(|| None)?.as_slice()) + } else { + Err(crate::http::format_err!( + "tcp listener must be used with a http scheme" + )) + } + } + } +} + #[async_trait::async_trait] impl Listener for TcpListener where @@ -111,6 +182,14 @@ where } Ok(stream) => { + if let Some(nodelay) = self.tcp_nodelay { + stream.set_nodelay(nodelay)?; + } + + if let Some(ttl) = self.tcp_ttl { + stream.set_ttl(ttl)?; + } + handle_tcp(server.clone(), stream); } }; diff --git a/src/listener/to_listener_impls.rs b/src/listener/to_listener_impls.rs index 1e92fef0d..0ed748c7c 100644 --- a/src/listener/to_listener_impls.rs +++ b/src/listener/to_listener_impls.rs @@ -34,9 +34,10 @@ where } } - "tcp" | "http" => Ok(ParsedListener::Tcp(TcpListener::from_addrs( - self.socket_addrs(|| Some(80))?, - ))), + "tcp" | "http" => Ok(ParsedListener::Tcp( + TcpListener::from_addrs(self.socket_addrs(|| None)?.as_slice()) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?, + )), "tls" | "ssl" | "https" => Err(io::Error::new( io::ErrorKind::Other, @@ -79,9 +80,10 @@ where fn to_listener(self) -> io::Result { if let Ok(socket_addrs) = self.to_socket_addrs() { - Ok(ParsedListener::Tcp(TcpListener::from_addrs( - socket_addrs.collect(), - ))) + Ok(ParsedListener::Tcp( + TcpListener::from_addrs(socket_addrs.collect::>().as_slice()) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?, + )) } else if let Ok(url) = Url::parse(self) { ToListener::::to_listener(url) } else { @@ -162,7 +164,8 @@ where type Listener = TcpListener; fn to_listener(self) -> io::Result { - Ok(TcpListener::from_addrs(self.to_socket_addrs()?.collect())) + TcpListener::from_addrs(self) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string())) } } @@ -245,7 +248,8 @@ where { type Listener = TcpListener; fn to_listener(self) -> io::Result { - Ok(TcpListener::from_addrs(vec![self])) + TcpListener::from_addrs(self) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string())) } }