replace trust-dns with blocking code

This commit is contained in:
Nikolay Kim 2021-04-03 14:45:27 +06:00
parent cb9e3ffeda
commit 111e4ec717
15 changed files with 90 additions and 152 deletions

View file

@ -1,5 +1,9 @@
# Changes # Changes
## [0.4.3] - 2021-04-03
* Disable some of regex features
## [0.4.2] - 2021-03-16 ## [0.4.2] - 2021-03-16
* Use `IntoPattern` for prefix resources * Use `IntoPattern` for prefix resources

View file

@ -1,6 +1,6 @@
[package] [package]
name = "ntex-router" name = "ntex-router"
version = "0.4.2" version = "0.4.3"
authors = ["ntex contributors <team@ntex.rs>"] authors = ["ntex contributors <team@ntex.rs>"]
description = "Path router" description = "Path router"
keywords = ["ntex"] keywords = ["ntex"]
@ -17,11 +17,11 @@ path = "src/lib.rs"
default = ["http"] default = ["http"]
[dependencies] [dependencies]
regex = "1.4"
serde = "1.0" serde = "1.0"
bytestring = "1.0" bytestring = "1.0"
log = "0.4" log = "0.4"
http = { version = "0.2", optional = true } http = { version = "0.2", optional = true }
regex = { version = "1.4.5", default-features = false, features = ["std"] }
[dev-dependencies] [dev-dependencies]
http = "0.2" http = "0.2"

View file

@ -6,6 +6,8 @@
* util: add custom Ready, Either future and several helper functions * util: add custom Ready, Either future and several helper functions
* drop trust-dns, use blocking calls
* reduce futures crate dependencies * reduce futures crate dependencies
## [0.3.13] - 2021-03-26 ## [0.3.13] - 2021-03-26

View file

@ -62,7 +62,7 @@ num_cpus = "1.13"
nanorand = "0.5" nanorand = "0.5"
percent-encoding = "2.1" percent-encoding = "2.1"
pin-project-lite = "0.2" pin-project-lite = "0.2"
regex = "1.4" regex = { version = "1.4.5", default-features = false, features = ["std"] }
sha-1 = "0.9" sha-1 = "0.9"
slab = "0.4" slab = "0.4"
serde = { version = "1.0", features=["derive"] } serde = { version = "1.0", features=["derive"] }
@ -73,10 +73,6 @@ url = "2.1"
coo-kie = { version = "0.15", package = "cookie", optional = true } coo-kie = { version = "0.15", package = "cookie", optional = true }
tokio = { version = "1", default-features=false, features = ["sync"] } tokio = { version = "1", default-features=false, features = ["sync"] }
# resolver
trust-dns-proto = { version = "0.20", default-features = false }
trust-dns-resolver = { version = "0.20", default-features = false, features=["system-config", "tokio-runtime"] }
# openssl # openssl
open-ssl = { version="0.10", package = "openssl", optional = true } open-ssl = { version="0.10", package = "openssl", optional = true }
tokio-openssl = { version = "0.6.1", optional = true } tokio-openssl = { version = "0.6.1", optional = true }

View file

