From 3324575c9603b8031b1ded599f49faa5e6e611a7 Mon Sep 17 00:00:00 2001 From: Christopher Brickley Date: Sun, 8 Jan 2017 15:13:58 -0500 Subject: [PATCH 01/27] WIP(tls): add TlsStream support When `-- features tls` is specified for tarpc, RPC communication can also occur over a `TlsStream` instead of a `TcpStream`. The functional tests have been refactored to use a common set of functions for constructing the client and server structs so that all the tests are shared across non-tls and tls test runs. --- .travis.yml | 4 +- Cargo.toml | 23 ++- README.md | 109 +++++++++++++++ src/client.rs | 350 ++++++++++++++++++++++++++++++++++++++-------- src/errors.rs | 6 + src/lib.rs | 68 ++++++++- src/macros.rs | 334 ++++++++++++++++++++++++++++++++----------- src/server.rs | 186 ++++++++++++++++++------ test/identity.p12 | Bin 0 -> 3386 bytes test/root-ca.der | Bin 0 -> 865 bytes test/root-ca.pem | 21 +++ 11 files changed, 911 insertions(+), 190 deletions(-) create mode 100644 test/identity.p12 create mode 100644 test/root-ca.der create mode 100644 test/root-ca.pem diff --git a/.travis.yml b/.travis.yml index 9205d1da..10f8c56f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,7 @@ addons: - libcurl4-openssl-dev - libelf-dev - libdw-dev + - libssl-dev before_script: - | @@ -21,7 +22,8 @@ before_script: script: - | - travis-cargo build && travis-cargo test + travis-cargo build && travis-cargo test && + travis-cargo build --features tls && travis-cargo test --features tls after_success: - travis-cargo coveralls --no-sudo diff --git a/Cargo.toml b/Cargo.toml index e4eacaea..60c618b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" documentation = "https://docs.rs/tarpc" homepage = "https://github.com/google/tarpc" repository = "https://github.com/google/tarpc" -keywords = ["rpc", "protocol", "remote", "procedure", "serialize"] +keywords = ["rpc", "protocol", "remote", "procedure", "serialize", "tls"] readme = "README.md" description = "An RPC framework for Rust with a focus on ease of use." @@ -14,30 +14,45 @@ description = "An RPC framework for Rust with a focus on ease of use." bincode = "0.6" byteorder = "0.5" bytes = { git = "https://github.com/carllerche/bytes" } +cfg-if = "0.1.0" futures = { git = "https://github.com/alexcrichton/futures-rs" } lazy_static = "0.2" log = "0.3" +net2 = "0.2" scoped-pool = "1.0" serde = "0.8" serde_derive = "0.8" tarpc-plugins = { path = "src/plugins" } take = "0.1" tokio-service = { git = "https://github.com/tokio-rs/tokio-service" } -tokio-proto = { git = "https://github.com/tokio-rs/tokio-proto" } +tokio-proto = { git = "https://github.com/tokio-rs/tokio-proto", rev = "cdbdb11f3349a5a540cf8ddb60431ebbca4be712" } tokio-core = { git = "https://github.com/tokio-rs/tokio-core" } -net2 = "0.2" [replace] "tokio-core:0.1.3" = { git = "https://github.com/tokio-rs/tokio-core" } "futures:0.1.7" = { git = "https://github.com/alexcrichton/futures-rs" } +"native-tls:0.1.0" = { git = "https://github.com/sfackler/rust-native-tls" } + +[dependencies.native-tls] +optional = true +git = "https://github.com/sfackler/rust-native-tls" + +[dependencies.tokio-tls] +optional = true +git = "https://github.com/tokio-rs/tokio-tls" [dev-dependencies] chrono = "0.2" +clap = "2.0" env_logger = "0.3" futures-cpupool = "0.1" -clap = "2.0" + +[target.'cfg(target_os = "macos")'.dev-dependencies] +security-framework = "0.1" [features] +default = [] +tls = ["tokio-tls", "native-tls"] unstable = ["serde/unstable"] [workspace] diff --git a/README.md b/README.md index b8b3e754..14c6d5dd 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,115 @@ fn main() { } ``` +## Example: TLS + Sync + +Instead of using a `TcpStream`, a `TlsStream` will be used instead +when the `tls` feature of `tarpc` is enabled. When using TLS, some additional +information is required from the user. You will need to be make the +`TlsAcceptor` and `TlsClientContext` structs. The `TlsAcceptor` and +`TlsConnector` (which is required in `TlsClientContext`) structs are defined in +the [native-tls](https://github.com/sfackler/rust-native-tls) crate which is +exposed by `tarpc`. + +```rust +#![feature(conservative_impl_trait, plugin)] +#![plugin(tarpc_plugins)] + +#[macro_use] +extern crate tarpc; + +use tarpc::sync::Connect; +use tarpc::util::Never; +use tarpc::TlsClientContext; +use tarpc::native_tls::{Pkcs12, TlsAcceptor, TlsConnector}; + +service! { + rpc hello(name: String) -> String; +} + +#[derive(Clone)] +struct HelloServer; + +impl SyncService for HelloServer { + fn hello(&mut self, name: String) -> Result { + Ok(format!("Hello, {}!", name)) + } +} + +fn tls_context() -> (TlsAcceptor, TlsClientContext) { + let buf = include_bytes!("test/identity.p12"); + let pkcs12 = Pkcs12::from_der(buf, "password").unwrap(); + let acceptor = TlsAcceptor::builder(pkcs12).unwrap().build().unwrap(); + let client_cx = TlsClientContext::try_new("foobar.com").unwrap(); + + (acceptor, client_cx) +} + +fn main() { + let addr = "localhost:10000"; + let (acceptor, client_cx) = tls_context(); + let _server = HelloServer.listen(addr, acceptor); + let mut client = SyncClient::connect(addr, client_cx).unwrap(); + println!("{}", client.hello("Mom".to_string()).unwrap()); +} +``` + +## Example: Futures + TLS + +Here's the same TLS service, implemented using futures. + +```rust +#![feature(conservative_impl_trait, plugin)] +#![plugin(tarpc_plugins)] + +extern crate futures; +#[macro_use] +extern crate tarpc; +extern crate tokio_core; + +use futures::Future; +use tarpc::future::Connect; +use tarpc::util::{FirstSocketAddr, Never}; +use tokio_core::reactor; + +service! { + rpc hello(name: String) -> String; +} + +#[derive(Clone)] +struct HelloServer; + +impl FutureService for HelloServer { + type HelloFut = futures::Finished; + + fn hello(&mut self, name: String) -> Self::HelloFut { + futures::finished(format!("Hello, {}!", name)) + } +} + +fn tls_context() -> (TlsAcceptor, TlsClientContext) { + let buf = include_bytes!("test/identity.p12"); + let pkcs12 = Pkcs12::from_der(buf, "password").unwrap(); + let acceptor = TlsAcceptor::builder(pkcs12).unwrap().build().unwrap(); + let client_cx = TlsClientContext::try_new("foobar.com").unwrap(); + + (acceptor, client_cx) +} + +fn main() { + let addr = "localhost:10000".first_socket_addr(); + let mut core = reactor::Core::new().unwrap(); + let (acceptor, client_cx) = tls_context(); + HelloServer.listen_with(addr, core.handle(), acceptor).unwrap(); + core.run( + FutureClient::connect(&addr, client_cx) + .map_err(tarpc::Error::from) + .and_then(|mut client| client.hello("Mom".to_string())) + .map(|resp| println!("{}", resp)) + ).unwrap(); +} +``` + ## Documentation Use `cargo doc` as you normally would to see the documentation created for all items expanded by a `service!` invocation. diff --git a/src/client.rs b/src/client.rs index 391e69c8..b5367dda 100644 --- a/src/client.rs +++ b/src/client.rs @@ -10,42 +10,97 @@ use protocol::Proto; use serde::{Deserialize, Serialize}; use std::fmt; use std::io; +use std::marker::PhantomData; +use tokio_core::io::Io; use tokio_core::net::TcpStream; use tokio_proto::BindClient as ProtoBindClient; use tokio_proto::multiplex::Multiplex; use tokio_service::Service; type WireResponse = Result>, DeserializeError>; -type ResponseFuture = futures::Map< as Service>::Future, - fn(WireResponse) -> Result>>; -type BindClient = - >> as ProtoBindClient>::BindClient; +type BindClient = >> as ProtoBindClient>::BindClient; + +#[cfg(feature = "tls")] +pub mod tls { + use native_tls::TlsConnector; + use super::*; + use tokio_tls::TlsStream; + + /// TLS context + pub struct TlsClientContext { + /// Domain to connect to + pub domain: String, + /// TLS connector + pub tls_connector: TlsConnector, + } + + impl TlsClientContext { + /// Try to make a new `TlsClientContext`, providing the domain the client will + /// connect to. + pub fn try_new>(domain: S) + -> Result { + Ok(TlsClientContext { + domain: domain.into(), + tls_connector: TlsConnector::builder()?.build()?, + }) + } + } + + impl Config> { + /// Construct a new `Config>` + pub fn new_tls(tls_client_cx: TlsClientContext) -> Self { + Config { + _stream: PhantomData, + tls_client_cx: Some(tls_client_cx), + } + } + } + + impl Service for Client> + where Req: Serialize + Sync + Send + 'static, + Resp: Deserialize + Sync + Send + 'static, + E: Deserialize + Sync + Send + 'static + { + type Request = Req; + type Response = Result>; + type Error = io::Error; + type Future = futures::Map<> as Service>::Future, + fn(WireResponse) -> Result>>; + + fn call(&self, request: Self::Request) -> Self::Future { + self.inner.call(request).map(Self::map_err) + } + } +} + +#[cfg(feature = "tls")] +use self::tls::*; /// A client that impls `tokio_service::Service` that writes and reads bytes. /// /// Typically, this would be combined with a serialization pre-processing step /// and a deserialization post-processing step. -pub struct Client +pub struct Client where Req: Serialize + 'static, Resp: Deserialize + 'static, E: Deserialize + 'static, + S: Io + 'static { - inner: BindClient, + inner: BindClient, } -impl Clone for Client +impl Clone for Client where Req: Serialize + 'static, Resp: Deserialize + 'static, E: Deserialize + 'static, + S: Io + 'static { fn clone(&self) -> Self { - Client { - inner: self.inner.clone(), - } + Client { inner: self.inner.clone() } } } -impl Service for Client +impl Service for Client where Req: Serialize + Sync + Send + 'static, Resp: Deserialize + Sync + Send + 'static, E: Deserialize + Sync + Send + 'static @@ -53,26 +108,27 @@ impl Service for Client type Request = Req; type Response = Result>; type Error = io::Error; - type Future = ResponseFuture; + type Future = futures::Map< as Service>::Future, + fn(WireResponse) -> Result>>; - fn call(&mut self, request: Self::Request) -> Self::Future { + fn call(&self, request: Self::Request) -> Self::Future { self.inner.call(request).map(Self::map_err) } } -impl Client +impl Client where Req: Serialize + 'static, Resp: Deserialize + 'static, E: Deserialize + 'static, + S: Io + 'static { - fn new(inner: BindClient) -> Self + fn new(inner: BindClient) -> Self where Req: Serialize + Sync + Send + 'static, Resp: Deserialize + Sync + Send + 'static, - E: Deserialize + Sync + Send + 'static + E: Deserialize + Sync + Send + 'static, + S: Io + 'static { - Client { - inner: inner, - } + Client { inner: inner } } fn map_err(resp: WireResponse) -> Result> { @@ -82,16 +138,45 @@ impl Client } } -impl fmt::Debug for Client +impl fmt::Debug for Client where Req: Serialize + 'static, Resp: Deserialize + 'static, E: Deserialize + 'static, + S: Io + 'static { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "Client {{ .. }}") } } +/// TODO: +pub struct Config + where S: Io +{ + _stream: PhantomData, + #[cfg(feature = "tls")] + tls_client_cx: Option, +} + +#[cfg(feature = "tls")] +impl Config { + /// Construct a new `Config` + pub fn new_tcp() -> Self { + Config { + _stream: PhantomData, + tls_client_cx: None, + } + } +} + +#[cfg(not(feature = "tls"))] +impl Config { + /// Construct a new `Config` + pub fn new_tcp() -> Self { + Config { _stream: PhantomData } + } +} + /// Exposes a trait for connecting asynchronously to servers. pub mod future { use future::REMOTE; @@ -101,47 +186,155 @@ pub mod future { use std::io; use std::marker::PhantomData; use std::net::SocketAddr; - use super::Client; + use super::{Client, Config}; + use tokio_core::io::Io; use tokio_core::net::TcpStream; - use tokio_core::{self, reactor}; + use tokio_core::reactor; use tokio_proto::BindClient; /// Types that can connect to a server asynchronously. - pub trait Connect<'a>: Sized { + pub trait Connect<'a, S>: Sized + where S: Io + { /// The type of the future returned when calling `connect`. type ConnectFut: Future + 'static; /// The type of the future returned when calling `connect_with`. type ConnectWithFut: Future + 'a; - /// Connects to a server located at the given address, using a remote to the default /// reactor. - fn connect(addr: &SocketAddr) -> Self::ConnectFut { - Self::connect_remotely(addr, &REMOTE) + fn connect(addr: &SocketAddr, config: Config) -> Self::ConnectFut { + Self::connect_remotely(addr, &REMOTE, config) } - /// Connects to a server located at the given address, using the given reactor remote. - fn connect_remotely(addr: &SocketAddr, remote: &reactor::Remote) -> Self::ConnectFut; + /// Connects to a server located at the given address, using the given reactor + /// remote. + fn connect_remotely(addr: &SocketAddr, + remote: &reactor::Remote, + config: Config) + -> Self::ConnectFut; - /// Connects to a server located at the given address, using the given reactor handle. - fn connect_with(addr: &SocketAddr, handle: &'a reactor::Handle) -> Self::ConnectWithFut; + /// Connects to a server located at the given address, using the given reactor + /// handle. + fn connect_with(addr: &SocketAddr, + handle: &'a reactor::Handle, + config: Config) + -> Self::ConnectWithFut; } /// A future that resolves to a `Client` or an `io::Error`. - pub struct ConnectFuture + pub struct ConnectWithFuture<'a, Req, Resp, E, S, F> + where S: Io, + F: Future + { + inner: futures::Map>, + } + + #[cfg(feature = "tls")] + mod tls { + use errors::native2io; + use super::*; + use super::super::tls::TlsClientContext; + use tokio_core::net::TcpStreamNew; + use tokio_tls::{ConnectAsync, TlsStream, TlsConnectorExt}; + + /// Provides the connection Fn impl for Tls + pub struct TlsConnectFn; + + impl FnOnce<(((TcpStream, TlsClientContext),))> for TlsConnectFn { + type Output = futures::MapErr, + fn(::native_tls::Error) -> io::Error>; + + extern "rust-call" fn call_once(self, + ((tcp, tls_client_cx),): ((TcpStream, + TlsClientContext),)) + -> Self::Output { + tls_client_cx.tls_connector + .connect_async(&tls_client_cx.domain, tcp) + .map_err(native2io) + } + } + + type TlsConnectFut = + futures::AndThen>, + futures::MapErr, + fn(::native_tls::Error) -> io::Error>, + TlsConnectFn>; + + impl<'a, Req, Resp, E> Connect<'a, TlsStream> + for Client> + where Req: Serialize + Sync + Send + 'static, + Resp: Deserialize + Sync + Send + 'static, + E: Deserialize + Sync + Send + 'static + { + type ConnectFut = ConnectFuture>; + type ConnectWithFut = ConnectWithFuture<'a, + Req, + Resp, + E, + TlsStream, + TlsConnectFut>; + + fn connect_remotely(addr: &SocketAddr, + remote: &reactor::Remote, + config: Config>) + -> Self::ConnectFut { + let addr = *addr; + let (tx, rx) = futures::oneshot(); + remote.spawn(move |handle| { + let handle2 = handle.clone(); + TcpStream::connect(&addr, handle) + .and_then(move |socket| { + let tls_client_cx = config.tls_client_cx + .expect("Need TlsClientContext for a TlsStream"); + tls_client_cx.tls_connector + .connect_async(&tls_client_cx.domain, socket) + .map_err(native2io) + }) + .map(move |tcp| Client::new(Proto::new().bind_client(&handle2, tcp))) + .then(move |result| { + tx.complete(result); + Ok(()) + }) + }); + ConnectFuture { inner: rx } + } + + fn connect_with(addr: &SocketAddr, + handle: &'a reactor::Handle, + config: Config>) + -> Self::ConnectWithFut { + let tls_client_cx = config.tls_client_cx + .expect("Need TlsClientContext for a TlsStream"); + ConnectWithFuture { + inner: TcpStream::connect(addr, handle) + .join(futures::finished(tls_client_cx)) + .and_then(TlsConnectFn) + .map(MultiplexConnect::new(handle)), + } + } + } + } + + /// A future that resolves to a `Client` or an `io::Error`. + pub struct ConnectFuture where Req: Serialize + 'static, Resp: Deserialize + 'static, E: Deserialize + 'static, + S: Io + 'static { - inner: futures::Oneshot>>, + inner: futures::Oneshot>>, } - impl Future for ConnectFuture + impl Future for ConnectFuture where Req: Serialize + 'static, Resp: Deserialize + 'static, E: Deserialize + 'static, + S: Io + 'static { - type Item = Client; + type Item = Client; type Error = io::Error; fn poll(&mut self) -> futures::Poll { @@ -154,18 +347,14 @@ pub mod future { } } - /// A future that resolves to a `Client` or an `io::Error`. - pub struct ConnectWithFuture<'a, Req, Resp, E> { - inner: futures::Map>, - } - - impl<'a, Req, Resp, E> Future for ConnectWithFuture<'a, Req, Resp, E> + impl<'a, Req, Resp, E, S, F> Future for ConnectWithFuture<'a, Req, Resp, E, S, F> where Req: Serialize + Sync + Send + 'static, Resp: Deserialize + Sync + Send + 'static, - E: Deserialize + Sync + Send + 'static + E: Deserialize + Sync + Send + 'static, + S: Io + 'static, + F: Future { - type Item = Client; + type Item = Client; type Error = io::Error; fn poll(&mut self) -> futures::Poll { @@ -181,27 +370,36 @@ pub mod future { } } - impl<'a, Req, Resp, E> FnOnce<(TcpStream,)> for MultiplexConnect<'a, Req, Resp, E> + impl<'a, Req, Resp, E, S> FnOnce<(S,)> for MultiplexConnect<'a, Req, Resp, E> where Req: Serialize + Sync + Send + 'static, Resp: Deserialize + Sync + Send + 'static, - E: Deserialize + Sync + Send + 'static + E: Deserialize + Sync + Send + 'static, + S: Io + 'static { - type Output = Client; + type Output = Client; - extern "rust-call" fn call_once(self, (tcp,): (TcpStream,)) -> Client { - Client::new(Proto::new().bind_client(self.0, tcp)) + extern "rust-call" fn call_once(self, (s,): (S,)) -> Self::Output { + Client::new(Proto::new().bind_client(self.0, s)) } } - impl<'a, Req, Resp, E> Connect<'a> for Client + impl<'a, Req, Resp, E> Connect<'a, TcpStream> for Client where Req: Serialize + Sync + Send + 'static, Resp: Deserialize + Sync + Send + 'static, E: Deserialize + Sync + Send + 'static { - type ConnectFut = ConnectFuture; - type ConnectWithFut = ConnectWithFuture<'a, Req, Resp, E>; + type ConnectFut = ConnectFuture; + type ConnectWithFut = ConnectWithFuture<'a, + Req, + Resp, + E, + TcpStream, + ::tokio_core::net::TcpStreamNew>; - fn connect_remotely(addr: &SocketAddr, remote: &reactor::Remote) -> Self::ConnectFut { + fn connect_remotely(addr: &SocketAddr, + remote: &reactor::Remote, + _config: Config) + -> Self::ConnectFut { let addr = *addr; let (tx, rx) = futures::oneshot(); remote.spawn(move |handle| { @@ -216,9 +414,12 @@ pub mod future { ConnectFuture { inner: rx } } - fn connect_with(addr: &SocketAddr, handle: &'a reactor::Handle) -> Self::ConnectWithFut { + fn connect_with(addr: &SocketAddr, + handle: &'a reactor::Handle, + _config: Config) + -> Self::ConnectWithFut { ConnectWithFuture { - inner: TcpStream::connect(addr, handle).map(MultiplexConnect::new(handle)) + inner: TcpStream::connect(addr, handle).map(MultiplexConnect::new(handle)), } } } @@ -230,20 +431,24 @@ pub mod sync { use serde::{Deserialize, Serialize}; use std::io; use std::net::ToSocketAddrs; - use super::Client; + use super::{Client, Config}; + use tokio_core::io::Io; + use tokio_core::net::TcpStream; /// Types that can connect to a server synchronously. - pub trait Connect: Sized { + pub trait Connect: Sized + where S: Io + { /// Connects to a server located at the given address. - fn connect(addr: A) -> Result where A: ToSocketAddrs; + fn connect(addr: A, config: Config) -> Result where A: ToSocketAddrs; } - impl Connect for Client + impl Connect for Client where Req: Serialize + Sync + Send + 'static, Resp: Deserialize + Sync + Send + 'static, E: Deserialize + Sync + Send + 'static { - fn connect(addr: A) -> Result + fn connect(addr: A, config: Config) -> Result where A: ToSocketAddrs { let addr = if let Some(a) = addr.to_socket_addrs()?.next() { @@ -253,7 +458,32 @@ pub mod sync { "`ToSocketAddrs::to_socket_addrs` returned an empty \ iterator.")); }; - ::connect(&addr).wait() + >::connect(&addr, config).wait() } } + + cfg_if! { + if #[cfg(feature = "tls")] { + use ::tokio_tls::TlsStream; + + impl Connect> for Client> + where Req: Serialize + Sync + Send + 'static, + Resp: Deserialize + Sync + Send + 'static, + E: Deserialize + Sync + Send + 'static + { + fn connect(addr: A, config: Config>) -> Result + where A: ToSocketAddrs + { + let addr = if let Some(a) = addr.to_socket_addrs()?.next() { + a + } else { + return Err(io::Error::new(io::ErrorKind::AddrNotAvailable, + "`ToSocketAddrs::to_socket_addrs` returned an \ + empty iterator.")); + }; + >>::connect(&addr, config).wait() + } + } + } else {} + } } diff --git a/src/errors.rs b/src/errors.rs index 78bc44c7..cba83252 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -107,3 +107,9 @@ pub enum WireError { pub trait SerializableError: StdError + Deserialize + Serialize + Send + 'static {} impl SerializableError for E {} + +#[cfg(feature = "tls")] +/// Convert `native_tls::Error` to `std::io::Error` +pub fn native2io(e: ::native_tls::Error) -> io::Error { + io::Error::new(io::ErrorKind::Other, e) +} diff --git a/src/lib.rs b/src/lib.rs index 2545776a..bed59233 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,7 +26,8 @@ //! //! Example usage: //! -//! ``` +#![cfg_attr(feature = "tls", doc = " ```ignore")] +#![cfg_attr(not(feature = "tls"), doc = " ```")] //! // required by `FutureClient` (not used in this example) //! #![feature(conservative_impl_trait, plugin)] //! #![plugin(tarpc_plugins)] @@ -58,6 +59,52 @@ //! } //! ``` //! +//! Example usage with TLS: +//! +#![cfg_attr(feature = "tls", doc = " ```ignore,rust,no_run")] +#![cfg_attr(not(feature = "tls"), doc = " ```ignore")] +//! // required by `FutureClient` (not used in this example) +//! #![feature(conservative_impl_trait, plugin)] +//! #![plugin(tarpc_plugins)] +//! +//! #[macro_use] +//! extern crate tarpc; +//! +//! use tarpc::sync::Connect; +//! use tarpc::util::Never; +//! use tarpc::TlsClientContext; +//! use tarpc::native_tls::{Pkcs12, TlsAcceptor, TlsConnector}; +//! +//! service! { +//! rpc hello(name: String) -> String; +//! } +//! +//! #[derive(Clone)] +//! struct HelloServer; +//! +//! impl SyncService for HelloServer { +//! fn hello(&mut self, name: String) -> Result { +//! Ok(format!("Hello, {}!", name)) +//! } +//! } +//! +//! fn tls_context() -> (TlsAcceptor, TlsClientContext) { +//! let buf = include_bytes!("test/identity.p12"); +//! let pkcs12 = Pkcs12::from_der(buf, "password").unwrap(); +//! let acceptor = TlsAcceptor::builder(pkcs12).unwrap().build().unwrap(); +//! let client_cx = TlsClientContext::try_new("foobar.com").unwrap(); +//! (acceptor, client_cx) +//! } +//! +//! fn main() { +//! let addr = "localhost:10000"; +//! let (acceptor, client_cx) = tls_context(); +//! let _server = HelloServer.listen(addr, acceptor); +//! let mut client = SyncClient::connect(addr, client_cx).unwrap(); +//! println!("{}", client.hello("Mom".to_string()).unwrap()); +//! } +//! ``` +//! #![deny(missing_docs)] #![feature(plugin, conservative_impl_trait, never_type, unboxed_closures, fn_traits, specialization)] #![plugin(tarpc_plugins)] @@ -72,6 +119,8 @@ extern crate net2; #[macro_use] extern crate serde_derive; extern crate take; +#[macro_use] +extern crate cfg_if; #[doc(hidden)] pub extern crate bincode; @@ -88,13 +137,17 @@ pub extern crate tokio_service; #[doc(hidden)] pub use client::Client; +pub use client::Config as ClientConfig; #[doc(hidden)] pub use client::future::{ConnectFuture, ConnectWithFuture}; pub use errors::{Error, SerializableError}; #[doc(hidden)] pub use errors::WireError; #[doc(hidden)] -pub use server::{ListenFuture, Response, listen, listen_with}; +pub use server::{ListenFuture, Response, Listen}; +pub use server::Config as ServerConfig; +/// TcpStream +pub type Tcp = tokio_core::net::TcpStream; /// Provides some utility error types, as well as a trait for spawning futures on the default event /// loop. @@ -113,6 +166,17 @@ mod protocol; /// Provides a few different error types. mod errors; +cfg_if! { + if #[cfg(feature = "tls")] { + extern crate tokio_tls; + pub extern crate native_tls; + /// TlsStream + pub type Tls = tokio_tls::TlsStream<::tokio_core::net::TcpStream>; + + pub use client::tls::TlsClientContext; + } else {} +} + /// Utility specific to synchronous implementation. pub mod sync { pub use client::sync::*; diff --git a/src/macros.rs b/src/macros.rs index 97fa505e..3e9086a6 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -393,24 +393,30 @@ macro_rules! service { /// Provides a function for starting the service. This is a separate trait from /// `FutureService` to prevent collisions with the names of RPCs. pub trait FutureServiceExt: FutureService { - fn listen(self, addr: ::std::net::SocketAddr) -> $crate::ListenFuture + fn listen(self, addr: ::std::net::SocketAddr, config: $crate::ServerConfig) + -> $crate::ListenFuture + where S: $crate::tokio_core::io::Io + 'static, + $crate::ServerConfig: $crate::Listen { let (tx, rx) = $crate::futures::oneshot(); $crate::future::REMOTE.spawn(move |handle| Ok(tx.complete(Self::listen_with(self, addr, - handle.clone())))); + handle.clone(), config)))); $crate::ListenFuture::from_oneshot(rx) } /// Spawns the service, binding to the given address and running on /// the default tokio `Loop`. - fn listen_with(self, + fn listen_with(self, addr: ::std::net::SocketAddr, - handle: $crate::tokio_core::reactor::Handle) + handle: $crate::tokio_core::reactor::Handle, + config: $crate::ServerConfig) -> ::std::io::Result<::std::net::SocketAddr> + where S: $crate::tokio_core::io::Io + 'static, + $crate::ServerConfig: $crate::Listen { - return $crate::listen_with(addr, + return $crate::Listen::listen_with(config, addr, move || Ok(__tarpc_service_AsyncServer(self.clone())), handle); @@ -476,7 +482,7 @@ macro_rules! service { type Error = ::std::io::Error; type Future = __tarpc_service_FutureReply<__tarpc_service_S>; - fn call(&mut self, __tarpc_service_request: Self::Request) -> Self::Future { + fn call(&self, __tarpc_service_request: Self::Request) -> Self::Future { let __tarpc_service_request = match __tarpc_service_request { Ok(__tarpc_service_request) => __tarpc_service_request, Err(__tarpc_service_deserialize_err) => { @@ -536,21 +542,25 @@ macro_rules! service { /// Provides a function for starting the service. This is a separate trait from /// `SyncService` to prevent collisions with the names of RPCs. pub trait SyncServiceExt: SyncService { - fn listen(self, addr: L) + fn listen(self, addr: L, server_config: $crate::ServerConfig) -> ::std::io::Result<::std::net::SocketAddr> - where L: ::std::net::ToSocketAddrs + where L: ::std::net::ToSocketAddrs, + S: $crate::tokio_core::io::Io, + $crate::ServerConfig: $crate::Listen { let addr = $crate::util::FirstSocketAddr::try_first_socket_addr(&addr)?; let (tx, rx) = $crate::futures::oneshot(); - $crate::future::REMOTE.spawn(move |handle| Ok(tx.complete(Self::listen_with(self, addr, handle.clone())))); + $crate::future::REMOTE.spawn(move |handle| Ok(tx.complete(Self::listen_with(self, addr, handle.clone(), server_config)))); $crate::futures::Future::wait($crate::ListenFuture::from_oneshot(rx)) } /// Spawns the service, binding to the given address and running on /// the default tokio `Loop`. - fn listen_with(self, addr: L, handle: $crate::tokio_core::reactor::Handle) + fn listen_with(self, addr: L, handle: $crate::tokio_core::reactor::Handle, server_config: $crate::ServerConfig) -> ::std::io::Result<::std::net::SocketAddr> - where L: ::std::net::ToSocketAddrs + where L: ::std::net::ToSocketAddrs, + S: $crate::tokio_core::io::Io, + $crate::ServerConfig: $crate::Listen { let __tarpc_service_service = __SyncServer { service: self, @@ -558,7 +568,7 @@ macro_rules! service { return FutureServiceExt::listen_with( __tarpc_service_service, $crate::util::FirstSocketAddr::try_first_socket_addr(&addr)?, - handle); + handle, server_config); #[derive(Clone)] struct __SyncServer { @@ -611,21 +621,28 @@ macro_rules! service { #[allow(unused)] #[derive(Clone, Debug)] /// The client stub that makes RPC calls to the server. Exposes a blocking interface. - pub struct SyncClient(::std::sync::Arc<::std::sync::Mutex>); - - impl $crate::sync::Connect for SyncClient { - fn connect(addr: A) -> ::std::result::Result + pub struct SyncClient(::std::sync::Arc<::std::sync::Mutex>>) + where S: $crate::tokio_core::io::Io + 'static; + // $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error, S>: $crate::future::Connect<'a, S>; + + impl $crate::sync::Connect for SyncClient + where S: $crate::tokio_core::io::Io + 'static, + $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error, S>: $crate::future::Connect<'static, S> { + fn connect(addr: A, config: $crate::ClientConfig) -> ::std::result::Result where A: ::std::net::ToSocketAddrs, { let addr = $crate::util::FirstSocketAddr::try_first_socket_addr(&addr)?; - let client = ::connect(&addr); + let client = as $crate::future::Connect<'static, S>>::connect(&addr, config); let client = $crate::futures::Future::wait(client)?; let client = SyncClient(::std::sync::Arc::new(::std::sync::Mutex::new(client))); ::std::result::Result::Ok(client) } } - impl SyncClient { + impl SyncClient + where S: $crate::tokio_core::io::Io + 'static, + $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error, S>: $crate::sync::Connect + { $( #[allow(unused)] $(#[$attr])* @@ -639,21 +656,27 @@ macro_rules! service { } #[allow(non_camel_case_types)] - type __tarpc_service_Client = + type __tarpc_service_Client = $crate::Client<__tarpc_service_Request, __tarpc_service_Response, - __tarpc_service_Error>; + __tarpc_service_Error, + S>; #[allow(non_camel_case_types)] /// Implementation detail: Pending connection. - pub struct __tarpc_service_ConnectFuture { + pub struct __tarpc_service_ConnectFuture + where S: $crate::tokio_core::io::Io + 'static { + // $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error, S>: $crate::future::Connect<'static, S> { inner: $crate::futures::Map<$crate::ConnectFuture<__tarpc_service_Request, __tarpc_service_Response, - __tarpc_service_Error>, - fn(__tarpc_service_Client) -> T>, + __tarpc_service_Error, + S>, + fn(__tarpc_service_Client) -> T>, } - impl $crate::futures::Future for __tarpc_service_ConnectFuture { + impl $crate::futures::Future for __tarpc_service_ConnectFuture + where S: $crate::tokio_core::io::Io + 'static { + // $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error, S>: $crate::future::Connect<'static, S> { type Item = T; type Error = ::std::io::Error; @@ -664,15 +687,27 @@ macro_rules! service { #[allow(non_camel_case_types)] /// Implementation detail: Pending connection. - pub struct __tarpc_service_ConnectWithFuture<'a, T> { + pub struct __tarpc_service_ConnectWithFuture<'a, T, S> + where S: $crate::tokio_core::io::Io + 'static + Sized, + $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error, S>: $crate::future::Connect<'a, S>, + // F: $crate::futures::Future + { inner: $crate::futures::Map<$crate::ConnectWithFuture<'a, __tarpc_service_Request, __tarpc_service_Response, - __tarpc_service_Error>, - fn(__tarpc_service_Client) -> T>, + __tarpc_service_Error, + S, + // FIXME: shouldn't need box here + // I don' think... + ::std::boxed::Box<$crate::futures::Future>>, + fn(__tarpc_service_Client) -> T>, } - impl<'a, T> $crate::futures::Future for __tarpc_service_ConnectWithFuture<'a, T> { + impl<'a, T, S> $crate::futures::Future for __tarpc_service_ConnectWithFuture<'a, T, S> + where S: $crate::tokio_core::io::Io + 'static, + $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error, S>: $crate::future::Connect<'a, S>, + // F: $crate::futures::Future + { type Item = T; type Error = ::std::io::Error; @@ -684,18 +719,24 @@ macro_rules! service { #[allow(unused)] #[derive(Clone, Debug)] /// The client stub that makes RPC calls to the server. Exposes a Future interface. - pub struct FutureClient(__tarpc_service_Client); + pub struct FutureClient(__tarpc_service_Client) + where S: $crate::tokio_core::io::Io + 'static; - impl<'a> $crate::future::Connect<'a> for FutureClient { - type ConnectFut = __tarpc_service_ConnectFuture; - type ConnectWithFut = __tarpc_service_ConnectWithFuture<'a, Self>; + impl<'a, S> $crate::future::Connect<'a, S> for FutureClient + where S: $crate::tokio_core::io::Io + 'a, + $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error, S>: $crate::future::Connect<'a, S>, + // F: $crate::futures::Future + { + type ConnectFut = __tarpc_service_ConnectFuture; + type ConnectWithFut = __tarpc_service_ConnectWithFuture<'a, Self, S>; fn connect_remotely(__tarpc_service_addr: &::std::net::SocketAddr, - __tarpc_service_remote: &$crate::tokio_core::reactor::Remote) + __tarpc_service_remote: &$crate::tokio_core::reactor::Remote, + __tarpc_client_config: $crate::ClientConfig) -> Self::ConnectFut { - let client = <__tarpc_service_Client as $crate::future::Connect>::connect_remotely( - __tarpc_service_addr, __tarpc_service_remote); + let client = <__tarpc_service_Client as $crate::future::Connect<'a, S>>::connect_remotely( + __tarpc_service_addr, __tarpc_service_remote, __tarpc_client_config); __tarpc_service_ConnectFuture { inner: $crate::futures::Future::map(client, FutureClient) @@ -703,11 +744,12 @@ macro_rules! service { } fn connect_with(__tarpc_service_addr: &::std::net::SocketAddr, - __tarpc_service_handle: &'a $crate::tokio_core::reactor::Handle) + __tarpc_service_handle: &'a $crate::tokio_core::reactor::Handle, + __tarpc_client_config: $crate::ClientConfig) -> Self::ConnectWithFut { - let client = <__tarpc_service_Client as $crate::future::Connect>::connect_with( - __tarpc_service_addr, __tarpc_service_handle); + let client = <__tarpc_service_Client as $crate::future::Connect<'a, S>>::connect_with( + __tarpc_service_addr, __tarpc_service_handle, __tarpc_client_config); __tarpc_service_ConnectWithFuture { inner: $crate::futures::Future::map(client, FutureClient) @@ -715,7 +757,10 @@ macro_rules! service { } } - impl FutureClient { + impl FutureClient + where S: $crate::tokio_core::io::Io + 'static, + $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error, S>: $crate::tokio_service::Service + { $( #[allow(unused)] $(#[$attr])* @@ -725,7 +770,7 @@ macro_rules! service { { let __tarpc_service_req = __tarpc_service_Request::$fn_name(($($arg,)*)); let __tarpc_service_fut = - $crate::tokio_service::Service::call(&mut self.0, __tarpc_service_req); + $crate::tokio_service::Service::call(&self.0, __tarpc_service_req); $crate::futures::Future::then(__tarpc_service_fut, move |__tarpc_service_msg| { match __tarpc_service_msg? { @@ -773,49 +818,182 @@ macro_rules! service { } } + } + // allow dead code; we're just testing that the macro expansion compiles #[allow(dead_code)] #[cfg(test)] mod syntax_test { + use futures::*; use util::Never; service! { - #[deny(warnings)] - #[allow(non_snake_case)] - rpc TestCamelCaseDoesntConflict(); - rpc hello() -> String; - #[doc="attr"] + // #[deny(warnings)] + // #[allow(non_snake_case)] + // rpc TestCamelCaseDoesntConflict(); + // rpc hello() -> String; + // #[doc="attr"] rpc attr(s: String) -> String; - rpc no_args_no_return(); - rpc no_args() -> (); - rpc one_arg(foo: String) -> i32; - rpc two_args_no_return(bar: String, baz: u64); - rpc two_args(bar: String, baz: u64) -> String; - rpc no_args_ret_error() -> i32 | Never; - rpc one_arg_ret_error(foo: String) -> String | Never; - rpc no_arg_implicit_return_error() | Never; - #[doc="attr"] - rpc one_arg_implicit_return_error(foo: String) | Never; + // rpc no_args_no_return(); + // rpc no_args() -> (); + // rpc one_arg(foo: String) -> i32; + // rpc two_args_no_return(bar: String, baz: u64); + // rpc two_args(bar: String, baz: u64) -> String; + // rpc no_args_ret_error() -> i32 | Never; + // rpc one_arg_ret_error(foo: String) -> String | Never; + // rpc no_arg_implicit_return_error() | Never; + // #[doc="attr"] + // rpc one_arg_implicit_return_error(foo: String) | Never; } } -#[cfg(test)] +#[cfg(all(test, feature = "cow"))] mod functional_test { + extern crate env_logger; + + use {Tcp, ClientConfig, ServerConfig}; + + #[cfg(feature = "tls")] + use Tls; use futures::{Future, failed}; use util::FirstSocketAddr; - extern crate env_logger; + + macro_rules! t { + ($e:expr) => (match $e { + Ok(e) => e, + Err(e) => panic!("{} failed with {:?}", stringify!($e), e), + }) + } service! { rpc add(x: i32, y: i32) -> i32; rpc hey(name: String) -> String; } + cfg_if! { + if #[cfg(feature = "tls")] { + const DOMAIN: &'static str = "foobar.com"; + + use ::TlsClientContext; + use ::native_tls::{Pkcs12, TlsAcceptor, TlsConnector}; + + fn tls_context() -> (ServerConfig, ClientConfig) { + let buf = include_bytes!("../test/identity.p12"); + let pkcs12 = t!(Pkcs12::from_der(buf, "mypass")); + let acceptor = t!(t!(TlsAcceptor::builder(pkcs12)).build()); + let server_config = ServerConfig::new_tls(acceptor); + let client_config = get_tls_client_config(); + + (server_config, client_config) + } + + // Making the TlsConnector for testing needs to be OS-dependent just like native-tls. + // We need to go through this trickery because the test self-signed cert is not part + // of the system's cert chain. If it was, then all that is required is + // `TlsConnector::builder().unwrap().build().unwrap()`. + cfg_if! { + if #[cfg(target_os = "macos")] { + extern crate security_framework; + + use self::security_framework::certificate::SecCertificate; + use native_tls::backend::security_framework::TlsConnectorBuilderExt; + + fn get_tls_client_config() -> ClientConfig { + let buf = include_bytes!("../test/root-ca.der"); + let cert = t!(SecCertificate::from_der(buf)); + let mut connector = t!(TlsConnector::builder()); + connector.anchor_certificates(&[cert]); + + ClientConfig::new_tls(TlsClientContext { + domain: DOMAIN.into(), + tls_connector: t!(connector.build()), + }) + } + } else if #[cfg(all(not(target_os = "macos"), not(windows)))] { + use native_tls::backend::openssl::TlsConnectorBuilderExt; + + fn get_tls_client_config() -> ClientConfig { + let mut connector = t!(TlsConnector::builder()); + t!(connector.builder_mut() + .builder_mut() + .set_ca_file("../test/root-ca.pem")); + + ClientConfig::new_tls(TlsClientContext { + domain: DOMAIN.into(), + tls_connector: t!(connector.build()), + }) + } + // not implemented for windows or other platforms + } else { + fn get_tls_client_context() -> TlsClientContext { + unimplemented!() + } + } + } + + fn get_sync_client>(addr: &::std::net::SocketAddr) -> ::std::io::Result { + let client_config = get_tls_client_config(); + C::connect(addr, client_config) + } + + fn start_server_with_sync_client, S: SyncServiceExt>(server: S) -> (::std::net::SocketAddr, ::std::io::Result) { + let (server_config, client_config) = tls_context(); + let addr = t!(server.listen("localhost:0".first_socket_addr(), server_config)); + let client = C::connect(addr, client_config); + (addr, client) + } + + fn start_server_with_async_client<'a, C: ::future::Connect<'a, Tls>, S: FutureServiceExt>(server: S) -> (::std::net::SocketAddr, C) { + let (server_config, client_config) = tls_context(); + let addr = t!(server.listen("localhost:0".first_socket_addr(), server_config).wait()); + let client = t!(C::connect(&addr, client_config).wait()); + (addr, client) + } + + fn start_err_server_with_async_client<'a, C: ::future::Connect<'a, Tls>, S: error_service::FutureServiceExt>(server: S) -> (::std::net::SocketAddr, C) { + let (server_config, client_config) = tls_context(); + let addr = t!(server.listen("localhost:0".first_socket_addr(), server_config).wait()); + let client = t!(C::connect(&addr, client_config).wait()); + (addr, client) + } + } else { + + fn get_server_config() -> ServerConfig { + ServerConfig::new_tcp() + } + + fn get_client_config() -> ClientConfig { + ClientConfig::new_tcp() + } + + fn get_sync_client>(addr: &::std::net::SocketAddr) -> ::std::io::Result { + C::connect(addr, get_client_config()) + } + + /// start server and return `SyncClient` + fn start_server_with_sync_client, S: SyncServiceExt>(server: S) -> (::std::net::SocketAddr, ::std::io::Result) { + let addr = t!(server.listen("localhost:0".first_socket_addr(), get_server_config())); + let client = C::connect(addr, get_client_config()); + (addr, client) + } + + fn start_server_with_async_client<'a, C: ::future::Connect<'a, Tcp>, S: FutureServiceExt>(server: S) -> (::std::net::SocketAddr, C) { + let addr = t!(server.listen("localhost:0".first_socket_addr(), get_server_config()).wait()); + let client = t!(C::connect(&addr, get_client_config()).wait()); + (addr, client) + } + + fn start_err_server_with_async_client<'a, C: ::future::Connect<'a, Tcp>, S: error_service::FutureServiceExt>(server: S) -> (::std::net::SocketAddr, C) { + let addr = t!(server.listen("localhost:0".first_socket_addr(), get_server_config()).wait()); + let client = t!(C::connect(&addr, get_client_config()).wait()); + (addr, client) + } + } + } + mod sync { - use super::{SyncClient, SyncService, SyncServiceExt}; - use super::env_logger; - use sync::Connect; - use util::FirstSocketAddr; + use super::{SyncClient, SyncService, env_logger, start_server_with_sync_client}; use util::Never; #[derive(Clone, Copy)] @@ -833,8 +1011,8 @@ mod functional_test { #[test] fn simple() { let _ = env_logger::init(); - let addr = Server.listen("localhost:0".first_socket_addr()).unwrap(); - let client = SyncClient::connect(addr).unwrap(); + let (_, client) = start_server_with_sync_client::(Server); + let client = t!(client); assert_eq!(3, client.add(1, 2).unwrap()); assert_eq!("Hey, Tim.", client.hey("Tim".to_string()).unwrap()); } @@ -842,8 +1020,9 @@ mod functional_test { #[test] fn other_service() { let _ = env_logger::init(); - let addr = Server.listen("localhost:0".first_socket_addr()).unwrap(); - let client = super::other_service::SyncClient::connect(addr).expect("Could not connect!"); + let (_, client) = start_server_with_sync_client::(Server); + let client = client.expect("Could not connect!"); match client.foo().err().unwrap() { ::Error::ServerDeserialize(_) => {} // good bad => panic!("Expected Error::ServerDeserialize but got {}", bad), @@ -852,11 +1031,8 @@ mod functional_test { } mod future { - use future::Connect; use futures::{Finished, Future, finished}; - use super::{FutureClient, FutureService, FutureServiceExt}; - use super::env_logger; - use util::FirstSocketAddr; + use super::{FutureClient, FutureService, env_logger, start_server_with_async_client}; use util::Never; #[derive(Clone)] @@ -879,8 +1055,7 @@ mod functional_test { #[test] fn simple() { let _ = env_logger::init(); - let addr = Server.listen("localhost:0".first_socket_addr()).wait().unwrap(); - let mut client = FutureClient::connect(&addr).wait().unwrap(); + let (_, mut client) = start_server_with_async_client::(Server); assert_eq!(3, client.add(1, 2).wait().unwrap()); assert_eq!("Hey, Tim.", client.hey("Tim".to_string()).wait().unwrap()); } @@ -888,8 +1063,7 @@ mod functional_test { #[test] fn concurrent() { let _ = env_logger::init(); - let addr = Server.listen("localhost:0".first_socket_addr()).wait().unwrap(); - let mut client = FutureClient::connect(&addr).wait().unwrap(); + let (_, mut client) = start_server_with_async_client::(Server); let req1 = client.add(1, 2); let req2 = client.add(3, 4); let req3 = client.hey("Tim".to_string()); @@ -901,9 +1075,9 @@ mod functional_test { #[test] fn other_service() { let _ = env_logger::init(); - let addr = Server.listen("localhost:0".first_socket_addr()).wait().unwrap(); - let mut client = - super::other_service::FutureClient::connect(&addr).wait().unwrap(); + let (_, mut client) = + start_server_with_async_client::(Server); match client.foo().wait().err().unwrap() { ::Error::ServerDeserialize(_) => {} // good bad => panic!(r#"Expected Error::ServerDeserialize but got "{}""#, bad), @@ -931,14 +1105,12 @@ mod functional_test { #[test] fn error() { - use future::Connect as Fc; - use sync::Connect as Sc; use std::error::Error as E; use self::error_service::*; let _ = env_logger::init(); - let addr = ErrorServer.listen("localhost:0".first_socket_addr()).wait().unwrap(); - let mut client = FutureClient::connect(&addr).wait().unwrap(); + let (addr, mut client) = start_err_server_with_async_client::(ErrorServer); client.bar() .then(move |result| { match result.err().unwrap() { @@ -952,7 +1124,7 @@ mod functional_test { .wait() .unwrap(); - let mut client = SyncClient::connect(&addr).unwrap(); + let mut client = get_sync_client::(&addr).unwrap(); match client.bar().err().unwrap() { ::Error::App(e) => { assert_eq!(e.description(), "lol jk"); diff --git a/src/server.rs b/src/server.rs index 54bd9513..94cf39a0 100644 --- a/src/server.rs +++ b/src/server.rs @@ -3,76 +3,178 @@ // Licensed under the MIT License, . // This file may not be copied, modified, or distributed except according to those terms. -use net2; +use {net2, Tcp}; use bincode::serde::DeserializeError; use errors::WireError; use future::REMOTE; -use protocol::Proto; use futures::{self, Async, Future, Stream}; +use protocol::Proto; use serde::{Deserialize, Serialize}; use std::io; +use std::marker::PhantomData; use std::net::SocketAddr; +use tokio_core::io::Io; use tokio_core::net::TcpListener; use tokio_core::reactor::Handle; use tokio_proto::BindServer; use tokio_service::NewService; +cfg_if! { + if #[cfg(feature = "tls")] { + use Tls; + use native_tls::TlsAcceptor; + use tokio_tls::TlsAcceptorExt; + use errors::native2io; + } else {} +} + /// A message from server to client. pub type Response = Result>; -/// Spawns a service that binds to the given address and runs on the default reactor core. -pub fn listen(addr: SocketAddr, new_service: S) -> ListenFuture - where S: NewService, - Response = Response, - Error = io::Error> + Send + 'static, - Req: Deserialize + 'static, - Resp: Serialize + 'static, - E: Serialize + 'static -{ - let (tx, rx) = futures::oneshot(); - REMOTE.spawn(move |handle| Ok(tx.complete(listen_with(addr, new_service, handle.clone())))); - ListenFuture { inner: rx } +/// Enables service spawning +pub trait Listen: Sized + Send + 'static { + /// Spawns a service that binds to the given address and runs on the default reactor core. + fn listen(self, addr: SocketAddr, new_service: S) -> ListenFuture + where S: NewService, + Response = Response, + Error = io::Error> + Send + 'static, + Req: Deserialize + 'static, + Resp: Serialize + 'static, + E: Serialize + 'static + { + let (tx, rx) = futures::oneshot(); + REMOTE.spawn(move |handle| { + Ok(tx.complete(self.listen_with(addr, new_service, handle.clone()))) + }); + ListenFuture { inner: rx } + } + + /// Spawns a service that binds to the given address using the given handle. + fn listen_with(self, + addr: SocketAddr, + new_service: S, + handle: Handle) + -> io::Result + where S: NewService, + Response = Response, + Error = io::Error> + Send + 'static, + Req: Deserialize + 'static, + Resp: Serialize + 'static, + E: Serialize + 'static; } -/// Spawns a service that binds to the given address using the given handle. -pub fn listen_with(addr: SocketAddr, +impl Listen for Config { + fn listen_with(self, + addr: SocketAddr, new_service: S, handle: Handle) -> io::Result - where S: NewService, - Response = Response, - Error = io::Error> + Send + 'static, - Req: Deserialize + 'static, - Resp: Serialize + 'static, - E: Serialize + 'static + where S: NewService, + Response = Response, + Error = io::Error> + Send + 'static, + Req: Deserialize + 'static, + Resp: Serialize + 'static, + E: Serialize + 'static + { + let listener = listener(&addr, &handle)?; + let addr = listener.local_addr()?; + + let handle2 = handle.clone(); + let server = listener.incoming() + .for_each(move |(socket, _)| { + Proto::new().bind_server(&handle2, socket, new_service.new_service()?); + + Ok(()) + }) + .map_err(|e| error!("While processing incoming connections: {}", e)); + handle.spawn(server); + Ok(addr) + } +} + +#[cfg(feature = "tls")] +impl Listen for Config { + fn listen_with(self, + addr: SocketAddr, + new_service: S, + handle: Handle) + -> io::Result + where S: NewService, + Response = Response, + Error = io::Error> + Send + 'static, + Req: Deserialize + 'static, + Resp: Serialize + 'static, + E: Serialize + 'static + { + let listener = listener(&addr, &handle)?; + let addr = listener.local_addr()?; + + let handle2 = handle.clone(); + let tls_acceptor = self.tls_acceptor.expect("TlsAcceptor required for Tls server"); + let server = listener.incoming() + .and_then(move |(socket, _)| tls_acceptor.accept_async(socket).map_err(native2io)) + .for_each(move |socket| { + Proto::new().bind_server(&handle2, socket, new_service.new_service()?); + Ok(()) + }) + .map_err(|e| error!("While processing incoming connections: {}", e)); + + + handle.spawn(server); + Ok(addr) + } +} + +/// TODO: +pub struct Config + where S: Io { - let listener = listener(&addr, &handle)?; - let addr = listener.local_addr()?; + #[cfg(feature = "tls")] + tls_acceptor: Option, + _client_stream: PhantomData, +} - let handle2 = handle.clone(); - let server = listener.incoming().for_each(move |(socket, _)| { - Proto::new().bind_server(&handle2, socket, new_service.new_service()?); +#[cfg(feature = "tls")] +impl Config + where S: Io +{ + /// TODO + pub fn new_tcp() -> Config { + Config { + _client_stream: PhantomData, + tls_acceptor: None, + } + } + + /// TODO + pub fn new_tls(tls_acceptor: TlsAcceptor) -> Config { + Config { + _client_stream: PhantomData, + tls_acceptor: Some(tls_acceptor), + } + } +} - Ok(()) - }).map_err(|e| error!("While processing incoming connections: {}", e)); - handle.spawn(server); - Ok(addr) +#[cfg(not(feature = "tls"))] +impl Config { + /// TODO + pub fn new_tcp() -> Config { + Config { _client_stream: PhantomData } + } } -fn listener(addr: &SocketAddr, - handle: &Handle) -> io::Result { +fn listener(addr: &SocketAddr, handle: &Handle) -> io::Result { const PENDING_CONNECTION_BACKLOG: i32 = 1024; match *addr { - SocketAddr::V4(_) => net2::TcpBuilder::new_v4(), - SocketAddr::V6(_) => net2::TcpBuilder::new_v6() - }? - .reuse_address(true)? - .bind(addr)? - .listen(PENDING_CONNECTION_BACKLOG) - .and_then(|l| { - TcpListener::from_listener(l, addr, handle) - }) + SocketAddr::V4(_) => net2::TcpBuilder::new_v4(), + SocketAddr::V6(_) => net2::TcpBuilder::new_v6(), + } + ? + .reuse_address(true)? + .bind(addr)? + .listen(PENDING_CONNECTION_BACKLOG) + .and_then(|l| TcpListener::from_listener(l, addr, handle)) } /// A future that resolves to a `ServerHandle`. diff --git a/test/identity.p12 b/test/identity.p12 new file mode 100644 index 0000000000000000000000000000000000000000..d16abb8c70627bd959667495e578fb6f010a7f1d GIT binary patch literal 3386 zcmY+Fc{CIb*T-kZ%-F^$D#rP(jjrM6DXq;ULSwJ0;C7#;$Z7w z9PHO$c@c@DfAsGWG#5wj^;dEO0RexD!@nm$qyUuZe?P#0P$Vmufe?7XI$4!A4gxU% zusC`o{$ASWtHZ!%6&Yr};{6u`2KLv+>Oc-hrRqJ_6t51LvyFjb*MY2v@UpI}BEGHo zvK<0`n{R9EEh2758@vOSP&dDqOy0%>n)6OY2s1K@{ZZ4dUib$1=yGu{qy4)68IXNX zkk#_0j(a5M*dt*Sb*_j1P%B2^j47C!ky#GS(OVUXx=1c`4$i|hrFN!o2AI7yk$-hcE5vQ_4v$r`H0%4V>8 z@eO82W_dR!EPHOKwd{tqnGp6|1N4%pQ^bZ&wmM^1xKK`mmp5~Z!-*jOxzzTtyjI@N znR!#3*mi1n<$H_gljnHMOm{8 zY+OW$1dI#%nC00mv=w|&H?Ds_&Q&>zV$~?as>se&LcGBkC+KuH))CEF+bqRg55Y-3 z?~vHFBT!PxQJ=a_tK7s`O43jI#enGN$Sir<+d_5iA)Nn{pocY~r%FKRo?P!z?Yz^^ltS`1rG&W|$}6 zIK1R;ZV;$x`_4!Z3O$S$y|fdG=LX_+wEsUm|gb|J|R6=$2&5w5;*9HoUf+ zLC&xB@i{S7wciFKq`HokLs&MYUDh;a#&Nq`>{RQv)JdX8iEVvb{5|aGL9i^^AOpXY z&L6LF6u+99$t7+kFys94TO$8LR-yenXgMarH6$3GlTQl|GYr#AJ*J8<*w1~fu~s1O zdhUXpvVUHaLYgm_R_B=VPu;^q1k37|S#Az*#kI@%`yAGFcpg*|9J$55Dn2i3qil7T z#{o*c=&(?Um6=WCnJ<;C^yU0Wn5+U;LCTeF5LRZG`&7h5u>K>DCz^!_kAQ+Uk_>p^ZJ-AG#sMX^}swaF9O348d8KL2>E z)<=Onr|{6URV7{c-I=%8nti`|U(Z3YP74t0c_K)3>xS6vW%e^B29#WNrqLHAUIEy{ zzB?q&G}1_h*%0C4?15@moi|7#SL*3DFn*TcNo)PGTXh0X35{``dR4uHIDDveSU-f9 z{K;uqKl-Nv+V4mA(kBrDEVii_A1b!KJS}U4Iyn5L=6`(p(ctU_de;X1DiO>4u2__k zVlWt+97!S9u=F;5c}*O*ufnS{X8;m3K*h#T0PXmn;cX%gWvELfIb5GGobB|9yhDDpE&NP`u@Gnp&0 zQZLqLDBx6TpRdU#?(psb%LU%s+uHH3^q4!9CoL-jgzIVo56|~LFj*Dya8o&rW&R9f z#MCF&8DNgw4~2m6o|%0c4_>q*LnYa8v7A*R-OG2mtCse5WW>N06;mbHTJR0ly~=vb zjkL_*lL_>a|0oF63*HEu&>67XeZie|`a^`89-;zuIMvx0wZDKHFrH{;{>A+ay$ZLY zQ&)*|(POvl>U&=z{Tg-ooGiyts!|CuF0TS^0VgtL{JJ0I`v@^G`!EHbzFi}2 zL*x~uu?J$3fnQa7=3r+w1G;jan4eqgmvr-O)nlM%Da;H8qk?wE67l%lcqMyzw_r@o02)63je6Xu1-n29!*PLHn-90_SxMGSU5yp=aUNgAPf&JU@M3Mr*>n3*Qfs4@UZdr)3<49u!j(8;{>P^eE^Osav(h>l?db zz-D`eK9#oSSADv+c=1$Wl1_5pV%p++fJluaNC@?&3cRf~mUihw;wH~1NpD-OAhEp@ zp%J>UkR&rBuE;-O(>>(RI-$%WT}_LVD?y;mD%UYD&1i4MAzBQ}1OKO=ov1 zqOb6W(VoB8*XL$(xwOFE$}X1Q4kfSMF15mhjE{%TXEK8mEZLjGl#c@Kdami3&$?8R z3X0QOt>y=BbRV!lHr4bxPL(_bvynK6$A17e7YDHh;~?gLW%S=PgfjmhZkd3fTpV}< zi36|x7lrBmq3~Tpke%T}RQEp=2IIiJs)bH-t;&|}Fh+NaTeU~I+z6U$_st%d4& z-GhEwwpii2MIE&MtZ#2+NiaN#V8qoDMFa~9WuL=dYyA0VF!^rHt`>`@lvQaI9rjF% zf>&A5%p>?lyx=A)lifEe-ZTF_&hx;HqdgC=j^D`xrXHl2F1=@bAN8)917)sJ6!*me z<7HAMfY#b&Kyj?9V{7jX0ex*Do!y1*jQ4BlZqzj@WpzG)?+b1vXK$aw@ws>CE|ZxA z5+_}aqAICBLaX0@Sx&A-B{8F6=5ieD+o4bn7mz)?DFYwS9ISfcSkc1D?^7F(MYzNFqebD~<*>|~&C#$*Y)t41%< zSUb^f@Dm)@x`dT(>NzTU>>Qbs5c=rJ4Yz`@*9~DI<0a2AHA?iN*a+E!i8J>{0~PG?gk>1>Zj*jW^JtpfIAm$MRWkXoi1Cv@Ar>b zH0;ah2{C4g3|27>74O!U!X+684ZU@I&-k@Ud2^qCUNAL{TZwyyN$G#HNDrFsP#65c zrxf~mk;jg7wV|ONPtWwMdR6?%)Ot`$mI?|@QaMOVx=?!cXeO8~Fz9+qIZ5WmPKHb7 zgofgUUSjO+ua`8j{?2gr-`{@-43inOgBoKao68B6iAOcPQPh6Q%T2XayOCFbynb>X zH$308=bjoRmehcbbLNXtZBq z0J~mAB-w(EFUlR4pRu`v?GEVIWM&zLpe{CcX2(DI&Sy^R84J751Y9rEzC(o6CO0>y z=E{ugtGsQie~J(y5De$j6$PK5iB=^dz9SE8W5!8={%vccv}UmILPt41Z;;(tc^)R{ zZ=k`tzDp0hSkCT9WhKp4h)^M!%VaxWo6p-gp4Xnu=HqXHv&7k?hh-$IK1 zzdJH=%5w>F0z3c@{)P*{8Q=$y1-Sn0@Bil#LY@oEY0E8>a&AS}wv{Gv=((Kjlgzys zg_K7MBN?F(X;wNA9|QpA+;x-8N-+3rFwTsLlk6hIQoAssV1}Dt>u@5F2fhDT>Hh%N CKSRy{ literal 0 HcmV?d00001 diff --git a/test/root-ca.der b/test/root-ca.der new file mode 100644 index 0000000000000000000000000000000000000000..a9335c6fc2c81c3802c3d9ce20b0a83b124328a6 GIT binary patch literal 865 zcmXqLVvaRvVsc%;%*4pV#L4hTpHDUV`thj-ylk9WZ60mkc^MhGSs4sm4Y>_C*_cCF z*o2uJLk)!u1VJ1Q9qfVU#$p5r}I94=}PaFgG#sGZ-{6axpbA zGBT_Uye3vxrn|Fr`uw<~w`b0~8kDYZW4Xau{wLdX-T7XAaf}kJzq01V@waVhv!iz$ zTvpTjh^g!gN63#Ix=+|#1g2K0OR!uDS8MeNsq0(+wAiKp_l4NE3gMlPL@QdKCWpMA z&ZOU8oO}4r^SmZY2wonMv_e*dnN*PZxl z${CvXmX@9>anO4GZ0^CPI~(37ewVpC)WBZoOK(E!7oks&l};#1b{l$O8;Hdek0>8u>x zOGKuduVOfQ;lrys{_U*0xNYWU@XuNMqU*)>4?-sv)w*A(zW&eu$w9B_9!Zz=(-XgK zvviF=n%(P|Z@2sT)3bZH9-X-Oi?Oj^TvAi;X0ycE9J8K}T6Zn#HO@I@-TclFxjiRo zn$g96_wIk+-4zAyDxY1TrrMGn>QTG**|xWvo95U0ZhSXAmid`SU)!m2BMas$n?4_! z5__We(bsJW8C$a^N!7+A3%&TUM|pB?!#BD0y@vzb)m(1|UEOy_V$D_a*-TXuN^f+k w3ZnlkKK1iYhiC4b*#}RYzgH=FKQ?X}&$CaRvswPtH6Lm5TzJJ;Y|UN?0B@CI7XSbN literal 0 HcmV?d00001 diff --git a/test/root-ca.pem b/test/root-ca.pem new file mode 100644 index 00000000..4ec2f538 --- /dev/null +++ b/test/root-ca.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDXTCCAkWgAwIBAgIJAOIvDiVb18eVMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQwHhcNMTYwODE0MTY1NjExWhcNMjYwODEyMTY1NjExWjBF +MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 +ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEArVHWFn52Lbl1l59exduZntVSZyDYpzDND+S2LUcO6fRBWhV/1Kzox+2G +ZptbuMGmfI3iAnb0CFT4uC3kBkQQlXonGATSVyaFTFR+jq/lc0SP+9Bd7SBXieIV +eIXlY1TvlwIvj3Ntw9zX+scTA4SXxH6M0rKv9gTOub2vCMSHeF16X8DQr4XsZuQr +7Cp7j1I4aqOJyap5JTl5ijmG8cnu0n+8UcRlBzy99dLWJG0AfI3VRJdWpGTNVZ92 +aFff3RpK3F/WI2gp3qV1ynRAKuvmncGC3LDvYfcc2dgsc1N6Ffq8GIrkgRob6eBc +klDHp1d023Lwre+VaVDSo1//Y72UFwIDAQABo1AwTjAdBgNVHQ4EFgQUbNOlA6sN +XyzJjYqciKeId7g3/ZowHwYDVR0jBBgwFoAUbNOlA6sNXyzJjYqciKeId7g3/Zow +DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAVVaR5QWLZIRR4Dw6TSBn +BQiLpBSXN6oAxdDw6n4PtwW6CzydaA+creiK6LfwEsiifUfQe9f+T+TBSpdIYtMv +Z2H2tjlFX8VrjUFvPrvn5c28CuLI0foBgY8XGSkR2YMYzWw2jPEq3Th/KM5Catn3 +AFm3bGKWMtGPR4v+90chEN0jzaAmJYRrVUh9vea27bOCn31Nse6XXQPmSI6Gyncy +OAPUsvPClF3IjeL1tmBotWqSGn1cYxLo+Lwjk22A9h6vjcNQRyZF2VLVvtwYrNU3 +mwJ6GCLsLHpwW/yjyvn8iEltnJvByM/eeRnfXV6WDObyiZsE/n6DxIRJodQzFqy9 +GA== +-----END CERTIFICATE----- From 5fa435959c5bcc3f97808d5c462b703f25785e1d Mon Sep 17 00:00:00 2001 From: Tim Kuehn Date: Tue, 10 Jan 2017 21:22:08 -0800 Subject: [PATCH 02/27] Work around some type issues --- examples/concurrency.rs | 11 +-- examples/pubsub.rs | 17 ++--- examples/readme_errors.rs | 7 +- examples/readme_expanded.rs | 7 +- examples/readme_futures.rs | 9 +-- examples/readme_sync.rs | 6 +- examples/server_calling_server.rs | 13 ++-- examples/throughput.rs | 7 +- examples/two_clients.rs | 13 ++-- src/client.rs | 48 +++++++------ src/lib.rs | 9 +-- src/macros.rs | 112 ++++++++++++++++-------------- src/server.rs | 43 ++++++++---- 13 files changed, 168 insertions(+), 134 deletions(-) diff --git a/examples/concurrency.rs b/examples/concurrency.rs index 621146bb..03d7fb09 100644 --- a/examples/concurrency.rs +++ b/examples/concurrency.rs @@ -26,6 +26,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::time::{Duration, Instant}; use tarpc::future::{Connect}; use tarpc::util::{FirstSocketAddr, Never, spawn_core}; +use tarpc::{ClientConfig, ServerConfig}; use tokio_core::reactor; service! { @@ -50,7 +51,7 @@ impl Server { impl FutureService for Server { type ReadFut = CpuFuture, Never>; - fn read(&mut self, size: u32) -> Self::ReadFut { + fn read(&self, size: u32) -> Self::ReadFut { let request_number = self.request_count.fetch_add(1, Ordering::SeqCst); debug!("Server received read({}) no. {}", size, request_number); self.pool @@ -88,13 +89,13 @@ struct Stats { max: Option, } -fn run_once(mut clients: Vec, concurrency: u32) -> impl Future + 'static { +fn run_once(clients: Vec, concurrency: u32) -> impl Future + 'static { let start = Instant::now(); let num_clients = clients.len(); futures::stream::futures_unordered((0..concurrency as usize) .map(|iteration| (iteration + 1, iteration % num_clients)) .map(|(iteration, client_idx)| { - let mut client = &mut clients[client_idx]; + let client = &clients[client_idx]; let start = Instant::now(); debug!("Client {} reading (iteration {})...", client_idx, iteration); client.read(CHUNK_SIZE) @@ -149,7 +150,7 @@ fn main() { .map(Result::unwrap) .unwrap_or(4); - let addr = Server::new().listen("localhost:0".first_socket_addr()).wait().unwrap(); + let addr = Server::new().listen("localhost:0".first_socket_addr(), ServerConfig::new_tcp()).wait().unwrap(); info!("Server listening on {}.", addr); let clients = (0..num_clients) @@ -157,7 +158,7 @@ fn main() { .map(|i| (i, spawn_core())) .map(|(i, remote)| { info!("Client {} connecting...", i); - FutureClient::connect_remotely(&addr, &remote) + FutureClient::connect_remotely(&addr, &remote, ClientConfig::new_tcp()) .map_err(|e| panic!(e)) }) // Need an intermediate collection to connect the clients in parallel, diff --git a/examples/pubsub.rs b/examples/pubsub.rs index 3e770e65..d557e52b 100644 --- a/examples/pubsub.rs +++ b/examples/pubsub.rs @@ -23,6 +23,7 @@ use std::time::Duration; use tarpc::future::Connect as Fc; use tarpc::sync::Connect as Sc; use tarpc::util::{FirstSocketAddr, Message, Never}; +use tarpc::{ClientConfig, ServerConfig}; pub mod subscriber { service! { @@ -49,7 +50,7 @@ struct Subscriber { impl subscriber::FutureService for Subscriber { type ReceiveFut = futures::Finished<(), Never>; - fn receive(&mut self, message: String) -> Self::ReceiveFut { + fn receive(&self, message: String) -> Self::ReceiveFut { println!("{} received message: {}", self.id, message); futures::finished(()) } @@ -60,7 +61,7 @@ impl Subscriber { Subscriber { id: id, } - .listen("localhost:0".first_socket_addr()) + .listen("localhost:0".first_socket_addr(), ServerConfig::new_tcp()) .wait() .unwrap() } @@ -80,7 +81,7 @@ impl Publisher { impl publisher::FutureService for Publisher { type BroadcastFut = BoxFuture<(), Never>; - fn broadcast(&mut self, message: String) -> Self::BroadcastFut { + fn broadcast(&self, message: String) -> Self::BroadcastFut { futures::collect(self.clients .lock() .unwrap() @@ -94,9 +95,9 @@ impl publisher::FutureService for Publisher { type SubscribeFut = BoxFuture<(), Message>; - fn subscribe(&mut self, id: u32, address: SocketAddr) -> Self::SubscribeFut { + fn subscribe(&self, id: u32, address: SocketAddr) -> Self::SubscribeFut { let clients = self.clients.clone(); - subscriber::FutureClient::connect(&address) + subscriber::FutureClient::connect(&address, ClientConfig::new_tcp()) .map(move |subscriber| { println!("Subscribing {}.", id); clients.lock().unwrap().insert(id, subscriber); @@ -108,7 +109,7 @@ impl publisher::FutureService for Publisher { type UnsubscribeFut = BoxFuture<(), Never>; - fn unsubscribe(&mut self, id: u32) -> Self::UnsubscribeFut { + fn unsubscribe(&self, id: u32) -> Self::UnsubscribeFut { println!("Unsubscribing {}", id); self.clients.lock().unwrap().remove(&id).unwrap(); futures::finished(()).boxed() @@ -118,11 +119,11 @@ impl publisher::FutureService for Publisher { fn main() { let _ = env_logger::init(); let publisher_addr = Publisher::new() - .listen("localhost:0".first_socket_addr()) + .listen("localhost:0".first_socket_addr(), ServerConfig::new_tcp()) .wait() .unwrap(); - let mut publisher_client = publisher::SyncClient::connect(publisher_addr).unwrap(); + let publisher_client = publisher::SyncClient::connect(publisher_addr, ClientConfig::new_tcp()).unwrap(); let subscriber1 = Subscriber::new(0); publisher_client.subscribe(0, subscriber1).unwrap(); diff --git a/examples/readme_errors.rs b/examples/readme_errors.rs index b8f27eac..5e231f0a 100644 --- a/examples/readme_errors.rs +++ b/examples/readme_errors.rs @@ -15,6 +15,7 @@ extern crate serde_derive; use std::error::Error; use std::fmt; use tarpc::sync::Connect; +use tarpc::{ClientConfig, ServerConfig}; service! { rpc hello(name: String) -> String | NoNameGiven; @@ -39,7 +40,7 @@ impl Error for NoNameGiven { struct HelloServer; impl SyncService for HelloServer { - fn hello(&mut self, name: String) -> Result { + fn hello(&self, name: String) -> Result { if name == "" { Err(NoNameGiven) } else { @@ -49,8 +50,8 @@ impl SyncService for HelloServer { } fn main() { - let addr = HelloServer.listen("localhost:10000").unwrap(); - let client = SyncClient::connect(addr).unwrap(); + let addr = HelloServer.listen("localhost:10000", ServerConfig::new_tcp()).unwrap(); + let client = SyncClient::connect(addr, ClientConfig::new_tcp()).unwrap(); println!("{}", client.hello("Mom".to_string()).unwrap()); println!("{}", client.hello("".to_string()).unwrap_err()); } diff --git a/examples/readme_expanded.rs b/examples/readme_expanded.rs index 246fae15..3a65d061 100644 --- a/examples/readme_expanded.rs +++ b/examples/readme_expanded.rs @@ -25,6 +25,7 @@ use std::net::SocketAddr; use tarpc::future::Connect; use tarpc::util::FirstSocketAddr; use tarpc::util::Never; +use tarpc::{Listen, ClientConfig, ServerConfig}; use tokio_service::Service; #[derive(Clone, Copy)] @@ -34,7 +35,7 @@ impl HelloServer { fn listen(addr: SocketAddr) -> impl Future { let (tx, rx) = futures::oneshot(); tarpc::future::REMOTE.spawn(move |handle| { - Ok(tx.complete(tarpc::listen_with(addr, move || Ok(HelloServer), handle.clone()))) + Ok(tx.complete(ServerConfig::new_tcp().listen_with(addr, move || Ok(HelloServer), handle.clone()))) }); rx.map_err(|e| panic!(e)).and_then(|result| result) } @@ -46,7 +47,7 @@ impl Service for HelloServer { type Error = io::Error; type Future = Box, Error = io::Error>>; - fn call(&mut self, request: Self::Request) -> Self::Future { + fn call(&self, request: Self::Request) -> Self::Future { Ok(Ok(format!("Hello, {}!", request.unwrap()))).into_future().boxed() } } @@ -57,7 +58,7 @@ pub struct FutureClient(tarpc::Client); impl FutureClient { fn connect(addr: &SocketAddr) -> impl Future { - tarpc::Client::connect_remotely(addr, &tarpc::future::REMOTE).map(FutureClient) + tarpc::Client::connect_remotely(addr, &tarpc::future::REMOTE, ClientConfig::new_tcp()).map(FutureClient) } pub fn hello(&mut self, name: String) diff --git a/examples/readme_futures.rs b/examples/readme_futures.rs index 90022a68..64dc7a06 100644 --- a/examples/readme_futures.rs +++ b/examples/readme_futures.rs @@ -14,6 +14,7 @@ extern crate tokio_core; use futures::Future; use tarpc::future::Connect; use tarpc::util::{FirstSocketAddr, Never}; +use tarpc::{ClientConfig, ServerConfig}; use tokio_core::reactor; service! { @@ -26,7 +27,7 @@ struct HelloServer; impl FutureService for HelloServer { type HelloFut = futures::Finished; - fn hello(&mut self, name: String) -> Self::HelloFut { + fn hello(&self, name: String) -> Self::HelloFut { futures::finished(format!("Hello, {}!", name)) } } @@ -34,11 +35,11 @@ impl FutureService for HelloServer { fn main() { let addr = "localhost:10000".first_socket_addr(); let mut core = reactor::Core::new().unwrap(); - HelloServer.listen_with(addr, core.handle()).unwrap(); + HelloServer.listen_with(addr, core.handle(), ServerConfig::new_tcp()).unwrap(); core.run( - FutureClient::connect(&addr) + FutureClient::connect(&addr, ClientConfig::new_tcp()) .map_err(tarpc::Error::from) - .and_then(|mut client| client.hello("Mom".to_string())) + .and_then(|client| client.hello("Mom".to_string())) .map(|resp| println!("{}", resp)) ).unwrap(); } diff --git a/examples/readme_sync.rs b/examples/readme_sync.rs index ecfbcdc2..4150ae7a 100644 --- a/examples/readme_sync.rs +++ b/examples/readme_sync.rs @@ -22,14 +22,14 @@ service! { struct HelloServer; impl SyncService for HelloServer { - fn hello(&mut self, name: String) -> Result { + fn hello(&self, name: String) -> Result { Ok(format!("Hello, {}!", name)) } } fn main() { let addr = "localhost:10000"; - HelloServer.listen(addr).unwrap(); - let mut client = SyncClient::connect(addr).unwrap(); + HelloServer.listen(addr, tarpc::ServerConfig::new_tcp()).unwrap(); + let client = SyncClient::connect(addr, tarpc::ClientConfig::new_tcp()).unwrap(); println!("{}", client.hello("Mom".to_string()).unwrap()); } diff --git a/examples/server_calling_server.rs b/examples/server_calling_server.rs index efebdf14..be794fb2 100644 --- a/examples/server_calling_server.rs +++ b/examples/server_calling_server.rs @@ -18,6 +18,7 @@ use std::sync::{Arc, Mutex}; use tarpc::util::{FirstSocketAddr, Message, Never}; use tarpc::future::Connect as Fc; use tarpc::sync::Connect as Sc; +use tarpc::{ClientConfig, ServerConfig}; pub mod add { service! { @@ -41,7 +42,7 @@ struct AddServer; impl AddFutureService for AddServer { type AddFut = futures::Finished; - fn add(&mut self, x: i32, y: i32) -> Self::AddFut { + fn add(&self, x: i32, y: i32) -> Self::AddFut { futures::finished(x + y) } } @@ -62,7 +63,7 @@ impl DoubleServer { impl DoubleFutureService for DoubleServer { type DoubleFut = BoxFuture; - fn double(&mut self, x: i32) -> Self::DoubleFut { + fn double(&self, x: i32) -> Self::DoubleFut { self.client .lock() .unwrap() @@ -74,13 +75,13 @@ impl DoubleFutureService for DoubleServer { fn main() { let _ = env_logger::init(); - let add_addr = AddServer.listen("localhost:0".first_socket_addr()).wait().unwrap(); - let add_client = add::FutureClient::connect(&add_addr).wait().unwrap(); + let add_addr = AddServer.listen("localhost:0".first_socket_addr(), ServerConfig::new_tcp()).wait().unwrap(); + let add_client = add::FutureClient::connect(&add_addr, ClientConfig::new_tcp()).wait().unwrap(); let double = DoubleServer::new(add_client); - let double_addr = double.listen("localhost:0".first_socket_addr()).wait().unwrap(); + let double_addr = double.listen("localhost:0".first_socket_addr(), ServerConfig::new_tcp()).wait().unwrap(); - let double_client = double::SyncClient::connect(&double_addr).unwrap(); + let double_client = double::SyncClient::connect(&double_addr, ClientConfig::new_tcp()).unwrap(); for i in 0..5 { println!("{:?}", double_client.double(i).unwrap()); } diff --git a/examples/throughput.rs b/examples/throughput.rs index cf77e17f..99f4fd41 100644 --- a/examples/throughput.rs +++ b/examples/throughput.rs @@ -21,6 +21,7 @@ use std::io::{Read, Write, stdout}; use futures::Future; use tarpc::sync::Connect; use tarpc::util::{FirstSocketAddr, Never}; +use tarpc::{ClientConfig, ServerConfig}; lazy_static! { static ref BUF: Arc> = Arc::new(gen_vec(CHUNK_SIZE as usize)); @@ -44,7 +45,7 @@ struct Server; impl FutureService for Server { type ReadFut = futures::Finished>, Never>; - fn read(&mut self) -> Self::ReadFut { + fn read(&self) -> Self::ReadFut { futures::finished(BUF.clone()) } } @@ -52,8 +53,8 @@ impl FutureService for Server { const CHUNK_SIZE: u32 = 1 << 19; fn bench_tarpc(target: u64) { - let addr = Server.listen("localhost:0".first_socket_addr()).wait().unwrap(); - let client = SyncClient::connect(&addr).unwrap(); + let addr = Server.listen("localhost:0".first_socket_addr(), ServerConfig::new_tcp()).wait().unwrap(); + let client = SyncClient::connect(&addr, ClientConfig::new_tcp()).unwrap(); let start = time::Instant::now(); let mut nread = 0; while nread < target { diff --git a/examples/two_clients.rs b/examples/two_clients.rs index 7f82fa34..5c03254f 100644 --- a/examples/two_clients.rs +++ b/examples/two_clients.rs @@ -19,6 +19,7 @@ use baz::FutureServiceExt as BazExt; use futures::Future; use tarpc::util::{FirstSocketAddr, Never}; use tarpc::sync::Connect; +use tarpc::{ClientConfig, ServerConfig}; mod bar { service! { @@ -31,7 +32,7 @@ struct Bar; impl bar::FutureService for Bar { type BarFut = futures::Finished; - fn bar(&mut self, i: i32) -> Self::BarFut { + fn bar(&self, i: i32) -> Self::BarFut { futures::finished(i) } } @@ -47,7 +48,7 @@ struct Baz; impl baz::FutureService for Baz { type BazFut = futures::Finished; - fn baz(&mut self, s: String) -> Self::BazFut { + fn baz(&self, s: String) -> Self::BazFut { futures::finished(format!("Hello, {}!", s)) } } @@ -58,11 +59,11 @@ macro_rules! pos { fn main() { let _ = env_logger::init(); - let bar_addr = Bar.listen("localhost:0".first_socket_addr()).wait().unwrap(); - let baz_addr = Baz.listen("localhost:0".first_socket_addr()).wait().unwrap(); + let bar_addr = Bar.listen("localhost:0".first_socket_addr(), ServerConfig::new_tcp()).wait().unwrap(); + let baz_addr = Baz.listen("localhost:0".first_socket_addr(), ServerConfig::new_tcp()).wait().unwrap(); - let mut bar_client = bar::SyncClient::connect(&bar_addr).unwrap(); - let mut baz_client = baz::SyncClient::connect(&baz_addr).unwrap(); + let bar_client = bar::SyncClient::connect(&bar_addr, ClientConfig::new_tcp()).unwrap(); + let baz_client = baz::SyncClient::connect(&baz_addr, ClientConfig::new_tcp()).unwrap(); info!("Result: {:?}", bar_client.bar(17)); diff --git a/src/client.rs b/src/client.rs index b5367dda..77107b2b 100644 --- a/src/client.rs +++ b/src/client.rs @@ -55,22 +55,6 @@ pub mod tls { } } } - - impl Service for Client> - where Req: Serialize + Sync + Send + 'static, - Resp: Deserialize + Sync + Send + 'static, - E: Deserialize + Sync + Send + 'static - { - type Request = Req; - type Response = Result>; - type Error = io::Error; - type Future = futures::Map<> as Service>::Future, - fn(WireResponse) -> Result>>; - - fn call(&self, request: Self::Request) -> Self::Future { - self.inner.call(request).map(Self::map_err) - } - } } #[cfg(feature = "tls")] @@ -80,7 +64,7 @@ use self::tls::*; /// /// Typically, this would be combined with a serialization pre-processing step /// and a deserialization post-processing step. -pub struct Client +pub struct Client where Req: Serialize + 'static, Resp: Deserialize + 'static, E: Deserialize + 'static, @@ -100,15 +84,16 @@ impl Clone for Client } } -impl Service for Client +impl Service for Client where Req: Serialize + Sync + Send + 'static, Resp: Deserialize + Sync + Send + 'static, - E: Deserialize + Sync + Send + 'static + E: Deserialize + Sync + Send + 'static, + I: Io + 'static, { type Request = Req; type Response = Result>; type Error = io::Error; - type Future = futures::Map< as Service>::Future, + type Future = futures::Map< as Service>::Future, fn(WireResponse) -> Result>>; fn call(&self, request: Self::Request) -> Self::Future { @@ -150,14 +135,31 @@ impl fmt::Debug for Client } /// TODO: -pub struct Config - where S: Io -{ +pub struct Config { _stream: PhantomData, #[cfg(feature = "tls")] tls_client_cx: Option, } +#[cfg(feature = "tls")] +impl Default for Config { + fn default() -> Self { + Config { + _stream: PhantomData, + tls_client_cx: None, + } + } +} + +#[cfg(not(feature = "tls"))] +impl Default for Config { + fn default() -> Self { + Config { + _stream: PhantomData, + } + } +} + #[cfg(feature = "tls")] impl Config { /// Construct a new `Config` diff --git a/src/lib.rs b/src/lib.rs index bed59233..f2fcb7e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,6 +37,7 @@ //! //! use tarpc::sync::Connect; //! use tarpc::util::Never; +//! use tarpc::{ClientConfig, ServerConfig}; //! //! service! { //! rpc hello(name: String) -> String; @@ -46,15 +47,15 @@ //! struct HelloServer; //! //! impl SyncService for HelloServer { -//! fn hello(&mut self, name: String) -> Result { +//! fn hello(&self, name: String) -> Result { //! Ok(format!("Hello, {}!", name)) //! } //! } //! //! fn main() { //! let addr = "localhost:10000"; -//! let _server = HelloServer.listen(addr); -//! let mut client = SyncClient::connect(addr).unwrap(); +//! let _server = HelloServer.listen(addr, ServerConfig::default()); +//! let mut client = SyncClient::connect(addr, ClientConfig::default()).unwrap(); //! println!("{}", client.hello("Mom".to_string()).unwrap()); //! } //! ``` @@ -83,7 +84,7 @@ //! struct HelloServer; //! //! impl SyncService for HelloServer { -//! fn hello(&mut self, name: String) -> Result { +//! fn hello(&self, name: String) -> Result { //! Ok(format!("Hello, {}!", name)) //! } //! } diff --git a/src/macros.rs b/src/macros.rs index 3e9086a6..272ec6c7 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -336,7 +336,7 @@ macro_rules! service { #[allow(non_camel_case_types, unused)] #[derive(Debug)] - enum __tarpc_service_Request { + pub enum __tarpc_service_Request { NotIrrefutable(()), $( $fn_name(( $($in_,)* )) @@ -348,7 +348,7 @@ macro_rules! service { #[allow(non_camel_case_types, unused)] #[derive(Debug)] - enum __tarpc_service_Response { + pub enum __tarpc_service_Response { NotIrrefutable(()), $( $fn_name($out) @@ -360,7 +360,7 @@ macro_rules! service { #[allow(non_camel_case_types, unused)] #[derive(Debug)] - enum __tarpc_service_Error { + pub enum __tarpc_service_Error { NotIrrefutable(()), $( $fn_name($error) @@ -386,7 +386,7 @@ macro_rules! service { } $(#[$attr])* - fn $fn_name(&mut self, $($arg:$in_),*) -> ty_snake_to_camel!(Self::$fn_name); + fn $fn_name(&self, $($arg:$in_),*) -> ty_snake_to_camel!(Self::$fn_name); )* } @@ -515,7 +515,7 @@ macro_rules! service { } return __tarpc_service_FutureReply::$fn_name( $crate::futures::Future::then( - FutureService::$fn_name(&mut self.0, $($arg),*), + FutureService::$fn_name(&self.0, $($arg),*), __tarpc_service_wrap)); } )* @@ -535,7 +535,7 @@ macro_rules! service { { $( $(#[$attr])* - fn $fn_name(&mut self, $($arg:$in_),*) -> ::std::result::Result<$out, $error>; + fn $fn_name(&self, $($arg:$in_),*) -> ::std::result::Result<$out, $error>; )* } @@ -588,7 +588,7 @@ macro_rules! service { $crate::futures::Done<$out, $error>>, fn($crate::futures::Canceled) -> $error>>; } - fn $fn_name(&mut self, $($arg:$in_),*) -> ty_snake_to_camel!(Self::$fn_name) { + fn $fn_name(&self, $($arg:$in_),*) -> ty_snake_to_camel!(Self::$fn_name) { fn unimplemented(_: $crate::futures::Canceled) -> $error { // TODO(tikue): what do do if SyncService panics? unimplemented!() @@ -621,7 +621,7 @@ macro_rules! service { #[allow(unused)] #[derive(Clone, Debug)] /// The client stub that makes RPC calls to the server. Exposes a blocking interface. - pub struct SyncClient(::std::sync::Arc<::std::sync::Mutex>>) + pub struct SyncClient(FutureClient) where S: $crate::tokio_core::io::Io + 'static; // $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error, S>: $crate::future::Connect<'a, S>; @@ -634,7 +634,7 @@ macro_rules! service { let addr = $crate::util::FirstSocketAddr::try_first_socket_addr(&addr)?; let client = as $crate::future::Connect<'static, S>>::connect(&addr, config); let client = $crate::futures::Future::wait(client)?; - let client = SyncClient(::std::sync::Arc::new(::std::sync::Mutex::new(client))); + let client = SyncClient(client); ::std::result::Result::Ok(client) } } @@ -649,7 +649,7 @@ macro_rules! service { pub fn $fn_name(&self, $($arg: $in_),*) -> ::std::result::Result<$out, $crate::Error<$error>> { - let rpc = self.0.lock().unwrap().$fn_name($($arg),*); + let rpc = (self.0).$fn_name($($arg),*); $crate::futures::Future::wait(rpc) } )* @@ -719,7 +719,7 @@ macro_rules! service { #[allow(unused)] #[derive(Clone, Debug)] /// The client stub that makes RPC calls to the server. Exposes a Future interface. - pub struct FutureClient(__tarpc_service_Client) + pub struct FutureClient(__tarpc_service_Client) where S: $crate::tokio_core::io::Io + 'static; impl<'a, S> $crate::future::Connect<'a, S> for FutureClient @@ -727,8 +727,20 @@ macro_rules! service { $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error, S>: $crate::future::Connect<'a, S>, // F: $crate::futures::Future { - type ConnectFut = __tarpc_service_ConnectFuture; - type ConnectWithFut = __tarpc_service_ConnectWithFuture<'a, Self, S>; + type ConnectFut = $crate::futures::Map<<$crate::Client<__tarpc_service_Request, + __tarpc_service_Response, + __tarpc_service_Error, + S> + as $crate::future::Connect<'a, S>>::ConnectFut, + fn($crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error, S>) -> FutureClient>; + type ConnectWithFut = $crate::futures::Map<<$crate::Client<__tarpc_service_Request, + __tarpc_service_Response, + __tarpc_service_Error, S> as + $crate::future::Connect<'a, S>>::ConnectWithFut, + fn($crate::Client<__tarpc_service_Request, + __tarpc_service_Response, + __tarpc_service_Error, S>) + -> FutureClient>; fn connect_remotely(__tarpc_service_addr: &::std::net::SocketAddr, __tarpc_service_remote: &$crate::tokio_core::reactor::Remote, @@ -738,9 +750,7 @@ macro_rules! service { let client = <__tarpc_service_Client as $crate::future::Connect<'a, S>>::connect_remotely( __tarpc_service_addr, __tarpc_service_remote, __tarpc_client_config); - __tarpc_service_ConnectFuture { - inner: $crate::futures::Future::map(client, FutureClient) - } + $crate::futures::Future::map(client, FutureClient) } fn connect_with(__tarpc_service_addr: &::std::net::SocketAddr, @@ -751,20 +761,17 @@ macro_rules! service { let client = <__tarpc_service_Client as $crate::future::Connect<'a, S>>::connect_with( __tarpc_service_addr, __tarpc_service_handle, __tarpc_client_config); - __tarpc_service_ConnectWithFuture { - inner: $crate::futures::Future::map(client, FutureClient) - } + $crate::futures::Future::map(client, FutureClient) } } impl FutureClient where S: $crate::tokio_core::io::Io + 'static, - $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error, S>: $crate::tokio_service::Service { $( #[allow(unused)] $(#[$attr])* - pub fn $fn_name(&mut self, $($arg: $in_),*) + pub fn $fn_name(&self, $($arg: $in_),*) -> impl $crate::futures::Future> + 'static { @@ -825,34 +832,32 @@ macro_rules! service { #[allow(dead_code)] #[cfg(test)] mod syntax_test { - use futures::*; use util::Never; - service! { - // #[deny(warnings)] - // #[allow(non_snake_case)] - // rpc TestCamelCaseDoesntConflict(); - // rpc hello() -> String; - // #[doc="attr"] + #[deny(warnings)] + #[allow(non_snake_case)] + rpc TestCamelCaseDoesntConflict(); + rpc hello() -> String; + #[doc="attr"] rpc attr(s: String) -> String; - // rpc no_args_no_return(); - // rpc no_args() -> (); - // rpc one_arg(foo: String) -> i32; - // rpc two_args_no_return(bar: String, baz: u64); - // rpc two_args(bar: String, baz: u64) -> String; - // rpc no_args_ret_error() -> i32 | Never; - // rpc one_arg_ret_error(foo: String) -> String | Never; - // rpc no_arg_implicit_return_error() | Never; - // #[doc="attr"] - // rpc one_arg_implicit_return_error(foo: String) | Never; + rpc no_args_no_return(); + rpc no_args() -> (); + rpc one_arg(foo: String) -> i32; + rpc two_args_no_return(bar: String, baz: u64); + rpc two_args(bar: String, baz: u64) -> String; + rpc no_args_ret_error() -> i32 | Never; + rpc one_arg_ret_error(foo: String) -> String | Never; + rpc no_arg_implicit_return_error() | Never; + #[doc="attr"] + rpc one_arg_implicit_return_error(foo: String) | Never; } } -#[cfg(all(test, feature = "cow"))] +#[cfg(all(test))] mod functional_test { extern crate env_logger; - use {Tcp, ClientConfig, ServerConfig}; + use {ClientConfig, ServerConfig}; #[cfg(feature = "tls")] use Tls; @@ -958,6 +963,7 @@ mod functional_test { (addr, client) } } else { + use Tcp; fn get_server_config() -> ServerConfig { ServerConfig::new_tcp() @@ -1000,10 +1006,10 @@ mod functional_test { struct Server; impl SyncService for Server { - fn add(&mut self, x: i32, y: i32) -> Result { + fn add(&self, x: i32, y: i32) -> Result { Ok(x + y) } - fn hey(&mut self, name: String) -> Result { + fn hey(&self, name: String) -> Result { Ok(format!("Hey, {}.", name)) } } @@ -1011,7 +1017,7 @@ mod functional_test { #[test] fn simple() { let _ = env_logger::init(); - let (_, client) = start_server_with_sync_client::(Server); + let (_, client) = start_server_with_sync_client::, Server>(Server); let client = t!(client); assert_eq!(3, client.add(1, 2).unwrap()); assert_eq!("Hey, Tim.", client.hey("Tim".to_string()).unwrap()); @@ -1020,7 +1026,7 @@ mod functional_test { #[test] fn other_service() { let _ = env_logger::init(); - let (_, client) = start_server_with_sync_client::, Server>(Server); let client = client.expect("Could not connect!"); match client.foo().err().unwrap() { @@ -1041,13 +1047,13 @@ mod functional_test { impl FutureService for Server { type AddFut = Finished; - fn add(&mut self, x: i32, y: i32) -> Self::AddFut { + fn add(&self, x: i32, y: i32) -> Self::AddFut { finished(x + y) } type HeyFut = Finished; - fn hey(&mut self, name: String) -> Self::HeyFut { + fn hey(&self, name: String) -> Self::HeyFut { finished(format!("Hey, {}.", name)) } } @@ -1055,7 +1061,7 @@ mod functional_test { #[test] fn simple() { let _ = env_logger::init(); - let (_, mut client) = start_server_with_async_client::(Server); + let (_, client) = start_server_with_async_client::, Server>(Server); assert_eq!(3, client.add(1, 2).wait().unwrap()); assert_eq!("Hey, Tim.", client.hey("Tim".to_string()).wait().unwrap()); } @@ -1063,7 +1069,7 @@ mod functional_test { #[test] fn concurrent() { let _ = env_logger::init(); - let (_, mut client) = start_server_with_async_client::(Server); + let (_, client) = start_server_with_async_client::, Server>(Server); let req1 = client.add(1, 2); let req2 = client.add(3, 4); let req3 = client.hey("Tim".to_string()); @@ -1075,8 +1081,8 @@ mod functional_test { #[test] fn other_service() { let _ = env_logger::init(); - let (_, mut client) = - start_server_with_async_client::, Server>(Server); match client.foo().wait().err().unwrap() { ::Error::ServerDeserialize(_) => {} // good @@ -1097,7 +1103,7 @@ mod functional_test { impl error_service::FutureService for ErrorServer { type BarFut = ::futures::Failed; - fn bar(&mut self) -> Self::BarFut { + fn bar(&self) -> Self::BarFut { info!("Called bar"); failed("lol jk".into()) } @@ -1109,7 +1115,7 @@ mod functional_test { use self::error_service::*; let _ = env_logger::init(); - let (addr, mut client) = start_err_server_with_async_client::, ErrorServer>(ErrorServer); client.bar() .then(move |result| { @@ -1124,7 +1130,7 @@ mod functional_test { .wait() .unwrap(); - let mut client = get_sync_client::(&addr).unwrap(); + let client = get_sync_client::>(&addr).unwrap(); match client.bar().err().unwrap() { ::Error::App(e) => { assert_eq!(e.description(), "lol jk"); diff --git a/src/server.rs b/src/server.rs index 94cf39a0..cda1a201 100644 --- a/src/server.rs +++ b/src/server.rs @@ -13,7 +13,6 @@ use serde::{Deserialize, Serialize}; use std::io; use std::marker::PhantomData; use std::net::SocketAddr; -use tokio_core::io::Io; use tokio_core::net::TcpListener; use tokio_core::reactor::Handle; use tokio_proto::BindServer; @@ -126,31 +125,38 @@ impl Listen for Config { } /// TODO: -pub struct Config - where S: Io -{ +pub struct Config { #[cfg(feature = "tls")] tls_acceptor: Option, _client_stream: PhantomData, } #[cfg(feature = "tls")] -impl Config - where S: Io -{ - /// TODO - pub fn new_tcp() -> Config { +impl Default for Config { + fn default() -> Self { Config { - _client_stream: PhantomData, tls_acceptor: None, + _client_stream: PhantomData, + } + } +} + +#[cfg(not(feature = "tls"))] +impl Default for Config { + fn default() -> Self { + Config { + _client_stream: PhantomData, } } +} +#[cfg(feature = "tls")] +impl Config { /// TODO - pub fn new_tls(tls_acceptor: TlsAcceptor) -> Config { + pub fn new_tcp() -> Self { Config { _client_stream: PhantomData, - tls_acceptor: Some(tls_acceptor), + tls_acceptor: None, } } } @@ -158,11 +164,22 @@ impl Config #[cfg(not(feature = "tls"))] impl Config { /// TODO - pub fn new_tcp() -> Config { + pub fn new_tcp() -> Self { Config { _client_stream: PhantomData } } } +#[cfg(feature = "tls")] +impl Config { + /// TODO + pub fn new_tls(tls_acceptor: TlsAcceptor) -> Self { + Config { + _client_stream: PhantomData, + tls_acceptor: Some(tls_acceptor), + } + } +} + fn listener(addr: &SocketAddr, handle: &Handle) -> io::Result { const PENDING_CONNECTION_BACKLOG: i32 = 1024; From 6fc9469b1b86bc74eda7ec52cefa0fcd93bd0bbf Mon Sep 17 00:00:00 2001 From: Tim Kuehn Date: Wed, 11 Jan 2017 11:59:02 -0800 Subject: [PATCH 03/27] Remove stream type param --- src/client.rs | 436 +++++++++++++++++++++++--------------------------- src/lib.rs | 3 +- src/macros.rs | 139 +++++++--------- 3 files changed, 265 insertions(+), 313 deletions(-) diff --git a/src/client.rs b/src/client.rs index 77107b2b..2297e685 100644 --- a/src/client.rs +++ b/src/client.rs @@ -10,21 +10,25 @@ use protocol::Proto; use serde::{Deserialize, Serialize}; use std::fmt; use std::io; -use std::marker::PhantomData; use tokio_core::io::Io; use tokio_core::net::TcpStream; use tokio_proto::BindClient as ProtoBindClient; use tokio_proto::multiplex::Multiplex; use tokio_service::Service; +#[cfg(feature = "tls")] +use self::tls::*; +#[cfg(feature = "tls")] +use tokio_tls::{TlsStream}; + + type WireResponse = Result>, DeserializeError>; -type BindClient = >> as ProtoBindClient>::BindClient; +type BindClient = >> as ProtoBindClient>::BindClient; #[cfg(feature = "tls")] pub mod tls { use native_tls::TlsConnector; use super::*; - use tokio_tls::TlsStream; /// TLS context pub struct TlsClientContext { @@ -46,54 +50,97 @@ pub mod tls { } } - impl Config> { - /// Construct a new `Config>` + impl Config { + /// Construct a new `Config` pub fn new_tls(tls_client_cx: TlsClientContext) -> Self { Config { - _stream: PhantomData, tls_client_cx: Some(tls_client_cx), } } } } -#[cfg(feature = "tls")] -use self::tls::*; - /// A client that impls `tokio_service::Service` that writes and reads bytes. /// /// Typically, this would be combined with a serialization pre-processing step /// and a deserialization post-processing step. -pub struct Client +pub struct Client where Req: Serialize + 'static, Resp: Deserialize + 'static, E: Deserialize + 'static, - S: Io + 'static { - inner: BindClient, + inner: BindClient, +} + +#[derive(Debug)] +pub enum Either { + Tcp(TcpStream), + #[cfg(feature = "tls")] + Tls(TlsStream), +} + +impl From for Either { + fn from(stream: TcpStream) -> Self { + Either::Tcp(stream) + } +} + +#[cfg(feature = "tls")] +impl From> for Either { + fn from(stream: TlsStream) -> Self { + Either::Tls(stream) + } +} + +impl io::Read for Either { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match *self { + Either::Tcp(ref mut stream) => stream.read(buf), + #[cfg(feature = "tls")] + Either::Tls(ref mut stream) => stream.read(buf), + } + } +} + +impl io::Write for Either { + fn write(&mut self, buf: &[u8]) -> io::Result { + match *self { + Either::Tcp(ref mut stream) => stream.write(buf), + #[cfg(feature = "tls")] + Either::Tls(ref mut stream) => stream.write(buf), + } + } + + fn flush(&mut self) -> io::Result<()> { + match *self { + Either::Tcp(ref mut stream) => stream.flush(), + #[cfg(feature = "tls")] + Either::Tls(ref mut stream) => stream.flush(), + } + } } -impl Clone for Client +impl Io for Either { } + +impl Clone for Client where Req: Serialize + 'static, Resp: Deserialize + 'static, E: Deserialize + 'static, - S: Io + 'static { fn clone(&self) -> Self { Client { inner: self.inner.clone() } } } -impl Service for Client +impl Service for Client where Req: Serialize + Sync + Send + 'static, Resp: Deserialize + Sync + Send + 'static, E: Deserialize + Sync + Send + 'static, - I: Io + 'static, { type Request = Req; type Response = Result>; type Error = io::Error; - type Future = futures::Map< as Service>::Future, + type Future = futures::Map< as Service>::Future, fn(WireResponse) -> Result>>; fn call(&self, request: Self::Request) -> Self::Future { @@ -101,17 +148,15 @@ impl Service for Client } } -impl Client +impl Client where Req: Serialize + 'static, Resp: Deserialize + 'static, E: Deserialize + 'static, - S: Io + 'static { - fn new(inner: BindClient) -> Self + fn new(inner: BindClient) -> Self where Req: Serialize + Sync + Send + 'static, Resp: Deserialize + Sync + Send + 'static, E: Deserialize + Sync + Send + 'static, - S: Io + 'static { Client { inner: inner } } @@ -123,81 +168,65 @@ impl Client } } -impl fmt::Debug for Client +impl fmt::Debug for Client where Req: Serialize + 'static, Resp: Deserialize + 'static, E: Deserialize + 'static, - S: Io + 'static { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "Client {{ .. }}") } } -/// TODO: -pub struct Config { - _stream: PhantomData, +/// Client configuration when connecting. +#[derive(Default)] +pub struct Config { #[cfg(feature = "tls")] + /// Tls configuration tls_client_cx: Option, } #[cfg(feature = "tls")] -impl Default for Config { - fn default() -> Self { - Config { - _stream: PhantomData, - tls_client_cx: None, - } - } -} - -#[cfg(not(feature = "tls"))] -impl Default for Config { - fn default() -> Self { - Config { - _stream: PhantomData, - } - } -} - -#[cfg(feature = "tls")] -impl Config { - /// Construct a new `Config` +impl Config { + /// Construct a new `Config` pub fn new_tcp() -> Self { Config { - _stream: PhantomData, tls_client_cx: None, } } } #[cfg(not(feature = "tls"))] -impl Config { - /// Construct a new `Config` +impl Config { + /// Construct a new `Config` pub fn new_tcp() -> Self { - Config { _stream: PhantomData } + Config { } } } /// Exposes a trait for connecting asynchronously to servers. pub mod future { use future::REMOTE; - use futures::{self, Async, Future}; + use futures::{self, Async, Future, future}; use protocol::Proto; use serde::{Deserialize, Serialize}; use std::io; use std::marker::PhantomData; use std::net::SocketAddr; - use super::{Client, Config}; - use tokio_core::io::Io; + use super::{Client, Config, Either}; use tokio_core::net::TcpStream; use tokio_core::reactor; use tokio_proto::BindClient; + use tokio_core::net::TcpStreamNew; + #[cfg(feature = "tls")] + use super::tls::TlsClientContext; + #[cfg(feature = "tls")] + use tokio_tls::{ConnectAsync, TlsStream, TlsConnectorExt}; + #[cfg(feature = "tls")] + use errors::native2io; /// Types that can connect to a server asynchronously. - pub trait Connect<'a, S>: Sized - where S: Io - { + pub trait Connect<'a>: Sized { /// The type of the future returned when calling `connect`. type ConnectFut: Future + 'static; @@ -205,7 +234,7 @@ pub mod future { type ConnectWithFut: Future + 'a; /// Connects to a server located at the given address, using a remote to the default /// reactor. - fn connect(addr: &SocketAddr, config: Config) -> Self::ConnectFut { + fn connect(addr: &SocketAddr, config: Config) -> Self::ConnectFut { Self::connect_remotely(addr, &REMOTE, config) } @@ -213,130 +242,141 @@ pub mod future { /// remote. fn connect_remotely(addr: &SocketAddr, remote: &reactor::Remote, - config: Config) + config: Config) -> Self::ConnectFut; /// Connects to a server located at the given address, using the given reactor /// handle. fn connect_with(addr: &SocketAddr, handle: &'a reactor::Handle, - config: Config) + config: Config) -> Self::ConnectWithFut; } /// A future that resolves to a `Client` or an `io::Error`. - pub struct ConnectWithFuture<'a, Req, Resp, E, S, F> - where S: Io, - F: Future + pub struct ConnectWithFuture<'a, Req, Resp, E, F> + where F: Future { inner: futures::Map>, } - #[cfg(feature = "tls")] - mod tls { - use errors::native2io; - use super::*; - use super::super::tls::TlsClientContext; - use tokio_core::net::TcpStreamNew; - use tokio_tls::{ConnectAsync, TlsStream, TlsConnectorExt}; - - /// Provides the connection Fn impl for Tls - pub struct TlsConnectFn; - - impl FnOnce<(((TcpStream, TlsClientContext),))> for TlsConnectFn { - type Output = futures::MapErr, - fn(::native_tls::Error) -> io::Error>; - - extern "rust-call" fn call_once(self, - ((tcp, tls_client_cx),): ((TcpStream, - TlsClientContext),)) - -> Self::Output { - tls_client_cx.tls_connector + /// Provides the connection Fn impl for Tls + pub struct ConnectFn { + #[cfg(feature = "tls")] + tls_ctx: Option + } + + impl FnOnce<(TcpStream,)> for ConnectFn { + #[cfg(feature = "tls")] + type Output = future::Either< + future::FutureResult, + futures::Map< + futures::MapErr< + ConnectAsync, + fn(::native_tls::Error) -> io::Error>, + fn(TlsStream) -> Either>>; + #[cfg(not(feature = "tls"))] + type Output = future::FutureResult; + + extern "rust-call" fn call_once(self, (tcp,): (TcpStream,)) -> Self::Output { + #[cfg(feature = "tls")] + match self.tls_ctx { + None => future::Either::A(future::ok(Either::from(tcp))), + Some(tls_client_cx) => future::Either::B(tls_client_cx.tls_connector .connect_async(&tls_client_cx.domain, tcp) - .map_err(native2io) + .map_err(native2io as fn(_) -> _) + .map(Either::from as fn(_) -> _)), } + #[cfg(not(feature = "tls"))] + future::ok(Either::from(tcp)) } + } - type TlsConnectFut = - futures::AndThen>, - futures::MapErr, - fn(::native_tls::Error) -> io::Error>, - TlsConnectFn>; - - impl<'a, Req, Resp, E> Connect<'a, TlsStream> - for Client> - where Req: Serialize + Sync + Send + 'static, - Resp: Deserialize + Sync + Send + 'static, - E: Deserialize + Sync + Send + 'static - { - type ConnectFut = ConnectFuture>; - type ConnectWithFut = ConnectWithFuture<'a, - Req, - Resp, - E, - TlsStream, - TlsConnectFut>; - - fn connect_remotely(addr: &SocketAddr, - remote: &reactor::Remote, - config: Config>) - -> Self::ConnectFut { - let addr = *addr; - let (tx, rx) = futures::oneshot(); - remote.spawn(move |handle| { - let handle2 = handle.clone(); - TcpStream::connect(&addr, handle) - .and_then(move |socket| { - let tls_client_cx = config.tls_client_cx - .expect("Need TlsClientContext for a TlsStream"); - tls_client_cx.tls_connector - .connect_async(&tls_client_cx.domain, socket) - .map_err(native2io) - }) - .map(move |tcp| Client::new(Proto::new().bind_client(&handle2, tcp))) - .then(move |result| { - tx.complete(result); - Ok(()) - }) - }); - ConnectFuture { inner: rx } - } + type ConnectFut = + futures::AndThen>::Output, + ConnectFn>; - fn connect_with(addr: &SocketAddr, - handle: &'a reactor::Handle, - config: Config>) - -> Self::ConnectWithFut { - let tls_client_cx = config.tls_client_cx - .expect("Need TlsClientContext for a TlsStream"); - ConnectWithFuture { - inner: TcpStream::connect(addr, handle) - .join(futures::finished(tls_client_cx)) - .and_then(TlsConnectFn) - .map(MultiplexConnect::new(handle)), - } - } + + impl<'a, Req, Resp, E> Connect<'a> for Client + where Req: Serialize + Sync + Send + 'static, + Resp: Deserialize + Sync + Send + 'static, + E: Deserialize + Sync + Send + 'static + { + type ConnectFut = ConnectFuture; + type ConnectWithFut = ConnectWithFuture<'a, + Req, + Resp, + E, + ConnectFut>; + + fn connect_remotely(addr: &SocketAddr, + remote: &reactor::Remote, + _config: Config) -> Self::ConnectFut { + let addr = *addr; + let (tx, rx) = futures::oneshot(); + remote.spawn(move |handle| { + let handle2 = handle.clone(); + TcpStream::connect(&addr, handle) + .and_then(move |socket| { + #[cfg(feature = "tls")] + match _config.tls_client_cx { + Some(tls_client_cx) => { + future::Either::A(tls_client_cx.tls_connector + .connect_async(&tls_client_cx.domain, socket) + .map(Either::Tls) + .map_err(native2io)) + } + None => future::Either::B(future::ok(Either::Tcp(socket))), + } + #[cfg(not(feature = "tls"))] + future::ok(Either::Tcp(socket)) + }) + .map(move |tcp| Client::new(Proto::new().bind_client(&handle2, tcp))) + .then(move |result| { + tx.complete(result); + Ok(()) + }) + }); + ConnectFuture { inner: rx } + } + + fn connect_with(addr: &SocketAddr, + handle: &'a reactor::Handle, + _config: Config) + -> Self::ConnectWithFut { + #[cfg(feature = "tls")] + return ConnectWithFuture { + inner: TcpStream::connect(addr, handle) + .and_then(ConnectFn { + tls_ctx: _config.tls_client_cx, + }) + .map(MultiplexConnect::new(handle)) + }; + #[cfg(not(feature = "tls"))] + return ConnectWithFuture { + inner: TcpStream::connect(addr, handle) + .and_then(ConnectFn {}) + .map(MultiplexConnect::new(handle)) + }; } } /// A future that resolves to a `Client` or an `io::Error`. - pub struct ConnectFuture + pub struct ConnectFuture where Req: Serialize + 'static, Resp: Deserialize + 'static, E: Deserialize + 'static, - S: Io + 'static { - inner: futures::Oneshot>>, + inner: futures::Oneshot>>, } - impl Future for ConnectFuture + impl Future for ConnectFuture where Req: Serialize + 'static, Resp: Deserialize + 'static, E: Deserialize + 'static, - S: Io + 'static { - type Item = Client; + type Item = Client; type Error = io::Error; fn poll(&mut self) -> futures::Poll { @@ -349,14 +389,14 @@ pub mod future { } } - impl<'a, Req, Resp, E, S, F> Future for ConnectWithFuture<'a, Req, Resp, E, S, F> + impl<'a, Req, Resp, E, F, I> Future for ConnectWithFuture<'a, Req, Resp, E, F> where Req: Serialize + Sync + Send + 'static, Resp: Deserialize + Sync + Send + 'static, E: Deserialize + Sync + Send + 'static, - S: Io + 'static, - F: Future + F: Future, + I: Into { - type Item = Client; + type Item = Client; type Error = io::Error; fn poll(&mut self) -> futures::Poll { @@ -372,57 +412,16 @@ pub mod future { } } - impl<'a, Req, Resp, E, S> FnOnce<(S,)> for MultiplexConnect<'a, Req, Resp, E> + impl<'a, Req, Resp, E, I> FnOnce<(I,)> for MultiplexConnect<'a, Req, Resp, E> where Req: Serialize + Sync + Send + 'static, Resp: Deserialize + Sync + Send + 'static, E: Deserialize + Sync + Send + 'static, - S: Io + 'static - { - type Output = Client; - - extern "rust-call" fn call_once(self, (s,): (S,)) -> Self::Output { - Client::new(Proto::new().bind_client(self.0, s)) - } - } - - impl<'a, Req, Resp, E> Connect<'a, TcpStream> for Client - where Req: Serialize + Sync + Send + 'static, - Resp: Deserialize + Sync + Send + 'static, - E: Deserialize + Sync + Send + 'static + I: Into, { - type ConnectFut = ConnectFuture; - type ConnectWithFut = ConnectWithFuture<'a, - Req, - Resp, - E, - TcpStream, - ::tokio_core::net::TcpStreamNew>; + type Output = Client; - fn connect_remotely(addr: &SocketAddr, - remote: &reactor::Remote, - _config: Config) - -> Self::ConnectFut { - let addr = *addr; - let (tx, rx) = futures::oneshot(); - remote.spawn(move |handle| { - let handle2 = handle.clone(); - TcpStream::connect(&addr, handle) - .map(move |tcp| Client::new(Proto::new().bind_client(&handle2, tcp))) - .then(move |result| { - tx.complete(result); - Ok(()) - }) - }); - ConnectFuture { inner: rx } - } - - fn connect_with(addr: &SocketAddr, - handle: &'a reactor::Handle, - _config: Config) - -> Self::ConnectWithFut { - ConnectWithFuture { - inner: TcpStream::connect(addr, handle).map(MultiplexConnect::new(handle)), - } + extern "rust-call" fn call_once(self, (stream,): (I,)) -> Self::Output { + Client::new(Proto::new().bind_client(self.0, stream.into())) } } } @@ -434,23 +433,19 @@ pub mod sync { use std::io; use std::net::ToSocketAddrs; use super::{Client, Config}; - use tokio_core::io::Io; - use tokio_core::net::TcpStream; /// Types that can connect to a server synchronously. - pub trait Connect: Sized - where S: Io - { + pub trait Connect: Sized { /// Connects to a server located at the given address. - fn connect(addr: A, config: Config) -> Result where A: ToSocketAddrs; + fn connect(addr: A, config: Config) -> Result where A: ToSocketAddrs; } - impl Connect for Client + impl Connect for Client where Req: Serialize + Sync + Send + 'static, Resp: Deserialize + Sync + Send + 'static, E: Deserialize + Sync + Send + 'static { - fn connect(addr: A, config: Config) -> Result + fn connect(addr: A, config: Config) -> Result where A: ToSocketAddrs { let addr = if let Some(a) = addr.to_socket_addrs()?.next() { @@ -460,32 +455,7 @@ pub mod sync { "`ToSocketAddrs::to_socket_addrs` returned an empty \ iterator.")); }; - >::connect(&addr, config).wait() + ::connect(&addr, config).wait() } } - - cfg_if! { - if #[cfg(feature = "tls")] { - use ::tokio_tls::TlsStream; - - impl Connect> for Client> - where Req: Serialize + Sync + Send + 'static, - Resp: Deserialize + Sync + Send + 'static, - E: Deserialize + Sync + Send + 'static - { - fn connect(addr: A, config: Config>) -> Result - where A: ToSocketAddrs - { - let addr = if let Some(a) = addr.to_socket_addrs()?.next() { - a - } else { - return Err(io::Error::new(io::ErrorKind::AddrNotAvailable, - "`ToSocketAddrs::to_socket_addrs` returned an \ - empty iterator.")); - }; - >>::connect(&addr, config).wait() - } - } - } else {} - } } diff --git a/src/lib.rs b/src/lib.rs index f2fcb7e0..a1502982 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -158,7 +158,8 @@ pub mod util; #[macro_use] mod macros; /// Provides the base client stubs used by the service macro. -mod client; +#[doc(hidden)] +pub mod client; /// Provides the base server boilerplate used by service implementations. mod server; /// Provides implementations of `ClientProto` and `ServerProto` that implement the tarpc protocol. diff --git a/src/macros.rs b/src/macros.rs index 272ec6c7..58a47811 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -621,27 +621,23 @@ macro_rules! service { #[allow(unused)] #[derive(Clone, Debug)] /// The client stub that makes RPC calls to the server. Exposes a blocking interface. - pub struct SyncClient(FutureClient) - where S: $crate::tokio_core::io::Io + 'static; - // $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error, S>: $crate::future::Connect<'a, S>; - - impl $crate::sync::Connect for SyncClient - where S: $crate::tokio_core::io::Io + 'static, - $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error, S>: $crate::future::Connect<'static, S> { - fn connect(addr: A, config: $crate::ClientConfig) -> ::std::result::Result + pub struct SyncClient(FutureClient); + + impl $crate::sync::Connect for SyncClient + where $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error>: $crate::future::Connect<'static> { + fn connect(addr: A, config: $crate::ClientConfig) -> ::std::result::Result where A: ::std::net::ToSocketAddrs, { let addr = $crate::util::FirstSocketAddr::try_first_socket_addr(&addr)?; - let client = as $crate::future::Connect<'static, S>>::connect(&addr, config); + let client = >::connect(&addr, config); let client = $crate::futures::Future::wait(client)?; let client = SyncClient(client); ::std::result::Result::Ok(client) } } - impl SyncClient - where S: $crate::tokio_core::io::Io + 'static, - $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error, S>: $crate::sync::Connect + impl SyncClient + where $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error>: $crate::sync::Connect { $( #[allow(unused)] @@ -656,27 +652,23 @@ macro_rules! service { } #[allow(non_camel_case_types)] - type __tarpc_service_Client = + type __tarpc_service_Client = $crate::Client<__tarpc_service_Request, __tarpc_service_Response, - __tarpc_service_Error, - S>; + __tarpc_service_Error>; #[allow(non_camel_case_types)] /// Implementation detail: Pending connection. - pub struct __tarpc_service_ConnectFuture - where S: $crate::tokio_core::io::Io + 'static { - // $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error, S>: $crate::future::Connect<'static, S> { + pub struct __tarpc_service_ConnectFuture { + // $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error>: $crate::future::Connect<'static> { inner: $crate::futures::Map<$crate::ConnectFuture<__tarpc_service_Request, __tarpc_service_Response, - __tarpc_service_Error, - S>, - fn(__tarpc_service_Client) -> T>, + __tarpc_service_Error>, + fn(__tarpc_service_Client) -> T>, } - impl $crate::futures::Future for __tarpc_service_ConnectFuture - where S: $crate::tokio_core::io::Io + 'static { - // $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error, S>: $crate::future::Connect<'static, S> { + impl $crate::futures::Future for __tarpc_service_ConnectFuture { + // $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error>: $crate::future::Connect<'static> { type Item = T; type Error = ::std::io::Error; @@ -687,26 +679,21 @@ macro_rules! service { #[allow(non_camel_case_types)] /// Implementation detail: Pending connection. - pub struct __tarpc_service_ConnectWithFuture<'a, T, S> - where S: $crate::tokio_core::io::Io + 'static + Sized, - $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error, S>: $crate::future::Connect<'a, S>, - // F: $crate::futures::Future - { + pub struct __tarpc_service_ConnectWithFuture<'a, T> + where $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error>: $crate::future::Connect<'a>, + { inner: $crate::futures::Map<$crate::ConnectWithFuture<'a, __tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error, - S, // FIXME: shouldn't need box here // I don' think... - ::std::boxed::Box<$crate::futures::Future>>, - fn(__tarpc_service_Client) -> T>, + ::std::boxed::Box<$crate::futures::Future>>, + fn(__tarpc_service_Client) -> T>, } - impl<'a, T, S> $crate::futures::Future for __tarpc_service_ConnectWithFuture<'a, T, S> - where S: $crate::tokio_core::io::Io + 'static, - $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error, S>: $crate::future::Connect<'a, S>, - // F: $crate::futures::Future + impl<'a, T> $crate::futures::Future for __tarpc_service_ConnectWithFuture<'a, T> + where $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error>: $crate::future::Connect<'a>, { type Item = T; type Error = ::std::io::Error; @@ -719,35 +706,31 @@ macro_rules! service { #[allow(unused)] #[derive(Clone, Debug)] /// The client stub that makes RPC calls to the server. Exposes a Future interface. - pub struct FutureClient(__tarpc_service_Client) - where S: $crate::tokio_core::io::Io + 'static; - - impl<'a, S> $crate::future::Connect<'a, S> for FutureClient - where S: $crate::tokio_core::io::Io + 'a, - $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error, S>: $crate::future::Connect<'a, S>, - // F: $crate::futures::Future - { + pub struct FutureClient(__tarpc_service_Client); + + impl<'a> $crate::future::Connect<'a> for FutureClient + where $crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error>: $crate::future::Connect<'a>, + { type ConnectFut = $crate::futures::Map<<$crate::Client<__tarpc_service_Request, __tarpc_service_Response, - __tarpc_service_Error, - S> - as $crate::future::Connect<'a, S>>::ConnectFut, - fn($crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error, S>) -> FutureClient>; + __tarpc_service_Error> + as $crate::future::Connect<'a>>::ConnectFut, + fn($crate::Client<__tarpc_service_Request, __tarpc_service_Response, __tarpc_service_Error>) -> FutureClient>; type ConnectWithFut = $crate::futures::Map<<$crate::Client<__tarpc_service_Request, __tarpc_service_Response, - __tarpc_service_Error, S> as - $crate::future::Connect<'a, S>>::ConnectWithFut, + __tarpc_service_Error> as + $crate::future::Connect<'a>>::ConnectWithFut, fn($crate::Client<__tarpc_service_Request, __tarpc_service_Response, - __tarpc_service_Error, S>) - -> FutureClient>; + __tarpc_service_Error>) + -> FutureClient>; fn connect_remotely(__tarpc_service_addr: &::std::net::SocketAddr, __tarpc_service_remote: &$crate::tokio_core::reactor::Remote, - __tarpc_client_config: $crate::ClientConfig) + __tarpc_client_config: $crate::ClientConfig) -> Self::ConnectFut { - let client = <__tarpc_service_Client as $crate::future::Connect<'a, S>>::connect_remotely( + let client = <__tarpc_service_Client as $crate::future::Connect<'a>>::connect_remotely( __tarpc_service_addr, __tarpc_service_remote, __tarpc_client_config); $crate::futures::Future::map(client, FutureClient) @@ -755,19 +738,17 @@ macro_rules! service { fn connect_with(__tarpc_service_addr: &::std::net::SocketAddr, __tarpc_service_handle: &'a $crate::tokio_core::reactor::Handle, - __tarpc_client_config: $crate::ClientConfig) + __tarpc_client_config: $crate::ClientConfig) -> Self::ConnectWithFut { - let client = <__tarpc_service_Client as $crate::future::Connect<'a, S>>::connect_with( + let client = <__tarpc_service_Client as $crate::future::Connect<'a>>::connect_with( __tarpc_service_addr, __tarpc_service_handle, __tarpc_client_config); $crate::futures::Future::map(client, FutureClient) } } - impl FutureClient - where S: $crate::tokio_core::io::Io + 'static, - { + impl FutureClient { $( #[allow(unused)] $(#[$attr])* @@ -883,7 +864,7 @@ mod functional_test { use ::TlsClientContext; use ::native_tls::{Pkcs12, TlsAcceptor, TlsConnector}; - fn tls_context() -> (ServerConfig, ClientConfig) { + fn tls_context() -> (ServerConfig, ClientConfig) { let buf = include_bytes!("../test/identity.p12"); let pkcs12 = t!(Pkcs12::from_der(buf, "mypass")); let acceptor = t!(t!(TlsAcceptor::builder(pkcs12)).build()); @@ -904,7 +885,7 @@ mod functional_test { use self::security_framework::certificate::SecCertificate; use native_tls::backend::security_framework::TlsConnectorBuilderExt; - fn get_tls_client_config() -> ClientConfig { + fn get_tls_client_config() -> ClientConfig { let buf = include_bytes!("../test/root-ca.der"); let cert = t!(SecCertificate::from_der(buf)); let mut connector = t!(TlsConnector::builder()); @@ -918,11 +899,11 @@ mod functional_test { } else if #[cfg(all(not(target_os = "macos"), not(windows)))] { use native_tls::backend::openssl::TlsConnectorBuilderExt; - fn get_tls_client_config() -> ClientConfig { + fn get_tls_client_config() -> ClientConfig { let mut connector = t!(TlsConnector::builder()); t!(connector.builder_mut() .builder_mut() - .set_ca_file("../test/root-ca.pem")); + .set_ca_file("test/root-ca.pem")); ClientConfig::new_tls(TlsClientContext { domain: DOMAIN.into(), @@ -937,26 +918,26 @@ mod functional_test { } } - fn get_sync_client>(addr: &::std::net::SocketAddr) -> ::std::io::Result { + fn get_sync_client(addr: &::std::net::SocketAddr) -> ::std::io::Result { let client_config = get_tls_client_config(); C::connect(addr, client_config) } - fn start_server_with_sync_client, S: SyncServiceExt>(server: S) -> (::std::net::SocketAddr, ::std::io::Result) { + fn start_server_with_sync_client(server: S) -> (::std::net::SocketAddr, ::std::io::Result) { let (server_config, client_config) = tls_context(); let addr = t!(server.listen("localhost:0".first_socket_addr(), server_config)); let client = C::connect(addr, client_config); (addr, client) } - fn start_server_with_async_client<'a, C: ::future::Connect<'a, Tls>, S: FutureServiceExt>(server: S) -> (::std::net::SocketAddr, C) { + fn start_server_with_async_client<'a, C: ::future::Connect<'a>, S: FutureServiceExt>(server: S) -> (::std::net::SocketAddr, C) { let (server_config, client_config) = tls_context(); let addr = t!(server.listen("localhost:0".first_socket_addr(), server_config).wait()); let client = t!(C::connect(&addr, client_config).wait()); (addr, client) } - fn start_err_server_with_async_client<'a, C: ::future::Connect<'a, Tls>, S: error_service::FutureServiceExt>(server: S) -> (::std::net::SocketAddr, C) { + fn start_err_server_with_async_client<'a, C: ::future::Connect<'a>, S: error_service::FutureServiceExt>(server: S) -> (::std::net::SocketAddr, C) { let (server_config, client_config) = tls_context(); let addr = t!(server.listen("localhost:0".first_socket_addr(), server_config).wait()); let client = t!(C::connect(&addr, client_config).wait()); @@ -969,28 +950,28 @@ mod functional_test { ServerConfig::new_tcp() } - fn get_client_config() -> ClientConfig { + fn get_client_config() -> ClientConfig { ClientConfig::new_tcp() } - fn get_sync_client>(addr: &::std::net::SocketAddr) -> ::std::io::Result { + fn get_sync_client(addr: &::std::net::SocketAddr) -> ::std::io::Result { C::connect(addr, get_client_config()) } /// start server and return `SyncClient` - fn start_server_with_sync_client, S: SyncServiceExt>(server: S) -> (::std::net::SocketAddr, ::std::io::Result) { + fn start_server_with_sync_client(server: S) -> (::std::net::SocketAddr, ::std::io::Result) { let addr = t!(server.listen("localhost:0".first_socket_addr(), get_server_config())); let client = C::connect(addr, get_client_config()); (addr, client) } - fn start_server_with_async_client<'a, C: ::future::Connect<'a, Tcp>, S: FutureServiceExt>(server: S) -> (::std::net::SocketAddr, C) { + fn start_server_with_async_client<'a, C: ::future::Connect<'a>, S: FutureServiceExt>(server: S) -> (::std::net::SocketAddr, C) { let addr = t!(server.listen("localhost:0".first_socket_addr(), get_server_config()).wait()); let client = t!(C::connect(&addr, get_client_config()).wait()); (addr, client) } - fn start_err_server_with_async_client<'a, C: ::future::Connect<'a, Tcp>, S: error_service::FutureServiceExt>(server: S) -> (::std::net::SocketAddr, C) { + fn start_err_server_with_async_client<'a, C: ::future::Connect<'a>, S: error_service::FutureServiceExt>(server: S) -> (::std::net::SocketAddr, C) { let addr = t!(server.listen("localhost:0".first_socket_addr(), get_server_config()).wait()); let client = t!(C::connect(&addr, get_client_config()).wait()); (addr, client) @@ -1017,7 +998,7 @@ mod functional_test { #[test] fn simple() { let _ = env_logger::init(); - let (_, client) = start_server_with_sync_client::, Server>(Server); + let (_, client) = start_server_with_sync_client::(Server); let client = t!(client); assert_eq!(3, client.add(1, 2).unwrap()); assert_eq!("Hey, Tim.", client.hey("Tim".to_string()).unwrap()); @@ -1026,7 +1007,7 @@ mod functional_test { #[test] fn other_service() { let _ = env_logger::init(); - let (_, client) = start_server_with_sync_client::, + let (_, client) = start_server_with_sync_client::(Server); let client = client.expect("Could not connect!"); match client.foo().err().unwrap() { @@ -1061,7 +1042,7 @@ mod functional_test { #[test] fn simple() { let _ = env_logger::init(); - let (_, client) = start_server_with_async_client::, Server>(Server); + let (_, client) = start_server_with_async_client::(Server); assert_eq!(3, client.add(1, 2).wait().unwrap()); assert_eq!("Hey, Tim.", client.hey("Tim".to_string()).wait().unwrap()); } @@ -1069,7 +1050,7 @@ mod functional_test { #[test] fn concurrent() { let _ = env_logger::init(); - let (_, client) = start_server_with_async_client::, Server>(Server); + let (_, client) = start_server_with_async_client::(Server); let req1 = client.add(1, 2); let req2 = client.add(3, 4); let req3 = client.hey("Tim".to_string()); @@ -1082,7 +1063,7 @@ mod functional_test { fn other_service() { let _ = env_logger::init(); let (_, client) = - start_server_with_async_client::, + start_server_with_async_client::(Server); match client.foo().wait().err().unwrap() { ::Error::ServerDeserialize(_) => {} // good @@ -1115,7 +1096,7 @@ mod functional_test { use self::error_service::*; let _ = env_logger::init(); - let (addr, client) = start_err_server_with_async_client::, + let (addr, client) = start_err_server_with_async_client::(ErrorServer); client.bar() .then(move |result| { @@ -1130,7 +1111,7 @@ mod functional_test { .wait() .unwrap(); - let client = get_sync_client::>(&addr).unwrap(); + let client = get_sync_client::(&addr).unwrap(); match client.bar().err().unwrap() { ::Error::App(e) => { assert_eq!(e.description(), "lol jk"); From 31b993ffa2d5a6d5fe1adfc647b175efea9744c2 Mon Sep 17 00:00:00 2001 From: Tim Kuehn Date: Wed, 11 Jan 2017 12:00:53 -0800 Subject: [PATCH 04/27] Update pre-push to test tls --- hooks/pre-push | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hooks/pre-push b/hooks/pre-push index 6d63c15c..b3ddbfeb 100755 --- a/hooks/pre-push +++ b/hooks/pre-push @@ -70,10 +70,12 @@ run_cargo() { rustup run $2 cargo $1 &>/dev/null else rustup run nightly cargo $1 --features unstable &>/dev/null + rustup run nightly cargo $1 --features unstable,tls &>/dev/null fi else printf "${PREFIX} $VERB... " cargo $1 &>/dev/null + cargo $1 --features tls &>/dev/null fi if [ "$?" != "0" ]; then printf "${FAILURE}\n" From 7e4257f2dee18c5ab27a4fe8ae535c6829102d13 Mon Sep 17 00:00:00 2001 From: Christopher Brickley Date: Wed, 11 Jan 2017 20:12:22 -0500 Subject: [PATCH 05/27] chore(deps): use crates.io versions for tls dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A new version of `native-tls` has been released so we don’t need to depend on the git version anymore. --- Cargo.toml | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 60c618b3..827f11ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ cfg-if = "0.1.0" futures = { git = "https://github.com/alexcrichton/futures-rs" } lazy_static = "0.2" log = "0.3" +native-tls = { version = "0.1.1", optional = true } net2 = "0.2" scoped-pool = "1.0" serde = "0.8" @@ -27,19 +28,11 @@ take = "0.1" tokio-service = { git = "https://github.com/tokio-rs/tokio-service" } tokio-proto = { git = "https://github.com/tokio-rs/tokio-proto", rev = "cdbdb11f3349a5a540cf8ddb60431ebbca4be712" } tokio-core = { git = "https://github.com/tokio-rs/tokio-core" } +tokio-tls = { version = "0.1", optional = true } [replace] "tokio-core:0.1.3" = { git = "https://github.com/tokio-rs/tokio-core" } -"futures:0.1.7" = { git = "https://github.com/alexcrichton/futures-rs" } -"native-tls:0.1.0" = { git = "https://github.com/sfackler/rust-native-tls" } - -[dependencies.native-tls] -optional = true -git = "https://github.com/sfackler/rust-native-tls" - -[dependencies.tokio-tls] -optional = true -git = "https://github.com/tokio-rs/tokio-tls" +"futures:0.1.8" = { git = "https://github.com/alexcrichton/futures-rs" } [dev-dependencies] chrono = "0.2" From 31c2c88aac6ceacd516e8160628045fe97bbc843 Mon Sep 17 00:00:00 2001 From: Christopher Brickley Date: Wed, 11 Jan 2017 20:18:57 -0500 Subject: [PATCH 06/27] chore(error): native2io -> native_to_io --- src/client.rs | 91 ++++++++++++++++++++++----------------------------- src/errors.rs | 2 +- src/server.rs | 10 +++--- 3 files changed, 46 insertions(+), 57 deletions(-) diff --git a/src/client.rs b/src/client.rs index 2297e685..b8fc73d2 100644 --- a/src/client.rs +++ b/src/client.rs @@ -7,6 +7,8 @@ use WireError; use bincode::serde::DeserializeError; use futures::{self, Future}; use protocol::Proto; +#[cfg(feature = "tls")] +use self::tls::*; use serde::{Deserialize, Serialize}; use std::fmt; use std::io; @@ -15,12 +17,8 @@ use tokio_core::net::TcpStream; use tokio_proto::BindClient as ProtoBindClient; use tokio_proto::multiplex::Multiplex; use tokio_service::Service; - -#[cfg(feature = "tls")] -use self::tls::*; #[cfg(feature = "tls")] -use tokio_tls::{TlsStream}; - +use tokio_tls::TlsStream; type WireResponse = Result>, DeserializeError>; type BindClient = >> as ProtoBindClient>::BindClient; @@ -53,9 +51,7 @@ pub mod tls { impl Config { /// Construct a new `Config` pub fn new_tls(tls_client_cx: TlsClientContext) -> Self { - Config { - tls_client_cx: Some(tls_client_cx), - } + Config { tls_client_cx: Some(tls_client_cx) } } } } @@ -67,7 +63,7 @@ pub mod tls { pub struct Client where Req: Serialize + 'static, Resp: Deserialize + 'static, - E: Deserialize + 'static, + E: Deserialize + 'static { inner: BindClient, } @@ -120,12 +116,12 @@ impl io::Write for Either { } } -impl Io for Either { } +impl Io for Either {} impl Clone for Client where Req: Serialize + 'static, Resp: Deserialize + 'static, - E: Deserialize + 'static, + E: Deserialize + 'static { fn clone(&self) -> Self { Client { inner: self.inner.clone() } @@ -135,7 +131,7 @@ impl Clone for Client impl Service for Client where Req: Serialize + Sync + Send + 'static, Resp: Deserialize + Sync + Send + 'static, - E: Deserialize + Sync + Send + 'static, + E: Deserialize + Sync + Send + 'static { type Request = Req; type Response = Result>; @@ -151,12 +147,12 @@ impl Service for Client impl Client where Req: Serialize + 'static, Resp: Deserialize + 'static, - E: Deserialize + 'static, + E: Deserialize + 'static { fn new(inner: BindClient) -> Self where Req: Serialize + Sync + Send + 'static, Resp: Deserialize + Sync + Send + 'static, - E: Deserialize + Sync + Send + 'static, + E: Deserialize + Sync + Send + 'static { Client { inner: inner } } @@ -171,7 +167,7 @@ impl Client impl fmt::Debug for Client where Req: Serialize + 'static, Resp: Deserialize + 'static, - E: Deserialize + 'static, + E: Deserialize + 'static { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "Client {{ .. }}") @@ -190,9 +186,7 @@ pub struct Config { impl Config { /// Construct a new `Config` pub fn new_tcp() -> Self { - Config { - tls_client_cx: None, - } + Config { tls_client_cx: None } } } @@ -200,12 +194,14 @@ impl Config { impl Config { /// Construct a new `Config` pub fn new_tcp() -> Self { - Config { } + Config {} } } /// Exposes a trait for connecting asynchronously to servers. pub mod future { + #[cfg(feature = "tls")] + use errors::native_to_io; use future::REMOTE; use futures::{self, Async, Future, future}; use protocol::Proto; @@ -214,16 +210,14 @@ pub mod future { use std::marker::PhantomData; use std::net::SocketAddr; use super::{Client, Config, Either}; + #[cfg(feature = "tls")] + use super::tls::TlsClientContext; use tokio_core::net::TcpStream; + use tokio_core::net::TcpStreamNew; use tokio_core::reactor; use tokio_proto::BindClient; - use tokio_core::net::TcpStreamNew; - #[cfg(feature = "tls")] - use super::tls::TlsClientContext; #[cfg(feature = "tls")] use tokio_tls::{ConnectAsync, TlsStream, TlsConnectorExt}; - #[cfg(feature = "tls")] - use errors::native2io; /// Types that can connect to a server asynchronously. pub trait Connect<'a>: Sized { @@ -263,18 +257,16 @@ pub mod future { /// Provides the connection Fn impl for Tls pub struct ConnectFn { #[cfg(feature = "tls")] - tls_ctx: Option + tls_ctx: Option, } impl FnOnce<(TcpStream,)> for ConnectFn { #[cfg(feature = "tls")] - type Output = future::Either< - future::FutureResult, - futures::Map< - futures::MapErr< - ConnectAsync, - fn(::native_tls::Error) -> io::Error>, - fn(TlsStream) -> Either>>; + type Output = future::Either, + futures::Map, + fn(::native_tls::Error) + -> io::Error>, + fn(TlsStream) -> Either>>; #[cfg(not(feature = "tls"))] type Output = future::FutureResult; @@ -282,10 +274,12 @@ pub mod future { #[cfg(feature = "tls")] match self.tls_ctx { None => future::Either::A(future::ok(Either::from(tcp))), - Some(tls_client_cx) => future::Either::B(tls_client_cx.tls_connector - .connect_async(&tls_client_cx.domain, tcp) - .map_err(native2io as fn(_) -> _) - .map(Either::from as fn(_) -> _)), + Some(tls_client_cx) => { + future::Either::B(tls_client_cx.tls_connector + .connect_async(&tls_client_cx.domain, tcp) + .map_err(native_to_io as fn(_) -> _) + .map(Either::from as fn(_) -> _)) + } } #[cfg(not(feature = "tls"))] future::ok(Either::from(tcp)) @@ -304,15 +298,12 @@ pub mod future { E: Deserialize + Sync + Send + 'static { type ConnectFut = ConnectFuture; - type ConnectWithFut = ConnectWithFuture<'a, - Req, - Resp, - E, - ConnectFut>; + type ConnectWithFut = ConnectWithFuture<'a, Req, Resp, E, ConnectFut>; fn connect_remotely(addr: &SocketAddr, remote: &reactor::Remote, - _config: Config) -> Self::ConnectFut { + _config: Config) + -> Self::ConnectFut { let addr = *addr; let (tx, rx) = futures::oneshot(); remote.spawn(move |handle| { @@ -325,7 +316,7 @@ pub mod future { future::Either::A(tls_client_cx.tls_connector .connect_async(&tls_client_cx.domain, socket) .map(Either::Tls) - .map_err(native2io)) + .map_err(native_to_io)) } None => future::Either::B(future::ok(Either::Tcp(socket))), } @@ -348,16 +339,14 @@ pub mod future { #[cfg(feature = "tls")] return ConnectWithFuture { inner: TcpStream::connect(addr, handle) - .and_then(ConnectFn { - tls_ctx: _config.tls_client_cx, - }) - .map(MultiplexConnect::new(handle)) + .and_then(ConnectFn { tls_ctx: _config.tls_client_cx }) + .map(MultiplexConnect::new(handle)), }; #[cfg(not(feature = "tls"))] return ConnectWithFuture { inner: TcpStream::connect(addr, handle) .and_then(ConnectFn {}) - .map(MultiplexConnect::new(handle)) + .map(MultiplexConnect::new(handle)), }; } } @@ -366,7 +355,7 @@ pub mod future { pub struct ConnectFuture where Req: Serialize + 'static, Resp: Deserialize + 'static, - E: Deserialize + 'static, + E: Deserialize + 'static { inner: futures::Oneshot>>, } @@ -374,7 +363,7 @@ pub mod future { impl Future for ConnectFuture where Req: Serialize + 'static, Resp: Deserialize + 'static, - E: Deserialize + 'static, + E: Deserialize + 'static { type Item = Client; type Error = io::Error; @@ -416,7 +405,7 @@ pub mod future { where Req: Serialize + Sync + Send + 'static, Resp: Deserialize + Sync + Send + 'static, E: Deserialize + Sync + Send + 'static, - I: Into, + I: Into { type Output = Client; diff --git a/src/errors.rs b/src/errors.rs index cba83252..81ca0857 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -110,6 +110,6 @@ impl SerializableError f #[cfg(feature = "tls")] /// Convert `native_tls::Error` to `std::io::Error` -pub fn native2io(e: ::native_tls::Error) -> io::Error { +pub fn native_to_io(e: ::native_tls::Error) -> io::Error { io::Error::new(io::ErrorKind::Other, e) } diff --git a/src/server.rs b/src/server.rs index cda1a201..79315a0c 100644 --- a/src/server.rs +++ b/src/server.rs @@ -23,7 +23,7 @@ cfg_if! { use Tls; use native_tls::TlsAcceptor; use tokio_tls::TlsAcceptorExt; - use errors::native2io; + use errors::native_to_io; } else {} } @@ -111,7 +111,9 @@ impl Listen for Config { let handle2 = handle.clone(); let tls_acceptor = self.tls_acceptor.expect("TlsAcceptor required for Tls server"); let server = listener.incoming() - .and_then(move |(socket, _)| tls_acceptor.accept_async(socket).map_err(native2io)) + .and_then(move |(socket, _)| { + tls_acceptor.accept_async(socket).map_err(native_to_io) + }) .for_each(move |socket| { Proto::new().bind_server(&handle2, socket, new_service.new_service()?); Ok(()) @@ -144,9 +146,7 @@ impl Default for Config { #[cfg(not(feature = "tls"))] impl Default for Config { fn default() -> Self { - Config { - _client_stream: PhantomData, - } + Config { _client_stream: PhantomData } } } From 9a21c2513e033e35d7f48de71d3e9f38f65106ac Mon Sep 17 00:00:00 2001 From: Christopher Brickley Date: Wed, 11 Jan 2017 20:25:29 -0500 Subject: [PATCH 07/27] docs: update examples --- README.md | 19 +++++++++++-------- src/lib.rs | 7 ++++--- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 14c6d5dd..283d926c 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ service! { struct HelloServer; impl SyncService for HelloServer { - fn hello(&mut self, name: String) -> Result { + fn hello(&self, name: String) -> Result { Ok(format!("Hello, {}!", name)) } } @@ -113,7 +113,7 @@ struct HelloServer; impl FutureService for HelloServer { type HelloFut = futures::Finished; - fn hello(&mut self, name: String) -> Self::HelloFut { + fn hello(&self, name: String) -> Self::HelloFut { futures::finished(format!("Hello, {}!", name)) } } @@ -142,6 +142,7 @@ the [native-tls](https://github.com/sfackler/rust-native-tls) crate which is exposed by `tarpc`. ```rust +// required by `FutureClient` (not used in this example) #![feature(conservative_impl_trait, plugin)] #![plugin(tarpc_plugins)] @@ -152,6 +153,7 @@ use tarpc::sync::Connect; use tarpc::util::Never; use tarpc::TlsClientContext; use tarpc::native_tls::{Pkcs12, TlsAcceptor, TlsConnector}; +use tarpc::{ClientConfig, ServerConfig}; service! { rpc hello(name: String) -> String; @@ -161,7 +163,7 @@ service! { struct HelloServer; impl SyncService for HelloServer { - fn hello(&mut self, name: String) -> Result { + fn hello(&self, name: String) -> Result { Ok(format!("Hello, {}!", name)) } } @@ -171,15 +173,14 @@ fn tls_context() -> (TlsAcceptor, TlsClientContext) { let pkcs12 = Pkcs12::from_der(buf, "password").unwrap(); let acceptor = TlsAcceptor::builder(pkcs12).unwrap().build().unwrap(); let client_cx = TlsClientContext::try_new("foobar.com").unwrap(); - (acceptor, client_cx) } fn main() { let addr = "localhost:10000"; let (acceptor, client_cx) = tls_context(); - let _server = HelloServer.listen(addr, acceptor); - let mut client = SyncClient::connect(addr, client_cx).unwrap(); + let _server = HelloServer.listen(addr, ServerConfig::new_tls(acceptor)); + let mut client = SyncClient::connect(addr, ClientConfig::new_tls(client_cx)).unwrap(); println!("{}", client.hello("Mom".to_string()).unwrap()); } ``` @@ -198,9 +199,11 @@ extern crate tarpc; extern crate tokio_core; use futures::Future; +use tarpc::{ClientConfig, ServerConfig}; use tarpc::future::Connect; use tarpc::util::{FirstSocketAddr, Never}; use tokio_core::reactor; +use tarpc::{ClientConfig, ServerConfig}; service! { rpc hello(name: String) -> String; @@ -230,9 +233,9 @@ fn main() { let addr = "localhost:10000".first_socket_addr(); let mut core = reactor::Core::new().unwrap(); let (acceptor, client_cx) = tls_context(); - HelloServer.listen_with(addr, core.handle(), acceptor).unwrap(); + HelloServer.listen_with(addr, core.handle(), ServerConfig::new_tls(acceptor)).unwrap(); core.run( - FutureClient::connect(&addr, client_cx) + FutureClient::connect(&addr, ClientConfig::new_tls(client_cx)) .map_err(tarpc::Error::from) .and_then(|mut client| client.hello("Mom".to_string())) .map(|resp| println!("{}", resp)) diff --git a/src/lib.rs b/src/lib.rs index a1502982..da1f77e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,7 +62,7 @@ //! //! Example usage with TLS: //! -#![cfg_attr(feature = "tls", doc = " ```ignore,rust,no_run")] +#![cfg_attr(feature = "tls", doc = " ```rust,no_run")] #![cfg_attr(not(feature = "tls"), doc = " ```ignore")] //! // required by `FutureClient` (not used in this example) //! #![feature(conservative_impl_trait, plugin)] @@ -75,6 +75,7 @@ //! use tarpc::util::Never; //! use tarpc::TlsClientContext; //! use tarpc::native_tls::{Pkcs12, TlsAcceptor, TlsConnector}; +//! use tarpc::{ClientConfig, ServerConfig}; //! //! service! { //! rpc hello(name: String) -> String; @@ -100,8 +101,8 @@ //! fn main() { //! let addr = "localhost:10000"; //! let (acceptor, client_cx) = tls_context(); -//! let _server = HelloServer.listen(addr, acceptor); -//! let mut client = SyncClient::connect(addr, client_cx).unwrap(); +//! let _server = HelloServer.listen(addr, ServerConfig::new_tls(acceptor)); +//! let mut client = SyncClient::connect(addr, ClientConfig::new_tls(client_cx)).unwrap(); //! println!("{}", client.hello("Mom".to_string()).unwrap()); //! } //! ``` From f29621966ef37dafa0b531b918bb5cdffdfeebca Mon Sep 17 00:00:00 2001 From: Christopher Brickley Date: Thu, 12 Jan 2017 15:42:55 -0500 Subject: [PATCH 08/27] test: add tls and tcp at the same time test case --- src/macros.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/macros.rs b/src/macros.rs index 58a47811..46be3e14 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1070,6 +1070,26 @@ mod functional_test { bad => panic!(r#"Expected Error::ServerDeserialize but got "{}""#, bad), } } + + #[cfg(feature = "tls")] + #[test] + fn tcp_and_tls() { + use util::FirstSocketAddr; + use client::future::Connect; + use super::FutureServiceExt; + + let _ = env_logger::init(); + let (_, client) = start_server_with_async_client::(Server); + assert_eq!(3, client.add(1, 2).wait().unwrap()); + assert_eq!("Hey, Tim.", client.hey("Tim".to_string()).wait().unwrap()); + + let addr = Server.listen("localhost:0".first_socket_addr(), ::ServerConfig::new_tcp()) + .wait() + .unwrap(); + let client = FutureClient::connect(&addr, ::ClientConfig::new_tcp()).wait().unwrap(); + assert_eq!(3, client.add(1, 2).wait().unwrap()); + assert_eq!("Hey, Tim.", client.hey("Tim".to_string()).wait().unwrap()); + } } pub mod error_service { From ce132a6ba7f6880e6f54362fc58566c00a99ace4 Mon Sep 17 00:00:00 2001 From: Christopher Brickley Date: Thu, 19 Jan 2017 15:58:24 -0500 Subject: [PATCH 09/27] chore: cleanup --- .travis.yml | 1 - Cargo.toml | 4 +- README.md | 183 ++++++++++++++++++++------------------------------ src/client.rs | 141 +++++++++++++++++++------------------- src/errors.rs | 2 +- src/macros.rs | 7 -- src/server.rs | 159 +------------------------------------------ 7 files changed, 149 insertions(+), 348 deletions(-) diff --git a/.travis.yml b/.travis.yml index 10f8c56f..56d8636e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,6 @@ addons: - libcurl4-openssl-dev - libelf-dev - libdw-dev - - libssl-dev before_script: - | diff --git a/Cargo.toml b/Cargo.toml index 41afcdae..37f546c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,6 @@ futures = "0.1.7" lazy_static = "0.2" log = "0.3" native-tls = { version = "0.1.1", optional = true } -net2 = "0.2" scoped-pool = "1.0" serde = "0.8" serde_derive = "0.8" @@ -29,12 +28,13 @@ tokio-service = "0.1" tokio-proto = "0.1" tokio-core = "0.1" tokio-tls = { version = "0.1", optional = true } +net2 = "0.2" [dev-dependencies] chrono = "0.2" -clap = "2.0" env_logger = "0.3" futures-cpupool = "0.1" +clap = "2.0" [target.'cfg(target_os = "macos")'.dev-dependencies] security-framework = "0.1" diff --git a/README.md b/README.md index 3c78e21f..5abd9011 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,78 @@ fn main() { } ``` +## Example: Futures + TLS + +By default, tarpc uses a `TcpStream` for communication, however you can also opt to use a +`TlsStream` when the `tls` feature of `tarpc` is enabled. + +When using TLS, some additional information is required. You will need to make the `TlsAcceptor` and +`TlsClientContext` structs. The [`TlsAcceptor`] and [`TlsConnector`] (`TlsConnector` is required in +`TlsClientContext`) structs are defined in the [native-tls] crate which is exposed by `tarpc`. + +[`TlsAcceptor`]: https://docs.rs/native-tls/0.1/native_tls/struct.TlsAcceptor.html +[`TlsConnector`]: https://docs.rs/native-tls/0.1/native_tls/struct.TlsConnector.html +[native-tls]: https://github.com/sfackler/rust-native-tls + +Enabling the `tls` feature does not require you to use TLS streams. You can still +use TCP streams as well or a combination of both stream types. + +```rust +#![feature(conservative_impl_trait, plugin)] +#![plugin(tarpc_plugins)] + +extern crate futures; +#[macro_use] +extern crate tarpc; +extern crate tokio_core; + +use futures::Future; +use tarpc::{client, server, TlsClientContext}; +use tarpc::client::future::Connect; +use tarpc::util::{FirstSocketAddr, Never}; +use tokio_core::reactor; +use tarpc::native_tls::{Pkcs12, TlsAcceptor}; + +service! { + rpc hello(name: String) -> String; +} + +#[derive(Clone)] +struct HelloServer; + +impl FutureService for HelloServer { + type HelloFut = futures::Finished; + + fn hello(&mut self, name: String) -> Self::HelloFut { + futures::finished(format!("Hello, {}!", name)) + } +} + +fn tls_context() -> (TlsAcceptor, TlsClientContext) { + let buf = include_bytes!("test/identity.p12"); + let pkcs12 = Pkcs12::from_der(buf, "password").unwrap(); + let acceptor = TlsAcceptor::builder(pkcs12).unwrap().build().unwrap(); + let client_cx = TlsClientContext::try_new("foobar.com").unwrap(); + + (acceptor, client_cx) +} + +fn main() { + let addr = "localhost:10000".first_socket_addr(); + let mut core = reactor::Core::new().unwrap(); + let (acceptor, client_cx) = tls_context(); + HelloServer.listen(addr, server::Options::default() + .handle(core.handle()) + .tls(acceptor)).wait().unwrap(); + let options = client::Options::default().handle(core.handle().tls(client_cx)); + core.run(FutureClient::connect(addr, options) + .map_err(tarpc::Error::from) + .and_then(|client| client.hello("Mom".to_string())) + .map(|resp| println!("{}", resp))) + .unwrap(); +} +``` + ## Tips ### Sync vs Futures @@ -187,117 +259,6 @@ impl FutureService for HelloServer { } ``` -## Example: TLS + Sync - -Instead of using a `TcpStream`, a `TlsStream` will be used instead -when the `tls` feature of `tarpc` is enabled. When using TLS, some additional -information is required from the user. You will need to be make the -`TlsAcceptor` and `TlsClientContext` structs. The `TlsAcceptor` and -`TlsConnector` (which is required in `TlsClientContext`) structs are defined in -the [native-tls](https://github.com/sfackler/rust-native-tls) crate which is -exposed by `tarpc`. - -```rust -// required by `FutureClient` (not used in this example) -#![feature(conservative_impl_trait, plugin)] -#![plugin(tarpc_plugins)] - -#[macro_use] -extern crate tarpc; - -use tarpc::sync::Connect; -use tarpc::util::Never; -use tarpc::TlsClientContext; -use tarpc::native_tls::{Pkcs12, TlsAcceptor, TlsConnector}; -use tarpc::{ClientConfig, ServerConfig}; - -service! { - rpc hello(name: String) -> String; -} - -#[derive(Clone)] -struct HelloServer; - -impl SyncService for HelloServer { - fn hello(&self, name: String) -> Result { - Ok(format!("Hello, {}!", name)) - } -} - -fn tls_context() -> (TlsAcceptor, TlsClientContext) { - let buf = include_bytes!("test/identity.p12"); - let pkcs12 = Pkcs12::from_der(buf, "password").unwrap(); - let acceptor = TlsAcceptor::builder(pkcs12).unwrap().build().unwrap(); - let client_cx = TlsClientContext::try_new("foobar.com").unwrap(); - (acceptor, client_cx) -} - -fn main() { - let addr = "localhost:10000"; - let (acceptor, client_cx) = tls_context(); - let _server = HelloServer.listen(addr, ServerConfig::new_tls(acceptor)); - let mut client = SyncClient::connect(addr, ClientConfig::new_tls(client_cx)).unwrap(); - println!("{}", client.hello("Mom".to_string()).unwrap()); -} -``` - -## Example: Futures + TLS - -Here's the same TLS service, implemented using futures. - -```rust -#![feature(conservative_impl_trait, plugin)] -#![plugin(tarpc_plugins)] - -extern crate futures; -#[macro_use] -extern crate tarpc; -extern crate tokio_core; - -use futures::Future; -use tarpc::{ClientConfig, ServerConfig}; -use tarpc::future::Connect; -use tarpc::util::{FirstSocketAddr, Never}; -use tokio_core::reactor; -use tarpc::{ClientConfig, ServerConfig}; - -service! { - rpc hello(name: String) -> String; -} - -#[derive(Clone)] -struct HelloServer; - -impl FutureService for HelloServer { - type HelloFut = futures::Finished; - - fn hello(&mut self, name: String) -> Self::HelloFut { - futures::finished(format!("Hello, {}!", name)) - } -} - -fn tls_context() -> (TlsAcceptor, TlsClientContext) { - let buf = include_bytes!("test/identity.p12"); - let pkcs12 = Pkcs12::from_der(buf, "password").unwrap(); - let acceptor = TlsAcceptor::builder(pkcs12).unwrap().build().unwrap(); - let client_cx = TlsClientContext::try_new("foobar.com").unwrap(); - - (acceptor, client_cx) -} - -fn main() { - let addr = "localhost:10000".first_socket_addr(); - let mut core = reactor::Core::new().unwrap(); - let (acceptor, client_cx) = tls_context(); - HelloServer.listen_with(addr, core.handle(), ServerConfig::new_tls(acceptor)).unwrap(); - core.run( - FutureClient::connect(&addr, ClientConfig::new_tls(client_cx)) - .map_err(tarpc::Error::from) - .and_then(|mut client| client.hello("Mom".to_string())) - .map(|resp| println!("{}", resp)) - ).unwrap(); -} -``` ## Documentation diff --git a/src/client.rs b/src/client.rs index 3657788a..82843011 100644 --- a/src/client.rs +++ b/src/client.rs @@ -123,7 +123,6 @@ impl fmt::Debug for Client pub struct Options { reactor: Option, #[cfg(feature = "tls")] - /// Tls configuration tls_ctx: Option, } @@ -140,8 +139,8 @@ impl Options { self } - #[cfg(feature = "tls")] /// Connect using the given `TlsClientContext` + #[cfg(feature = "tls")] pub fn tls(mut self, tls_ctx: TlsClientContext) -> Self { self.tls_ctx = Some(tls_ctx); self @@ -178,7 +177,77 @@ pub mod future { fn connect(addr: SocketAddr, options: Options) -> Self::ConnectFut; } + /// A future that resolves to a `Client` or an `io::Error`. + #[doc(hidden)] + pub struct ConnectFuture + where Req: Serialize + 'static, + Resp: Deserialize + 'static, + E: Deserialize + 'static + { + #[cfg(not(feature = "tls"))] + inner: + future::Either< + futures::Map, + ConnectFn>, MultiplexConnect>, + futures::Flatten< + futures::MapErr< + futures::Oneshot>>, + fn(futures::Canceled) -> io::Error>>>, + #[cfg(feature = "tls")] + inner: + future::Either< + futures::Map, + futures::Map, + fn(::native_tls::Error) -> io::Error>, fn(TlsStream) -> StreamType>>, + ConnectFn>, MultiplexConnect>, + futures::Flatten< + futures::MapErr< + futures::Oneshot>>, + fn(futures::Canceled) -> io::Error>>>, + } + + impl Future for ConnectFuture + where Req: Serialize + Sync + Send + 'static, + Resp: Deserialize + Sync + Send + 'static, + E: Deserialize + Sync + Send + 'static + { + type Item = Client; + type Error = io::Error; + + fn poll(&mut self) -> futures::Poll { + // Ok to unwrap because we ensure the oneshot is always completed. + match Future::poll(&mut self.inner)? { + Async::Ready(client) => Ok(Async::Ready(client)), + Async::NotReady => Ok(Async::NotReady), + } + } + } + + struct MultiplexConnect(reactor::Handle, PhantomData<(Req, Resp, E)>); + + impl MultiplexConnect { + fn new(handle: reactor::Handle) -> Self { + MultiplexConnect(handle, PhantomData) + } + } + + impl FnOnce<(I,)> for MultiplexConnect + where Req: Serialize + Sync + Send + 'static, + Resp: Deserialize + Sync + Send + 'static, + E: Deserialize + Sync + Send + 'static, + I: Into + { + type Output = Client; + + extern "rust-call" fn call_once(self, (stream,): (I,)) -> Self::Output { + Client::new(Proto::new().bind_client(&self.0, stream.into())) + } + } + /// Provides the connection Fn impl for Tls + #[doc(hidden)] pub struct ConnectFn { #[cfg(feature = "tls")] tls_ctx: Option, @@ -283,74 +352,6 @@ pub mod future { } } - /// A future that resolves to a `Client` or an `io::Error`. - #[doc(hidden)] - pub struct ConnectFuture - where Req: Serialize + 'static, - Resp: Deserialize + 'static, - E: Deserialize + 'static - { - #[cfg(not(feature = "tls"))] - inner: - future::Either< - futures::Map, - ConnectFn>, MultiplexConnect>, - futures::Flatten< - futures::MapErr< - futures::Oneshot>>, - fn(futures::Canceled) -> io::Error>>>, - #[cfg(feature = "tls")] - inner: - future::Either< - futures::Map, - futures::Map, - fn(::native_tls::Error) -> io::Error>, fn(TlsStream) -> StreamType>>, - ConnectFn>, MultiplexConnect>, - futures::Flatten< - futures::MapErr< - futures::Oneshot>>, - fn(futures::Canceled) -> io::Error>>>, - } - - impl Future for ConnectFuture - where Req: Serialize + Sync + Send + 'static, - Resp: Deserialize + Sync + Send + 'static, - E: Deserialize + Sync + Send + 'static - { - type Item = Client; - type Error = io::Error; - - fn poll(&mut self) -> futures::Poll { - // Ok to unwrap because we ensure the oneshot is always completed. - match Future::poll(&mut self.inner)? { - Async::Ready(client) => Ok(Async::Ready(client)), - Async::NotReady => Ok(Async::NotReady), - } - } - } - - struct MultiplexConnect(reactor::Handle, PhantomData<(Req, Resp, E)>); - - impl MultiplexConnect { - fn new(handle: reactor::Handle) -> Self { - MultiplexConnect(handle, PhantomData) - } - } - - impl FnOnce<(I,)> for MultiplexConnect - where Req: Serialize + Sync + Send + 'static, - Resp: Deserialize + Sync + Send + 'static, - E: Deserialize + Sync + Send + 'static, - I: Into - { - type Output = Client; - - extern "rust-call" fn call_once(self, (stream,): (I,)) -> Self::Output { - Client::new(Proto::new().bind_client(&self.0, stream.into())) - } - } } /// Exposes a trait for connecting synchronously to servers. diff --git a/src/errors.rs b/src/errors.rs index 5458551d..3bb4189e 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -103,8 +103,8 @@ pub enum WireError { App(E), } -#[cfg(feature = "tls")] /// Convert `native_tls::Error` to `std::io::Error` +#[cfg(feature = "tls")] pub fn native_to_io(e: ::native_tls::Error) -> io::Error { io::Error::new(io::ErrorKind::Other, e) } diff --git a/src/macros.rs b/src/macros.rs index 437a8f0e..2d36418c 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -824,9 +824,7 @@ mod functional_test { { let addr = t!(server.listen("localhost:0".first_socket_addr(), get_server_options())); - println!("server listening"); let client = C::connect(addr, get_client_options()); - println!("client connect"); (addr, client) } @@ -869,13 +867,10 @@ mod functional_test { #[test] fn simple() { - println!("starting..."); let _ = env_logger::init(); let (_, client) = start_server_with_sync_client::(Server); - println!("got client.."); let client = t!(client); assert_eq!(3, client.add(1, 2).unwrap()); - println!("added"); assert_eq!("Hey, Tim.", client.hey("Tim".to_string()).unwrap()); } @@ -994,7 +989,6 @@ mod functional_test { use self::error_service::*; let _ = env_logger::init(); - println!("get async"); let (addr, client) = start_err_server_with_async_client::(ErrorServer); client.bar() @@ -1010,7 +1004,6 @@ mod functional_test { .wait() .unwrap(); - println!("get sync"); let client = get_sync_client::(addr).unwrap(); match client.bar().err().unwrap() { ::Error::App(e) => { diff --git a/src/server.rs b/src/server.rs index c1044dd3..535300ef 100644 --- a/src/server.rs +++ b/src/server.rs @@ -54,8 +54,8 @@ impl Options { self } - #[cfg(feature = "tls")] /// Set the `TlsAcceptor` + #[cfg(feature = "tls")] pub fn tls(mut self, tls_acceptor: TlsAcceptor) -> Self { self.tls_acceptor = Some(tls_acceptor); self @@ -66,107 +66,6 @@ impl Options { #[doc(hidden)] pub type Response = Result>; -// <<<<<<< HEAD -// /// Enables service spawning -// pub trait Listen: Sized + Send + 'static { -// /// Spawns a service that binds to the given address and runs on the default reactor core. -// fn listen(self, addr: SocketAddr, new_service: S) -> ListenFuture -// where S: NewService, -// Response = Response, -// Error = io::Error> + Send + 'static, -// Req: Deserialize + 'static, -// Resp: Serialize + 'static, -// E: Serialize + 'static -// { -// let (tx, rx) = futures::oneshot(); -// REMOTE.spawn(move |handle| { -// Ok(tx.complete(self.listen_with(addr, new_service, handle.clone()))) -// }); -// ListenFuture { inner: rx } -// } -// -// /// Spawns a service that binds to the given address using the given handle. -// fn listen_with(self, -// addr: SocketAddr, -// new_service: S, -// handle: Handle) -// -> io::Result -// where S: NewService, -// Response = Response, -// Error = io::Error> + Send + 'static, -// Req: Deserialize + 'static, -// Resp: Serialize + 'static, -// E: Serialize + 'static; -// } -// -// impl Listen for Config { -// fn listen_with(self, -// addr: SocketAddr, -// new_service: S, -// handle: Handle) -// -> io::Result -// where S: NewService, -// Response = Response, -// Error = io::Error> + Send + 'static, -// Req: Deserialize + 'static, -// Resp: Serialize + 'static, -// E: Serialize + 'static -// { -// let listener = listener(&addr, &handle)?; -// let addr = listener.local_addr()?; -// -// let handle2 = handle.clone(); -// let server = listener.incoming() -// .for_each(move |(socket, _)| { -// Proto::new().bind_server(&handle2, socket, new_service.new_service()?); -// -// Ok(()) -// }) -// .map_err(|e| error!("While processing incoming connections: {}", e)); -// handle.spawn(server); -// Ok(addr) -// } -// } -// -// #[cfg(feature = "tls")] -// impl Listen for Config { -// fn listen_with(self, -// addr: SocketAddr, -// new_service: S, -// handle: Handle) -// -> io::Result -// where S: NewService, -// Response = Response, -// Error = io::Error> + Send + 'static, -// Req: Deserialize + 'static, -// Resp: Serialize + 'static, -// E: Serialize + 'static -// { -// let listener = listener(&addr, &handle)?; -// let addr = listener.local_addr()?; -// -// let handle2 = handle.clone(); -// let tls_acceptor = self.tls_acceptor.expect("TlsAcceptor required for Tls server"); -// let server = listener.incoming() -// .and_then(move |(socket, _)| { -// tls_acceptor.accept_async(socket).map_err(native_to_io) -// }) -// .for_each(move |socket| { -// Proto::new().bind_server(&handle2, socket, new_service.new_service()?); -// Ok(()) -// }) -// .map_err(|e| error!("While processing incoming connections: {}", e)); -// -// -// handle.spawn(server); -// Ok(addr) -// } -// } - - - -// TODO: ADD TLS STUFF - #[doc(hidden)] pub fn listen(new_service: S, addr: SocketAddr, options: Options) -> ListenFuture where S: NewService, @@ -176,6 +75,8 @@ pub fn listen(new_service: S, addr: SocketAddr, options: Option Resp: Serialize + 'static, E: Serialize + 'static { + // similar to the client, since `Options` is not `Send`, we take the `TlsAcceptor` when it is + // available. #[cfg(feature = "tls")] let acceptor = match options.tls_acceptor { Some(tls_acceptor) => Acceptor::Tls(tls_acceptor), @@ -285,57 +186,3 @@ impl Future for ListenFuture { } } } - -// /// TODO: -// pub struct Config { -// #[cfg(feature = "tls")] -// tls_acceptor: Option, -// _client_stream: PhantomData, -// } -// -// #[cfg(feature = "tls")] -// impl Default for Config { -// fn default() -> Self { -// Config { -// tls_acceptor: None, -// _client_stream: PhantomData, -// } -// } -// } -// -// #[cfg(not(feature = "tls"))] -// impl Default for Config { -// fn default() -> Self { -// Config { _client_stream: PhantomData } -// } -// } -// -// #[cfg(feature = "tls")] -// impl Config { -// /// TODO -// pub fn new_tcp() -> Self { -// Config { -// _client_stream: PhantomData, -// tls_acceptor: None, -// } -// } -// } -// -// #[cfg(not(feature = "tls"))] -// impl Config { -// /// TODO -// pub fn new_tcp() -> Self { -// Config { _client_stream: PhantomData } -// } -// } -// -// #[cfg(feature = "tls")] -// impl Config { -// /// TODO -// pub fn new_tls(tls_acceptor: TlsAcceptor) -> Self { -// Config { -// _client_stream: PhantomData, -// tls_acceptor: Some(tls_acceptor), -// } -// } -// } From a26805ac7b2c519f25978448b2288ca558f32ee4 Mon Sep 17 00:00:00 2001 From: Christopher Brickley Date: Thu, 19 Jan 2017 16:17:21 -0500 Subject: [PATCH 10/27] chore: update travis-cargo command --- .travis.yml | 2 +- README.md | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 56d8636e..ad81c00b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,7 +22,7 @@ before_script: script: - | travis-cargo build && travis-cargo test && - travis-cargo build --features tls && travis-cargo test --features tls + travis-cargo build -- --features tls && travis-cargo test -- --features tls after_success: - travis-cargo coveralls --no-sudo diff --git a/README.md b/README.md index 5abd9011..e5b9a809 100644 --- a/README.md +++ b/README.md @@ -259,7 +259,6 @@ impl FutureService for HelloServer { } ``` - ## Documentation Use `cargo doc` as you normally would to see the documentation created for all From 87a21ee60108f5960593132a92a8f8c171a5ef8f Mon Sep 17 00:00:00 2001 From: Christopher Brickley Date: Thu, 19 Jan 2017 16:35:28 -0500 Subject: [PATCH 11/27] docs(README): formatting for TLS section --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e5b9a809..81ee8889 100644 --- a/README.md +++ b/README.md @@ -138,9 +138,9 @@ fn main() { By default, tarpc uses a `TcpStream` for communication, however you can also opt to use a `TlsStream` when the `tls` feature of `tarpc` is enabled. -When using TLS, some additional information is required. You will need to make the `TlsAcceptor` and -`TlsClientContext` structs. The [`TlsAcceptor`] and [`TlsConnector`] (`TlsConnector` is required in -`TlsClientContext`) structs are defined in the [native-tls] crate which is exposed by `tarpc`. +When using TLS, some additional information is required. You will need to make [`TlsAcceptor`] and +`TlsClientContext` structs; `TlsClientContext` requires a [`TlsConnector`]. The [`TlsAcceptor`] and +[`TlsConnector`] structs are defined in the [native-tls] crate which is exposed by `tarpc`. [`TlsAcceptor`]: https://docs.rs/native-tls/0.1/native_tls/struct.TlsAcceptor.html [`TlsConnector`]: https://docs.rs/native-tls/0.1/native_tls/struct.TlsConnector.html From d7c2c33d7a2b578f4ea92e1ed9fbb5eddbbb86f6 Mon Sep 17 00:00:00 2001 From: Christopher Brickley Date: Thu, 19 Jan 2017 16:40:13 -0500 Subject: [PATCH 12/27] chore: rm unneeded comment --- src/macros.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/macros.rs b/src/macros.rs index 2d36418c..27f0421c 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -818,7 +818,6 @@ mod functional_test { C::connect(addr, get_client_options()) } - /// start server and return `SyncClient` fn start_server_with_sync_client(server: S) -> (SocketAddr, io::Result) where C: client::sync::Connect, S: SyncServiceExt { From 65c68be36724fccb96b994dd6d6d8e228a6b7204 Mon Sep 17 00:00:00 2001 From: Christopher Brickley Date: Thu, 19 Jan 2017 17:02:21 -0500 Subject: [PATCH 13/27] chore: correct client visibility --- src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0e9f082b..f49a9f86 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -154,7 +154,6 @@ pub mod util; #[macro_use] mod macros; /// Provides the base client stubs used by the service macro. -#[doc(hidden)] pub mod client; /// Provides the base server boilerplate used by service implementations. pub mod server; @@ -163,7 +162,7 @@ pub mod server; mod protocol; /// Provides a few different error types. mod errors; -/// Provides an abstraction over both Tls and Tcp streams. +/// Provides an abstraction over TLS and TCP streams. mod stream_type; use std::sync::mpsc; From c5a5dd600e5cc8a46fd4a4b679d8dd6aac044c7d Mon Sep 17 00:00:00 2001 From: Christopher Brickley Date: Thu, 19 Jan 2017 17:17:54 -0500 Subject: [PATCH 14/27] docs(client): add comment for `mod tls` --- src/client.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/client.rs b/src/client.rs index 82843011..00d05aef 100644 --- a/src/client.rs +++ b/src/client.rs @@ -24,6 +24,7 @@ type ResponseFuture = futures::Map< as Se type BindClient = >> as ProtoBindClient>::BindClient; +/// TLS-specific functionality #[cfg(feature = "tls")] pub mod tls { use native_tls::TlsConnector; From 16625f529299634cb87c06182ebb3ca25e0c3dab Mon Sep 17 00:00:00 2001 From: Christopher Brickley Date: Mon, 23 Jan 2017 17:08:27 -0500 Subject: [PATCH 15/27] refactor: address review comments --- README.md | 39 ++++++++++++++++------------ src/client.rs | 72 +++++++++++++++++++++++---------------------------- src/lib.rs | 25 +++++++++--------- src/macros.rs | 43 +++++++++++++++--------------- src/server.rs | 6 ++--- 5 files changed, 91 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index 81ee8889..0b71bfd8 100644 --- a/README.md +++ b/README.md @@ -135,19 +135,28 @@ fn main() { ## Example: Futures + TLS -By default, tarpc uses a `TcpStream` for communication, however you can also opt to use a -`TlsStream` when the `tls` feature of `tarpc` is enabled. +By default, tarpc internally uses a [`TcpStream`] for communication between your clients and +servers. However, TCP by itself has no encryption. As a result, your comunication will be sent in +the clear. If you want your RPC communications to be encrypted, you can choose to use [TLS]. TLS +operates as an encryption layer on top of TCP. When using TLS, your communication will occur over a +[`TlsStream`]. You can add the ability to make TLS clients and servers by adding `tarpc` +with the `tls` feature flag enabled. When using TLS, some additional information is required. You will need to make [`TlsAcceptor`] and `TlsClientContext` structs; `TlsClientContext` requires a [`TlsConnector`]. The [`TlsAcceptor`] and -[`TlsConnector`] structs are defined in the [native-tls] crate which is exposed by `tarpc`. +[`TlsConnector`] types are defined in the [native-tls]. `tarpc` re-exports TLS-related types in its +`tls` module (`tarpc::tls`). +[TLS]: https://en.wikipedia.org/wiki/Transport_Layer_Security +[`TcpStream`]: https://docs.rs/tokio-core/0.1/tokio_core/net/struct.TcpStream.html +[`TlsStream`]: https://docs.rs/native-tls/0.1/native_tls/struct.TlsStream.html [`TlsAcceptor`]: https://docs.rs/native-tls/0.1/native_tls/struct.TlsAcceptor.html [`TlsConnector`]: https://docs.rs/native-tls/0.1/native_tls/struct.TlsConnector.html [native-tls]: https://github.com/sfackler/rust-native-tls -Enabling the `tls` feature does not require you to use TLS streams. You can still -use TCP streams as well or a combination of both stream types. +Both TLS streams and TCP streams are supported in the same binary when the `tls` feature is enabled. +However, if you are working with both stream types, ensure that you use the TLS clients with TLS +servers and TCP clients with TCP servers. ```rust #![feature(conservative_impl_trait, plugin)] @@ -159,11 +168,11 @@ extern crate tarpc; extern crate tokio_core; use futures::Future; -use tarpc::{client, server, TlsClientContext}; +use tarpc::{client, server}; use tarpc::client::future::Connect; use tarpc::util::{FirstSocketAddr, Never}; use tokio_core::reactor; -use tarpc::native_tls::{Pkcs12, TlsAcceptor}; +use tarpc::tls::{Pkcs12, TlsAcceptor, TlsClientContext}; service! { rpc hello(name: String) -> String; @@ -180,23 +189,21 @@ impl FutureService for HelloServer { } } -fn tls_context() -> (TlsAcceptor, TlsClientContext) { - let buf = include_bytes!("test/identity.p12"); - let pkcs12 = Pkcs12::from_der(buf, "password").unwrap(); - let acceptor = TlsAcceptor::builder(pkcs12).unwrap().build().unwrap(); - let client_cx = TlsClientContext::try_new("foobar.com").unwrap(); - - (acceptor, client_cx) +fn get_acceptor() -> TlsAcceptor { + let buf = include_bytes!("test/identity.p12"); + let pkcs12 = Pkcs12::from_der(buf, "password").unwrap(); + TlsAcceptor::builder(pkcs12).unwrap().build().unwrap() } fn main() { let addr = "localhost:10000".first_socket_addr(); let mut core = reactor::Core::new().unwrap(); - let (acceptor, client_cx) = tls_context(); + let acceptor = get_acceptor(); HelloServer.listen(addr, server::Options::default() .handle(core.handle()) .tls(acceptor)).wait().unwrap(); - let options = client::Options::default().handle(core.handle().tls(client_cx)); + let options = client::Options::default().handle(core.handle() + .tls(TlsClientContext::new("foobar.com").unwrap())); core.run(FutureClient::connect(addr, options) .map_err(tarpc::Error::from) .and_then(|client| client.hello("Mom".to_string())) diff --git a/src/client.rs b/src/client.rs index 00d05aef..0c443c20 100644 --- a/src/client.rs +++ b/src/client.rs @@ -38,15 +38,23 @@ pub mod tls { } impl TlsClientContext { - /// Try to make a new `TlsClientContext`, providing the domain the client will + /// Try to construct a new `TlsClientContext`, providing the domain the client will /// connect to. - pub fn try_new>(domain: S) - -> Result { + pub fn new>(domain: S) -> Result { Ok(TlsClientContext { domain: domain.into(), tls_connector: TlsConnector::builder()?.build()?, }) } + + /// Construct a new `TlsClientContext` using the provided domain and `TlsConnector` + pub fn from_connector>(domain: S, tls_connector: TlsConnector) -> Self { + TlsClientContext { + domain: domain.into(), + tls_connector: tls_connector, + } + + } } } @@ -119,8 +127,7 @@ impl fmt::Debug for Client } /// Additional options to configure how the client connects and operates. -#[cfg_attr(feature = "tls", derive(Default))] -#[cfg_attr(not(feature = "tls"), derive(Clone, Default))] +#[derive(Default)] pub struct Options { reactor: Option, #[cfg(feature = "tls")] @@ -151,8 +158,6 @@ impl Options { /// Exposes a trait for connecting asynchronously to servers. pub mod future { use {REMOTE, Reactor}; - #[cfg(feature = "tls")] - use errors::native_to_io; use futures::{self, Async, Future, future}; use protocol::Proto; use serde::{Deserialize, Serialize}; @@ -161,13 +166,16 @@ pub mod future { use std::net::SocketAddr; use stream_type::StreamType; use super::{Client, Options}; - #[cfg(feature = "tls")] - use super::tls::TlsClientContext; use tokio_core::net::{TcpStream, TcpStreamNew}; use tokio_core::reactor; use tokio_proto::BindClient; - #[cfg(feature = "tls")] - use tokio_tls::{ConnectAsync, TlsStream, TlsConnectorExt}; + cfg_if! { + if #[cfg(feature = "tls")] { + use tokio_tls::{ConnectAsync, TlsStream, TlsConnectorExt}; + use super::tls::TlsClientContext; + use errors::native_to_io; + } else {} + } /// Types that can connect to a server asynchronously. pub trait Connect: Sized { @@ -178,6 +186,11 @@ pub mod future { fn connect(addr: SocketAddr, options: Options) -> Self::ConnectFut; } + type ConnectFutureInner = future::Either, MultiplexConnect>, futures::Flatten< + futures::MapErr>>, + fn(futures::Canceled) -> io::Error>>>; + /// A future that resolves to a `Client` or an `io::Error`. #[doc(hidden)] pub struct ConnectFuture @@ -186,27 +199,11 @@ pub mod future { E: Deserialize + 'static { #[cfg(not(feature = "tls"))] - inner: - future::Either< - futures::Map, - ConnectFn>, MultiplexConnect>, - futures::Flatten< - futures::MapErr< - futures::Oneshot>>, - fn(futures::Canceled) -> io::Error>>>, + inner: ConnectFutureInner>, #[cfg(feature = "tls")] - inner: - future::Either< - futures::Map, - futures::Map, - fn(::native_tls::Error) -> io::Error>, fn(TlsStream) -> StreamType>>, - ConnectFn>, MultiplexConnect>, - futures::Flatten< - futures::MapErr< - futures::Oneshot>>, - fn(futures::Canceled) -> io::Error>>>, + inner: ConnectFutureInner, futures::Map, + fn(::native_tls::Error) -> io::Error>, fn(TlsStream) -> StreamType>>>, } impl Future for ConnectFuture @@ -248,8 +245,7 @@ pub mod future { } /// Provides the connection Fn impl for Tls - #[doc(hidden)] - pub struct ConnectFn { + struct ConnectFn { #[cfg(feature = "tls")] tls_ctx: Option, } @@ -324,15 +320,12 @@ pub mod future { let rx = match options.reactor { Some(Reactor::Handle(handle)) => { #[cfg(feature = "tls")] - let tcp = TcpStream::connect(&addr, &handle) - .and_then(ConnectFn { tls_ctx: options.tls_ctx }) - .map(MultiplexConnect::new(handle)); - + let connect_fn = ConnectFn { tls_ctx: options.tls_ctx }; #[cfg(not(feature = "tls"))] + let connect_fn = ConnectFn {}; let tcp = TcpStream::connect(&addr, &handle) - .and_then(ConnectFn {}) + .and_then(connect_fn) .map(MultiplexConnect::new(handle)); - return ConnectFuture { inner: future::Either::A(tcp) }; } Some(Reactor::Remote(remote)) => { @@ -352,7 +345,6 @@ pub mod future { ConnectFuture { inner: future::Either::B(rx.map_err(panic as fn(_) -> _).flatten()) } } } - } /// Exposes a trait for connecting synchronously to servers. diff --git a/src/lib.rs b/src/lib.rs index f49a9f86..8fa60a0a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,8 +26,7 @@ //! //! Example usage: //! -#![cfg_attr(feature = "tls", doc = " ```ignore")] -#![cfg_attr(not(feature = "tls"), doc = " ```")] +//! ``` //! // required by `FutureClient` (not used in this example) //! #![feature(conservative_impl_trait, plugin)] //! #![plugin(tarpc_plugins)] @@ -74,8 +73,7 @@ //! use tarpc::{client, server}; //! use tarpc::client::sync::Connect; //! use tarpc::util::Never; -//! use tarpc::TlsClientContext; -//! use tarpc::native_tls::{Pkcs12, TlsAcceptor}; +//! use tarpc::tls::{TlsClientContext, TlsAcceptor, Pkcs12}; //! //! service! { //! rpc hello(name: String) -> String; @@ -90,19 +88,17 @@ //! } //! } //! -//! fn tls_context() -> (TlsAcceptor, TlsClientContext) { +//! fn get_acceptor() -> TlsAcceptor { //! let buf = include_bytes!("test/identity.p12"); //! let pkcs12 = Pkcs12::from_der(buf, "password").unwrap(); -//! let acceptor = TlsAcceptor::builder(pkcs12).unwrap().build().unwrap(); -//! let client_cx = TlsClientContext::try_new("foobar.com").unwrap(); -//! (acceptor, client_cx) +//! TlsAcceptor::builder(pkcs12).unwrap().build().unwrap() //! } //! //! fn main() { //! let addr = "localhost:10000"; -//! let (acceptor, client_cx) = tls_context(); +//! let acceptor = get_acceptor(); //! let _server = HelloServer.listen(addr, server::Options::default().tls(acceptor)); -//! let client = SyncClient::connect(addr, client::Options::default().tls(client_cx)).unwrap(); +//! let client = SyncClient::connect(addr, client::Options::default().tls(TlsClientContext::new("foobar.com").unwrap())).unwrap(); //! println!("{}", client.hello("Mom".to_string()).unwrap()); //! } //! ``` @@ -199,8 +195,13 @@ enum Reactor { cfg_if! { if #[cfg(feature = "tls")] { extern crate tokio_tls; - pub extern crate native_tls; + extern crate native_tls; - pub use client::tls::TlsClientContext; + /// Re-exported TLS-related types + pub mod tls { + pub use client::tls::TlsClientContext; + pub use native_tls::Error as NativeTlsError; + pub use native_tls::{Pkcs12, TlsAcceptor, TlsConnector}; + } } else {} } diff --git a/src/macros.rs b/src/macros.rs index 27f0421c..a67e9154 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -695,7 +695,7 @@ mod functional_test { use util::FirstSocketAddr; extern crate env_logger; - macro_rules! t { + macro_rules! unwrap { ($e:expr) => (match $e { Ok(e) => e, Err(e) => panic!("{} failed with {:?}", stringify!($e), e), @@ -711,13 +711,13 @@ mod functional_test { if #[cfg(feature = "tls")] { const DOMAIN: &'static str = "foobar.com"; - use ::TlsClientContext; + use tls::TlsClientContext; use ::native_tls::{Pkcs12, TlsAcceptor, TlsConnector}; fn tls_context() -> (server::Options, client::Options) { let buf = include_bytes!("../test/identity.p12"); - let pkcs12 = t!(Pkcs12::from_der(buf, "mypass")); - let acceptor = t!(t!(TlsAcceptor::builder(pkcs12)).build()); + let pkcs12 = unwrap!(Pkcs12::from_der(buf, "mypass")); + let acceptor = unwrap!(unwrap!(TlsAcceptor::builder(pkcs12)).build()); let server_options = server::Options::default().tls(acceptor); let client_options = get_tls_client_options(); @@ -737,27 +737,27 @@ mod functional_test { fn get_tls_client_options() -> client::Options { let buf = include_bytes!("../test/root-ca.der"); - let cert = t!(SecCertificate::from_der(buf)); - let mut connector = t!(TlsConnector::builder()); + let cert = unwrap!(SecCertificate::from_der(buf)); + let mut connector = unwrap!(TlsConnector::builder()); connector.anchor_certificates(&[cert]); client::Options::default().tls(TlsClientContext { domain: DOMAIN.into(), - tls_connector: t!(connector.build()), + tls_connector: unwrap!(connector.build()), }) } } else if #[cfg(all(not(target_os = "macos"), not(windows)))] { use native_tls::backend::openssl::TlsConnectorBuilderExt; fn get_tls_client_options() -> client::Options { - let mut connector = t!(TlsConnector::builder()); - t!(connector.builder_mut() + let mut connector = unwrap!(TlsConnector::builder()); + unwrap!(connector.builder_mut() .builder_mut() .set_ca_file("test/root-ca.pem")); client::Options::default().tls(TlsClientContext { domain: DOMAIN.into(), - tls_connector: t!(connector.build()), + tls_connector: unwrap!(connector.build()), }) } // not implemented for windows or other platforms @@ -779,7 +779,7 @@ mod functional_test { where C: client::sync::Connect, S: SyncServiceExt { let (server_options, client_options) = tls_context(); - let addr = t!(server.listen("localhost:0".first_socket_addr(), server_options)); + let addr = unwrap!(server.listen("localhost:0".first_socket_addr(), server_options)); let client = C::connect(addr, client_options); (addr, client) } @@ -788,9 +788,9 @@ mod functional_test { where C: client::future::Connect, S: FutureServiceExt { let (server_options, client_options) = tls_context(); - let addr = t!(server.listen("localhost:0".first_socket_addr(), + let addr = unwrap!(server.listen("localhost:0".first_socket_addr(), server_options).wait()); - let client = t!(C::connect(addr, client_options).wait()); + let client = unwrap!(C::connect(addr, client_options).wait()); (addr, client) } @@ -798,9 +798,9 @@ mod functional_test { where C: client::future::Connect, S: error_service::FutureServiceExt { let (server_options, client_options) = tls_context(); - let addr = t!(server.listen("localhost:0".first_socket_addr(), + let addr = unwrap!(server.listen("localhost:0".first_socket_addr(), server_options).wait()); - let client = t!(C::connect(addr, client_options).wait()); + let client = unwrap!(C::connect(addr, client_options).wait()); (addr, client) } } else { @@ -821,7 +821,7 @@ mod functional_test { fn start_server_with_sync_client(server: S) -> (SocketAddr, io::Result) where C: client::sync::Connect, S: SyncServiceExt { - let addr = t!(server.listen("localhost:0".first_socket_addr(), + let addr = unwrap!(server.listen("localhost:0".first_socket_addr(), get_server_options())); let client = C::connect(addr, get_client_options()); (addr, client) @@ -830,18 +830,18 @@ mod functional_test { fn start_server_with_async_client(server: S) -> (SocketAddr, C) where C: client::future::Connect, S: FutureServiceExt { - let addr = t!(server.listen("localhost:0".first_socket_addr(), + let addr = unwrap!(server.listen("localhost:0".first_socket_addr(), get_server_options()).wait()); - let client = t!(C::connect(addr, get_client_options()).wait()); + let client = unwrap!(C::connect(addr, get_client_options()).wait()); (addr, client) } fn start_err_server_with_async_client(server: S) -> (SocketAddr, C) where C: client::future::Connect, S: error_service::FutureServiceExt { - let addr = t!(server.listen("localhost:0".first_socket_addr(), + let addr = unwrap!(server.listen("localhost:0".first_socket_addr(), get_server_options()).wait()); - let client = t!(C::connect(addr, get_client_options()).wait()); + let client = unwrap!(C::connect(addr, get_client_options()).wait()); (addr, client) } } @@ -868,7 +868,7 @@ mod functional_test { fn simple() { let _ = env_logger::init(); let (_, client) = start_server_with_sync_client::(Server); - let client = t!(client); + let client = unwrap!(client); assert_eq!(3, client.add(1, 2).unwrap()); assert_eq!("Hey, Tim.", client.hey("Tim".to_string()).unwrap()); } @@ -961,7 +961,6 @@ mod functional_test { assert_eq!(3, client.add(1, 2).wait().unwrap()); assert_eq!("Hey, Tim.", client.hey("Tim".to_string()).wait().unwrap()); } - } pub mod error_service { diff --git a/src/server.rs b/src/server.rs index 535300ef..157329b9 100644 --- a/src/server.rs +++ b/src/server.rs @@ -33,8 +33,7 @@ enum Acceptor { } /// Additional options to configure how the server operates. -#[cfg_attr(feature = "tls", derive(Default))] -#[cfg_attr(not(feature = "tls"), derive(Clone, Default))] +#[derive(Default)] pub struct Options { reactor: Option, #[cfg(feature = "tls")] @@ -75,7 +74,7 @@ pub fn listen(new_service: S, addr: SocketAddr, options: Option Resp: Serialize + 'static, E: Serialize + 'static { - // similar to the client, since `Options` is not `Send`, we take the `TlsAcceptor` when it is + // Similar to the client, since `Options` is not `Send`, we take the `TlsAcceptor` when it is // available. #[cfg(feature = "tls")] let acceptor = match options.tls_acceptor { @@ -111,7 +110,6 @@ pub fn listen(new_service: S, addr: SocketAddr, options: Option } } /// Spawns a service that binds to the given address using the given handle. -#[doc(hidden)] fn listen_with(new_service: S, addr: SocketAddr, handle: Handle, From 61acb5f286c1ea80c4443d2793a55e70ab3dc297 Mon Sep 17 00:00:00 2001 From: Christopher Brickley Date: Mon, 23 Jan 2017 17:45:22 -0500 Subject: [PATCH 16/27] chore: rustfmt --- src/macros.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/macros.rs b/src/macros.rs index 6a8ef0b3..bc39badc 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -787,7 +787,8 @@ mod functional_test { where C: client::sync::Connect, S: SyncServiceExt { let (server_options, client_options) = tls_context(); - let addr = unwrap!(server.listen("localhost:0".first_socket_addr(), server_options)); + let addr = unwrap!(server.listen("localhost:0".first_socket_addr(), + server_options)); let client = C::connect(addr, client_options); (addr, client) } From 6e4c22bcb6949740250e3a89ff34a2099c895c1d Mon Sep 17 00:00:00 2001 From: Christopher Brickley Date: Mon, 23 Jan 2017 19:42:50 -0500 Subject: [PATCH 17/27] chore: rm extra newline --- src/client.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/client.rs b/src/client.rs index 7096fd94..a4b7ec23 100644 --- a/src/client.rs +++ b/src/client.rs @@ -53,7 +53,6 @@ pub mod tls { domain: domain.into(), tls_connector: tls_connector, } - } } } From e9653f5cf327dacc76dfb4829530ec6b719d9062 Mon Sep 17 00:00:00 2001 From: Christopher Brickley Date: Mon, 23 Jan 2017 19:47:33 -0500 Subject: [PATCH 18/27] chore: use re-exported `NativeTlsError` type --- src/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client.rs b/src/client.rs index a4b7ec23..a8a2f209 100644 --- a/src/client.rs +++ b/src/client.rs @@ -40,7 +40,7 @@ pub mod tls { impl TlsClientContext { /// Try to construct a new `TlsClientContext`, providing the domain the client will /// connect to. - pub fn new>(domain: S) -> Result { + pub fn new>(domain: S) -> Result { Ok(TlsClientContext { domain: domain.into(), tls_connector: TlsConnector::builder()?.build()?, From ce5cbcac685b1a5dc9b69e2a5aada73bab669c5d Mon Sep 17 00:00:00 2001 From: Christopher Brickley Date: Tue, 24 Jan 2017 09:06:09 -0500 Subject: [PATCH 19/27] refactor: `TlsClientContext` -> `tls::client::Context` --- README.md | 12 ++++++------ src/client.rs | 24 ++++++++++++------------ src/lib.rs | 7 ++++--- src/macros.rs | 8 ++++---- 4 files changed, 26 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 0b71bfd8..82ab2420 100644 --- a/README.md +++ b/README.md @@ -136,16 +136,16 @@ fn main() { ## Example: Futures + TLS By default, tarpc internally uses a [`TcpStream`] for communication between your clients and -servers. However, TCP by itself has no encryption. As a result, your comunication will be sent in +servers. However, TCP by itself has no encryption. As a result, your communication will be sent in the clear. If you want your RPC communications to be encrypted, you can choose to use [TLS]. TLS operates as an encryption layer on top of TCP. When using TLS, your communication will occur over a [`TlsStream`]. You can add the ability to make TLS clients and servers by adding `tarpc` with the `tls` feature flag enabled. When using TLS, some additional information is required. You will need to make [`TlsAcceptor`] and -`TlsClientContext` structs; `TlsClientContext` requires a [`TlsConnector`]. The [`TlsAcceptor`] and -[`TlsConnector`] types are defined in the [native-tls]. `tarpc` re-exports TLS-related types in its -`tls` module (`tarpc::tls`). +`client::tls::Context` structs; `client::tls::Context` requires a [`TlsConnector`]. The +[`TlsAcceptor`] and [`TlsConnector`] types are defined in the [native-tls]. tarpc re-exports +external TLS-related types in its `tls` module (`tarpc::tls`). [TLS]: https://en.wikipedia.org/wiki/Transport_Layer_Security [`TcpStream`]: https://docs.rs/tokio-core/0.1/tokio_core/net/struct.TcpStream.html @@ -172,7 +172,7 @@ use tarpc::{client, server}; use tarpc::client::future::Connect; use tarpc::util::{FirstSocketAddr, Never}; use tokio_core::reactor; -use tarpc::tls::{Pkcs12, TlsAcceptor, TlsClientContext}; +use tarpc::tls::{Pkcs12, TlsAcceptor}; service! { rpc hello(name: String) -> String; @@ -203,7 +203,7 @@ fn main() { .handle(core.handle()) .tls(acceptor)).wait().unwrap(); let options = client::Options::default().handle(core.handle() - .tls(TlsClientContext::new("foobar.com").unwrap())); + .tls(client::tls::Context::new("foobar.com").unwrap())); core.run(FutureClient::connect(addr, options) .map_err(tarpc::Error::from) .and_then(|client| client.hello("Mom".to_string())) diff --git a/src/client.rs b/src/client.rs index a8a2f209..e88cca31 100644 --- a/src/client.rs +++ b/src/client.rs @@ -29,27 +29,27 @@ type BindClient = >> as pub mod tls { use native_tls::TlsConnector; - /// TLS context - pub struct TlsClientContext { + /// TLS context for client + pub struct Context { /// Domain to connect to pub domain: String, /// TLS connector pub tls_connector: TlsConnector, } - impl TlsClientContext { - /// Try to construct a new `TlsClientContext`, providing the domain the client will + impl Context { + /// Try to construct a new `Context`, providing the domain the client will /// connect to. pub fn new>(domain: S) -> Result { - Ok(TlsClientContext { + Ok(Context { domain: domain.into(), tls_connector: TlsConnector::builder()?.build()?, }) } - /// Construct a new `TlsClientContext` using the provided domain and `TlsConnector` + /// Construct a new `Context` using the provided domain and `TlsConnector` pub fn from_connector>(domain: S, tls_connector: TlsConnector) -> Self { - TlsClientContext { + Context { domain: domain.into(), tls_connector: tls_connector, } @@ -130,7 +130,7 @@ impl fmt::Debug for Client pub struct Options { reactor: Option, #[cfg(feature = "tls")] - tls_ctx: Option, + tls_ctx: Option, } impl Options { @@ -146,9 +146,9 @@ impl Options { self } - /// Connect using the given `TlsClientContext` + /// Connect using the given `Context` #[cfg(feature = "tls")] - pub fn tls(mut self, tls_ctx: TlsClientContext) -> Self { + pub fn tls(mut self, tls_ctx: Context) -> Self { self.tls_ctx = Some(tls_ctx); self } @@ -171,7 +171,7 @@ pub mod future { cfg_if! { if #[cfg(feature = "tls")] { use tokio_tls::{ConnectAsync, TlsStream, TlsConnectorExt}; - use super::tls::TlsClientContext; + use super::tls::Context; use errors::native_to_io; } else {} } @@ -248,7 +248,7 @@ pub mod future { /// Provides the connection Fn impl for Tls struct ConnectFn { #[cfg(feature = "tls")] - tls_ctx: Option, + tls_ctx: Option, } impl FnOnce<(TcpStream,)> for ConnectFn { diff --git a/src/lib.rs b/src/lib.rs index 36418ec8..0a7946f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -73,7 +73,7 @@ //! use tarpc::{client, server}; //! use tarpc::client::sync::Connect; //! use tarpc::util::Never; -//! use tarpc::tls::{TlsClientContext, TlsAcceptor, Pkcs12}; +//! use tarpc::tls::{TlsAcceptor, Pkcs12}; //! //! service! { //! rpc hello(name: String) -> String; @@ -98,7 +98,9 @@ //! let addr = "localhost:10000"; //! let acceptor = get_acceptor(); //! let _server = HelloServer.listen(addr, server::Options::default().tls(acceptor)); -//! let client = SyncClient::connect(addr, client::Options::default().tls(TlsClientContext::new("foobar.com").unwrap())).unwrap(); +//! let client = SyncClient::connect(addr, client::Options::default() +//! .tls(client::tls::Context::new("foobar.com") +//! .unwrap())).unwrap(); //! println!("{}", client.hello("Mom".to_string()).unwrap()); //! } //! ``` @@ -194,7 +196,6 @@ cfg_if! { /// Re-exported TLS-related types pub mod tls { - pub use client::tls::TlsClientContext; pub use native_tls::Error as NativeTlsError; pub use native_tls::{Pkcs12, TlsAcceptor, TlsConnector}; } diff --git a/src/macros.rs b/src/macros.rs index bc39badc..2e444789 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -719,7 +719,7 @@ mod functional_test { if #[cfg(feature = "tls")] { const DOMAIN: &'static str = "foobar.com"; - use tls::TlsClientContext; + use client::tls::Context; use ::native_tls::{Pkcs12, TlsAcceptor, TlsConnector}; fn tls_context() -> (server::Options, client::Options) { @@ -749,7 +749,7 @@ mod functional_test { let mut connector = unwrap!(TlsConnector::builder()); connector.anchor_certificates(&[cert]); - client::Options::default().tls(TlsClientContext { + client::Options::default().tls(Context { domain: DOMAIN.into(), tls_connector: unwrap!(connector.build()), }) @@ -763,14 +763,14 @@ mod functional_test { .builder_mut() .set_ca_file("test/root-ca.pem")); - client::Options::default().tls(TlsClientContext { + client::Options::default().tls(Context { domain: DOMAIN.into(), tls_connector: unwrap!(connector.build()), }) } // not implemented for windows or other platforms } else { - fn get_tls_client_context() -> TlsClientContext { + fn get_tls_client_context() -> Context { unimplemented!() } } From e1311e6d40a3fb0df5cc59d5cf284d590ed00c8f Mon Sep 17 00:00:00 2001 From: Christopher Brickley Date: Tue, 24 Jan 2017 09:30:49 -0500 Subject: [PATCH 20/27] chore: clippy --- src/client.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/client.rs b/src/client.rs index e88cca31..2d11b48f 100644 --- a/src/client.rs +++ b/src/client.rs @@ -198,10 +198,10 @@ pub mod future { E: Deserialize + 'static { #[cfg(not(feature = "tls"))] - #[allow(unknown_lints, type_complexity)] + #[cfg_attr(feature = "cargo-clippy", allow(type_complexity))] inner: ConnectFutureInner>, #[cfg(feature = "tls")] - #[allow(unknown_lints, type_complexity)] + #[cfg_attr(feature = "cargo-clippy", allow(type_complexity))] inner: ConnectFutureInner, futures::Map, fn(::native_tls::Error) -> io::Error>, fn(TlsStream) -> StreamType>>>, @@ -253,6 +253,7 @@ pub mod future { impl FnOnce<(TcpStream,)> for ConnectFn { #[cfg(feature = "tls")] + #[cfg_attr(feature = "cargo-clippy", allow(type_complexity))] type Output = future::Either, futures::Map, fn(::native_tls::Error) From 4831c27319dfa37bfebf6ef58ca57d35702c4340 Mon Sep 17 00:00:00 2001 From: Christopher Brickley Date: Tue, 24 Jan 2017 09:36:25 -0500 Subject: [PATCH 21/27] docs(example): update formatting --- src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0a7946f6..76ed60d4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -98,9 +98,10 @@ //! let addr = "localhost:10000"; //! let acceptor = get_acceptor(); //! let _server = HelloServer.listen(addr, server::Options::default().tls(acceptor)); -//! let client = SyncClient::connect(addr, client::Options::default() -//! .tls(client::tls::Context::new("foobar.com") -//! .unwrap())).unwrap(); +//! let client = SyncClient::connect(addr, +//! client::Options::default() +//! .tls(client::tls::Context::new("foobar.com").unwrap())) +//! .unwrap(); //! println!("{}", client.hello("Mom".to_string()).unwrap()); //! } //! ``` From 9f358a809037dc02b8552d54b7e3595ea5480486 Mon Sep 17 00:00:00 2001 From: Christopher Brickley Date: Thu, 26 Jan 2017 13:08:27 -0500 Subject: [PATCH 22/27] docs(TLS): set the TLS doctest to ignore The `cfg_attr` logic caused many false warnings from clippy. --- src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 76ed60d4..b1bf7279 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,8 +61,7 @@ //! //! Example usage with TLS: //! -#![cfg_attr(feature = "tls", doc = " ```rust,no_run")] -#![cfg_attr(not(feature = "tls"), doc = " ```ignore")] +//! ```ignore //! // required by `FutureClient` (not used in this example) //! #![feature(conservative_impl_trait, plugin)] //! #![plugin(tarpc_plugins)] From f0dd32e5e7a01162d15d81811561fee82dd79e44 Mon Sep 17 00:00:00 2001 From: Christopher Brickley Date: Fri, 27 Jan 2017 13:45:42 -0500 Subject: [PATCH 23/27] docs(client): correct comment about `tls::Context.domain`'s usage --- src/client.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/client.rs b/src/client.rs index 2d11b48f..e86ece67 100644 --- a/src/client.rs +++ b/src/client.rs @@ -38,8 +38,11 @@ pub mod tls { } impl Context { - /// Try to construct a new `Context`, providing the domain the client will - /// connect to. + /// Try to construct a new `Context`. + /// + /// The provided domain will be used for both + /// [SNI](https://en.wikipedia.org/wiki/Server_Name_Indication) and certificate hostname + /// validation. pub fn new>(domain: S) -> Result { Ok(Context { domain: domain.into(), @@ -48,6 +51,10 @@ pub mod tls { } /// Construct a new `Context` using the provided domain and `TlsConnector` + /// + /// The domain will be used for both + /// [SNI](https://en.wikipedia.org/wiki/Server_Name_Indication) and certificate hostname + /// validation. pub fn from_connector>(domain: S, tls_connector: TlsConnector) -> Self { Context { domain: domain.into(), From 7c9cca83aa84e414ba1d6984815aad9ecba61b27 Mon Sep 17 00:00:00 2001 From: Christopher Brickley Date: Mon, 30 Jan 2017 21:04:23 -0500 Subject: [PATCH 24/27] fix(tls): change `tls` to `native_tls` for re-exports --- README.md | 4 ++-- src/client.rs | 4 ++-- src/lib.rs | 9 ++++----- src/macros.rs | 6 +++--- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 82ab2420..da7b06c6 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,7 @@ with the `tls` feature flag enabled. When using TLS, some additional information is required. You will need to make [`TlsAcceptor`] and `client::tls::Context` structs; `client::tls::Context` requires a [`TlsConnector`]. The [`TlsAcceptor`] and [`TlsConnector`] types are defined in the [native-tls]. tarpc re-exports -external TLS-related types in its `tls` module (`tarpc::tls`). +external TLS-related types in its `native_tls` module (`tarpc::native_tls`). [TLS]: https://en.wikipedia.org/wiki/Transport_Layer_Security [`TcpStream`]: https://docs.rs/tokio-core/0.1/tokio_core/net/struct.TcpStream.html @@ -172,7 +172,7 @@ use tarpc::{client, server}; use tarpc::client::future::Connect; use tarpc::util::{FirstSocketAddr, Never}; use tokio_core::reactor; -use tarpc::tls::{Pkcs12, TlsAcceptor}; +use tarpc::native_tls::{Pkcs12, TlsAcceptor}; service! { rpc hello(name: String) -> String; diff --git a/src/client.rs b/src/client.rs index e86ece67..a1a2b192 100644 --- a/src/client.rs +++ b/src/client.rs @@ -27,7 +27,7 @@ type BindClient = >> as /// TLS-specific functionality #[cfg(feature = "tls")] pub mod tls { - use native_tls::TlsConnector; + use native_tls::{Error, TlsConnector}; /// TLS context for client pub struct Context { @@ -43,7 +43,7 @@ pub mod tls { /// The provided domain will be used for both /// [SNI](https://en.wikipedia.org/wiki/Server_Name_Indication) and certificate hostname /// validation. - pub fn new>(domain: S) -> Result { + pub fn new>(domain: S) -> Result { Ok(Context { domain: domain.into(), tls_connector: TlsConnector::builder()?.build()?, diff --git a/src/lib.rs b/src/lib.rs index b1bf7279..da44cecf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,7 +72,7 @@ //! use tarpc::{client, server}; //! use tarpc::client::sync::Connect; //! use tarpc::util::Never; -//! use tarpc::tls::{TlsAcceptor, Pkcs12}; +//! use tarpc::native_tls::{TlsAcceptor, Pkcs12}; //! //! service! { //! rpc hello(name: String) -> String; @@ -192,12 +192,11 @@ enum Reactor { cfg_if! { if #[cfg(feature = "tls")] { extern crate tokio_tls; - extern crate native_tls; + extern crate native_tls as native_tls_inner; /// Re-exported TLS-related types - pub mod tls { - pub use native_tls::Error as NativeTlsError; - pub use native_tls::{Pkcs12, TlsAcceptor, TlsConnector}; + pub mod native_tls { + pub use native_tls_inner::{Error, Pkcs12, TlsAcceptor, TlsConnector}; } } else {} } diff --git a/src/macros.rs b/src/macros.rs index 2e444789..645eab3d 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -720,7 +720,7 @@ mod functional_test { const DOMAIN: &'static str = "foobar.com"; use client::tls::Context; - use ::native_tls::{Pkcs12, TlsAcceptor, TlsConnector}; + use native_tls::{Pkcs12, TlsAcceptor, TlsConnector}; fn tls_context() -> (server::Options, client::Options) { let buf = include_bytes!("../test/identity.p12"); @@ -741,7 +741,7 @@ mod functional_test { extern crate security_framework; use self::security_framework::certificate::SecCertificate; - use native_tls::backend::security_framework::TlsConnectorBuilderExt; + use native_tls_inner::backend::security_framework::TlsConnectorBuilderExt; fn get_tls_client_options() -> client::Options { let buf = include_bytes!("../test/root-ca.der"); @@ -755,7 +755,7 @@ mod functional_test { }) } } else if #[cfg(all(not(target_os = "macos"), not(windows)))] { - use native_tls::backend::openssl::TlsConnectorBuilderExt; + use native_tls_inner::backend::openssl::TlsConnectorBuilderExt; fn get_tls_client_options() -> client::Options { let mut connector = unwrap!(TlsConnector::builder()); From bec30afb7990a7471d179f5ff5b83d3f1a788f38 Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 31 Jan 2017 10:01:18 -0800 Subject: [PATCH 25/27] Fix merge fallout --- src/macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/macros.rs b/src/macros.rs index 8cc50c4c..28df21c3 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -980,7 +980,7 @@ mod functional_test { let client = FutureClient::connect(addr, client::Options::default()).wait().unwrap(); assert_eq!(3, client.add(1, 2).wait().unwrap()); assert_eq!("Hey, Tim.", client.hey("Tim".to_string()).wait().unwrap()); - + } } pub mod error_service { From c2bcc886ab2b3bcc3d6a97df3e092403b5b4e784 Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 31 Jan 2017 10:06:46 -0800 Subject: [PATCH 26/27] Fix merge fallout --- src/macros.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/macros.rs b/src/macros.rs index 28df21c3..b10533fa 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -951,6 +951,9 @@ mod functional_test { #[test] fn reuse_addr() { + use util::FirstSocketAddr; + use super::FutureServiceExt; + let _ = env_logger::init(); let addr = Server.listen("localhost:0".first_socket_addr(), server::Options::default()) .wait() From ac353e036d4d341e300e1d82f1e1ea35f595d95e Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 31 Jan 2017 10:13:28 -0800 Subject: [PATCH 27/27] Fix merge fallout. --- src/macros.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/macros.rs b/src/macros.rs index b10533fa..2eaeffad 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -952,6 +952,7 @@ mod functional_test { #[test] fn reuse_addr() { use util::FirstSocketAddr; + use server; use super::FutureServiceExt; let _ = env_logger::init();