@ -1,13 +1,13 @@
use std::io; use std::io;
use derive_more::{Display, From}; use derive_more::{Display, From};
use trust_dns_resolver::error::ResolveError;
#[derive(Debug, From, Display)] #[derive(Debug, From, Display)]
pub enum ConnectError { pub enum ConnectError {
/// Failed to resolve the hostname /// Failed to resolve the hostname
#[from(ignore)]
#[display(fmt = "Failed resolving hostname: {}", _0)] #[display(fmt = "Failed resolving hostname: {}", _0)]
Resolver(ResolveError), Resolver(io::Error),
/// No dns records /// No dns records
#[display(fmt = "No dns records found for the input")] #[display(fmt = "No dns records found for the input")]

View file

@ -13,42 +13,13 @@ pub mod openssl;
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
pub mod rustls; pub mod rustls;
pub use trust_dns_resolver::config::{self, ResolverConfig, ResolverOpts}; use crate::rt::net::TcpStream;
use trust_dns_resolver::system_conf::read_system_conf;
pub use trust_dns_resolver::{error::ResolveError, TokioAsyncResolver as DnsResolver};
use crate::rt::{net::TcpStream, Arbiter};
pub use self::error::ConnectError; pub use self::error::ConnectError;
pub use self::message::{Address, Connect}; pub use self::message::{Address, Connect};
pub use self::resolve::Resolver; pub use self::resolve::Resolver;
pub use self::service::Connector; pub use self::service::Connector;
pub fn start_resolver(cfg: ResolverConfig, opts: ResolverOpts) -> DnsResolver {
DnsResolver::tokio(cfg, opts).unwrap()
}
struct DefaultResolver(DnsResolver);
pub fn default_resolver() -> DnsResolver {
if Arbiter::contains_item::<DefaultResolver>() {
Arbiter::get_item(|item: &DefaultResolver| item.0.clone())
} else {
let (cfg, opts) = match read_system_conf() {
Ok((cfg, opts)) => (cfg, opts),
Err(e) => {
log::error!("TRust-DNS can not load system config: {}", e);
(ResolverConfig::default(), ResolverOpts::default())
}
};
let resolver = DnsResolver::tokio(cfg, opts).unwrap();
Arbiter::set_item(DefaultResolver(resolver.clone()));
resolver
}
}
/// Resolve and connect to remote host /// Resolve and connect to remote host
pub fn connect<T, U>(message: U) -> impl Future<Output = Result<TcpStream, ConnectError>> pub fn connect<T, U>(message: U) -> impl Future<Output = Result<TcpStream, ConnectError>>
where where
@ -56,6 +27,6 @@ where
Connect<T>: From<U>, Connect<T>: From<U>,
{ {
service::ConnectServiceResponse::new(Box::pin( service::ConnectServiceResponse::new(Box::pin(
Resolver::new(default_resolver()).lookup(message.into()), Resolver::new().lookup(message.into()),
)) ))
} }

View file

@ -7,7 +7,7 @@ use crate::rt::net::TcpStream;
use crate::service::{Service, ServiceFactory}; use crate::service::{Service, ServiceFactory};
use crate::util::Ready; use crate::util::Ready;
use super::{Address, Connect, ConnectError, Connector, DnsResolver}; use super::{Address, Connect, ConnectError, Connector};
pub struct OpensslConnector<T> { pub struct OpensslConnector<T> {
connector: Connector<T>, connector: Connector<T>,
@ -22,14 +22,6 @@ impl<T> OpensslConnector<T> {
openssl: connector, openssl: connector,
} }
} }
/// Construct new connect service with custom dns resolver
pub fn with_resolver(connector: SslConnector, resolver: DnsResolver) -> Self {
OpensslConnector {
connector: Connector::new(resolver),
openssl: connector,
}
}
} }
impl<T: Address + 'static> OpensslConnector<T> { impl<T: Address + 'static> OpensslConnector<T> {

View file

@ -1,33 +1,22 @@
use std::{ use std::{fmt, future::Future, io, marker, net, pin::Pin, task::Context, task::Poll};
fmt, future::Future, marker::PhantomData, net::SocketAddr, pin::Pin, rc::Rc,
task::Context, task::Poll,
};
use super::{default_resolver, Address, Connect, ConnectError, DnsResolver}; use super::{Address, Connect, ConnectError};
use crate::service::{Service, ServiceFactory}; use crate::service::{Service, ServiceFactory};
use crate::util::{Either, Ready}; use crate::util::{Either, Ready};
/// DNS Resolver Service /// DNS Resolver Service
pub struct Resolver<T> { pub struct Resolver<T>(marker::PhantomData<T>);
resolver: Rc<DnsResolver>,
_t: PhantomData<T>,
}
impl<T> fmt::Debug for Resolver<T> { impl<T> fmt::Debug for Resolver<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Resolver") f.debug_struct("Resolver").finish()
.field("resolver", &self.resolver)
.finish()
} }
} }
impl<T> Resolver<T> { impl<T> Resolver<T> {
/// Create new resolver instance with custom configuration and options. /// Create new resolver instance with custom configuration and options.
pub fn new(resolver: DnsResolver) -> Self { pub fn new() -> Self {
Resolver { Resolver(marker::PhantomData)
resolver: Rc::new(resolver),
_t: PhantomData,
}
} }
} }
@ -40,24 +29,29 @@ impl<T: Address> Resolver<T> {
if req.addr.is_some() || req.req.addr().is_some() { if req.addr.is_some() || req.req.addr().is_some() {
Either::Right(Ready::ok(req)) Either::Right(Ready::ok(req))
} else if let Ok(ip) = req.host().parse() { } else if let Ok(ip) = req.host().parse() {
req.addr = Some(Either::Left(SocketAddr::new(ip, req.port()))); req.addr = Some(Either::Left(net::SocketAddr::new(ip, req.port())));
Either::Right(Ready::ok(req)) Either::Right(Ready::ok(req))
} else { } else {
trace!("DNS resolver: resolving host {:?}", req.host()); trace!("DNS resolver: resolving host {:?}", req.host());
let resolver = self.resolver.clone();
Either::Left(async move { Either::Left(async move {
let fut = if let Some(host) = req.host().splitn(2, ':').next() { let host = if req.host().contains(':') {
resolver.lookup_ip(host) req.host().to_string()
} else { } else {
resolver.lookup_ip(req.host()) format!("{}:{}", req.host(), req.port())
}; };
let fut = crate::rt::task::spawn_blocking(move || {
net::ToSocketAddrs::to_socket_addrs(&host)
});
match fut.await { match fut.await {
Ok(ips) => { Ok(Ok(ips)) => {
let port = req.port(); let port = req.port();
let req = req let req = req.set_addrs(ips.map(|mut ip| {
.set_addrs(ips.iter().map(|ip| SocketAddr::new(ip, port))); ip.set_port(port);
ip
}));
trace!( trace!(
"DNS resolver: host {:?} resolved to {:?}", "DNS resolver: host {:?} resolved to {:?}",
@ -71,13 +65,24 @@ impl<T: Address> Resolver<T> {
Ok(req) Ok(req)
} }
} }
Ok(Err(e)) => {
trace!(
"DNS resolver: failed to resolve host {:?} err: {}",
req.host(),
e
);
Err(ConnectError::Resolver(e))
}
Err(e) => { Err(e) => {
trace!( trace!(
"DNS resolver: failed to resolve host {:?} err: {}", "DNS resolver: failed to resolve host {:?} err: {}",
req.host(), req.host(),
e e
); );
Err(e.into()) Err(ConnectError::Resolver(io::Error::new(
io::ErrorKind::Other,
e,
)))
} }
} }
}) })
@ -87,19 +92,13 @@ impl<T: Address> Resolver<T> {
impl<T> Default for Resolver<T> { impl<T> Default for Resolver<T> {
fn default() -> Resolver<T> { fn default() -> Resolver<T> {
Resolver { Resolver(marker::PhantomData)
resolver: Rc::new(default_resolver()),
_t: PhantomData,
}
} }
} }
impl<T> Clone for Resolver<T> { impl<T> Clone for Resolver<T> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Resolver { Resolver(marker::PhantomData)
resolver: self.resolver.clone(),
_t: PhantomData,
}
} }
} }
@ -141,7 +140,7 @@ mod tests {
#[crate::rt_test] #[crate::rt_test]
async fn resolver() { async fn resolver() {
let resolver = Resolver::new(DnsResolver::tokio_from_system_conf().unwrap()); let resolver = Resolver::new();
assert!(format!("{:?}", resolver).contains("Resolver")); assert!(format!("{:?}", resolver).contains("Resolver"));
let srv = resolver.new_service(()).await.unwrap(); let srv = resolver.new_service(()).await.unwrap();
assert!(lazy(|cx| srv.poll_ready(cx)).await.is_ready()); assert!(lazy(|cx| srv.poll_ready(cx)).await.is_ready());
@ -152,7 +151,7 @@ mod tests {
let res = srv.call(Connect::new("---11213")).await; let res = srv.call(Connect::new("---11213")).await;
assert!(res.is_err()); assert!(res.is_err());
let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap(); let addr: net::SocketAddr = "127.0.0.1:8080".parse().unwrap();
let res = srv let res = srv
.call(Connect::new("www.rust-lang.org").set_addrs(vec![addr])) .call(Connect::new("www.rust-lang.org").set_addrs(vec![addr]))
.await .await

View file

@ -10,7 +10,7 @@ use crate::rt::net::TcpStream;
use crate::service::{Service, ServiceFactory}; use crate::service::{Service, ServiceFactory};
use crate::util::Ready; use crate::util::Ready;
use super::{Address, Connect, ConnectError, Connector, DnsResolver}; use super::{Address, Connect, ConnectError, Connector};
/// Rustls connector factory /// Rustls connector factory
pub struct RustlsConnector<T> { pub struct RustlsConnector<T> {
@ -25,14 +25,6 @@ impl<T> RustlsConnector<T> {
connector: Connector::default(), connector: Connector::default(),
} }
} }
/// Construct new connect service with custom dns resolver
pub fn with_resolver(config: Arc<ClientConfig>, resolver: DnsResolver) -> Self {
RustlsConnector {
config,
connector: Connector::new(resolver),
}
}
} }
impl<T: Address + 'static> RustlsConnector<T> { impl<T: Address + 'static> RustlsConnector<T> {

View file

@ -5,7 +5,7 @@ use crate::rt::net::TcpStream;
use crate::service::{Service, ServiceFactory}; use crate::service::{Service, ServiceFactory};
use crate::util::{Either, Ready}; use crate::util::{Either, Ready};
use super::{Address, Connect, ConnectError, DnsResolver, Resolver}; use super::{Address, Connect, ConnectError, Resolver};
pub struct Connector<T> { pub struct Connector<T> {
resolver: Resolver<T>, resolver: Resolver<T>,
@ -13,9 +13,9 @@ pub struct Connector<T> {
impl<T> Connector<T> { impl<T> Connector<T> {
/// Construct new connect service with custom dns resolver /// Construct new connect service with custom dns resolver
pub fn new(resolver: DnsResolver) -> Self { pub fn new() -> Self {
Connector { Connector {
resolver: Resolver::new(resolver), resolver: Resolver::new(),
} }
} }
} }

View file

@ -1,7 +1,7 @@
use std::{rc::Rc, task::Context, task::Poll, time::Duration}; use std::{rc::Rc, task::Context, task::Poll, time::Duration};
use crate::codec::{AsyncRead, AsyncWrite}; use crate::codec::{AsyncRead, AsyncWrite};
use crate::connect::{self, Connect as TcpConnect, Connector as TcpConnector}; use crate::connect::{Connect as TcpConnect, Connector as TcpConnector};
use crate::http::{Protocol, Uri}; use crate::http::{Protocol, Uri};
use crate::service::{apply_fn, boxed, Service}; use crate::service::{apply_fn, boxed, Service};
use crate::util::timeout::{TimeoutError, TimeoutService}; use crate::util::timeout::{TimeoutError, TimeoutService};
@ -44,8 +44,6 @@ pub struct Connector {
limit: usize, limit: usize,
connector: BoxedConnector, connector: BoxedConnector,
ssl_connector: Option<BoxedConnector>, ssl_connector: Option<BoxedConnector>,
#[allow(dead_code)]
resolver: connect::DnsResolver,
} }
trait Io: AsyncRead + AsyncWrite + Unpin {} trait Io: AsyncRead + AsyncWrite + Unpin {}
@ -53,15 +51,15 @@ impl<T: AsyncRead + AsyncWrite + Unpin> Io for T {}
impl Default for Connector { impl Default for Connector {
fn default() -> Self { fn default() -> Self {
Connector::new(connect::default_resolver()) Connector::new()
} }
} }
impl Connector { impl Connector {
pub fn new(resolver: connect::DnsResolver) -> Connector { pub fn new() -> Connector {
let conn = Connector { let conn = Connector {
connector: boxed::service( connector: boxed::service(
TcpConnector::new(resolver.clone()) TcpConnector::new()
.map(|io| (Box::new(io) as Box<dyn Io>, Protocol::Http1)) .map(|io| (Box::new(io) as Box<dyn Io>, Protocol::Http1))
.map_err(ConnectError::from), .map_err(ConnectError::from),
), ),
@ -71,7 +69,6 @@ impl Connector {
conn_keep_alive: Duration::from_secs(15), conn_keep_alive: Duration::from_secs(15),
disconnect_timeout: Duration::from_millis(3000), disconnect_timeout: Duration::from_millis(3000),
limit: 100, limit: 100,
resolver,
}; };
#[cfg(feature = "openssl")] #[cfg(feature = "openssl")]
@ -114,23 +111,19 @@ impl Connector {
pub fn openssl(self, connector: OpensslConnector) -> Self { pub fn openssl(self, connector: OpensslConnector) -> Self {
use crate::connect::openssl::OpensslConnector; use crate::connect::openssl::OpensslConnector;
let resolver = self.resolver.clone();
const H2: &[u8] = b"h2"; const H2: &[u8] = b"h2";
self.secure_connector(OpensslConnector::with_resolver(connector, resolver).map( self.secure_connector(OpensslConnector::new(connector).map(|sock| {
|sock| { let h2 = sock
let h2 = sock .ssl()
.ssl() .selected_alpn_protocol()
.selected_alpn_protocol() .map(|protos| protos.windows(2).any(|w| w == H2))
.map(|protos| protos.windows(2).any(|w| w == H2)) .unwrap_or(false);
.unwrap_or(false); if h2 {
if h2 { (sock, Protocol::Http2)
(sock, Protocol::Http2) } else {
} else { (sock, Protocol::Http1)
(sock, Protocol::Http1) }
} }))
},
))
} }
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
@ -138,24 +131,20 @@ impl Connector {
pub fn rustls(self, connector: Arc<ClientConfig>) -> Self { pub fn rustls(self, connector: Arc<ClientConfig>) -> Self {
use crate::connect::rustls::{RustlsConnector, Session}; use crate::connect::rustls::{RustlsConnector, Session};
let resolver = self.resolver.clone();
const H2: &[u8] = b"h2"; const H2: &[u8] = b"h2";
self.secure_connector(RustlsConnector::with_resolver(connector, resolver).map( self.secure_connector(RustlsConnector::new(connector).map(|sock| {
|sock| { let h2 = sock
let h2 = sock .get_ref()
.get_ref() .1
.1 .get_alpn_protocol()
.get_alpn_protocol() .map(|protos| protos.windows(2).any(|w| w == H2))
.map(|protos| protos.windows(2).any(|w| w == H2)) .unwrap_or(false);
.unwrap_or(false); if h2 {
if h2 { (Box::new(sock) as Box<dyn Io>, Protocol::Http2)
(Box::new(sock) as Box<dyn Io>, Protocol::Http2) } else {
} else { (Box::new(sock) as Box<dyn Io>, Protocol::Http1)
(Box::new(sock) as Box<dyn Io>, Protocol::Http1) }
} }))
},
))
} }
/// Set total number of simultaneous connections per type of scheme. /// Set total number of simultaneous connections per type of scheme.

View file

@ -6,7 +6,6 @@ use serde_json::error::Error as JsonError;
#[cfg(feature = "openssl")] #[cfg(feature = "openssl")]
use crate::connect::openssl::{HandshakeError, SslError}; use crate::connect::openssl::{HandshakeError, SslError};
use crate::connect::ResolveError;
use crate::http::error::{HttpError, ParseError, PayloadError}; use crate::http::error::{HttpError, ParseError, PayloadError};
use crate::http::header::HeaderValue; use crate::http::header::HeaderValue;
@ -91,8 +90,9 @@ pub enum ConnectError {
SslHandshakeError(String), SslHandshakeError(String),
/// Failed to resolve the hostname /// Failed to resolve the hostname
#[from(ignore)]
#[display(fmt = "Failed resolving hostname: {}", _0)] #[display(fmt = "Failed resolving hostname: {}", _0)]
Resolver(ResolveError), Resolver(io::Error),
/// No dns records /// No dns records
#[display(fmt = "No dns records found for the input")] #[display(fmt = "No dns records found for the input")]

View file

@ -4,7 +4,7 @@ use bytes::Bytes;
use futures::SinkExt; use futures::SinkExt;
use ntex::codec::{BytesCodec, Framed}; use ntex::codec::{BytesCodec, Framed};
use ntex::connect::{Connect, ResolverConfig, ResolverOpts}; use ntex::connect::Connect;
use ntex::rt::net::TcpStream; use ntex::rt::net::TcpStream;
use ntex::server::test_server; use ntex::server::test_server;
use ntex::service::{fn_service, Service, ServiceFactory}; use ntex::service::{fn_service, Service, ServiceFactory};
@ -53,14 +53,13 @@ async fn test_static_str() {
}) })
}); });
let resolver = ntex::connect::default_resolver(); let conn = ntex::connect::Connector::new();
let conn = ntex::connect::Connector::new(resolver.clone());
let con = conn.call(Connect::with("10", srv.addr())).await.unwrap(); let con = conn.call(Connect::with("10", srv.addr())).await.unwrap();
assert_eq!(con.peer_addr().unwrap(), srv.addr()); assert_eq!(con.peer_addr().unwrap(), srv.addr());
let connect = Connect::new("127.0.0.1".to_owned()); let connect = Connect::new("127.0.0.1".to_owned());
let conn = ntex::connect::Connector::new(resolver); let conn = ntex::connect::Connector::new();
let con = conn.call(connect).await; let con = conn.call(connect).await;
assert!(con.is_err()); assert!(con.is_err());
} }
@ -75,13 +74,7 @@ async fn test_new_service() {
}) })
}); });
let resolver = ntex::connect::start_resolver( let factory = ntex::connect::Connector::new();
ResolverConfig::default(),
ResolverOpts::default(),
);
let factory = ntex::connect::Connector::new(resolver);
let conn = factory.new_service(()).await.unwrap(); let conn = factory.new_service(()).await.unwrap();
let con = conn.call(Connect::with("10", srv.addr())).await.unwrap(); let con = conn.call(Connect::with("10", srv.addr())).await.unwrap();
assert_eq!(con.peer_addr().unwrap(), srv.addr()); assert_eq!(con.peer_addr().unwrap(), srv.addr());

View file

@ -176,7 +176,7 @@ async fn test_timeout() {
let connector = Connector::default() let connector = Connector::default()
.connector( .connector(
ntex::connect::Connector::new(ntex::connect::default_resolver()) ntex::connect::Connector::new()
.map(|sock| (sock, ntex::http::Protocol::Http1)), .map(|sock| (sock, ntex::http::Protocol::Http1)),
) )
.timeout(Duration::from_secs(15)) .timeout(Duration::from_secs(15))

View file

@ -143,7 +143,7 @@ async fn test_chunked_payload() {
let mut data = String::new(); let mut data = String::new();
let _ = stream.read_to_string(&mut data); let _ = stream.read_to_string(&mut data);
let re = Regex::new(r"size=(\d+)").unwrap(); let re = Regex::new(r"size=([0-9]+)").unwrap();
let size: usize = match re.captures(&data) { let size: usize = match re.captures(&data) {
Some(caps) => caps.get(1).unwrap().as_str().parse().unwrap(), Some(caps) => caps.get(1).unwrap().as_str().parse().unwrap(),
None => panic!("Failed to find size in HTTP Response: {}", data), None => panic!("Failed to find size in HTTP Response: {}", data),