Skip to content

Commit

Permalink
Merge pull request #27 from tikue/listener
Browse files Browse the repository at this point in the history
[Breaking] Add support for arbitrary transports.
  • Loading branch information
shaladdle committed Apr 25, 2016
2 parents 3693c95 + 4d636d2 commit 6ce3a3d
Show file tree
Hide file tree
Showing 11 changed files with 414 additions and 113 deletions.
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ impl HelloService for HelloServer {
}

fn main() {
let server_handle = HelloServer.spawn("0.0.0.0:0").unwrap();
let client = hello_service::Client::new(server_handle.local_addr()).unwrap();
let addr = "localhost:10000";
let server_handle = HelloServer.spawn(addr).unwrap();
let client = hello_service::Client::new(addr).unwrap();
assert_eq!("Hello, Mom!", client.hello("Mom".into()).unwrap());
drop(client);
server_handle.shutdown();
Expand All @@ -56,17 +57,18 @@ fn main() {

The `service!` macro expands to a collection of items that collectively form an rpc service. In the
above example, the macro is called within the `hello_service` module. This module will contain a
`Client` (and `AsyncClient`) type, and a `Service` trait. The trait provides `default fn`s for
starting the service: `spawn` and `spawn_with_config`, which start the service listening on a tcp
port. A `Client` (or `AsyncClient`) can connect to such a service. These generated types make it
easy and ergonomic to write servers without dealing with sockets or serialization directly. See the
tarpc_examples package for more sophisticated examples.
`Client` (and `AsyncClient`) type, and a `Service` trait. The trait provides default `fn`s for
starting the service: `spawn` and `spawn_with_config`, which start the service listening over an
arbitrary transport. A `Client` (or `AsyncClient`) can connect to such a service. These generated
types make it easy and ergonomic to write servers without dealing with sockets or serialization
directly. See the tarpc_examples package for more sophisticated examples.

## Documentation
Use `cargo doc` as you normally would to see the documentation created for all
items expanded by a `service!` invocation.

## Additional Features
- Connect over any transport that `impl`s the `Transport` trait.
- Concurrent requests from a single client.
- Any type that `impl`s `serde`'s `Serialize` and `Deserialize` can be used in the rpc signatures.
- Attributes can be specified on rpc methods. These will be included on both the `Service` trait
Expand Down
14 changes: 8 additions & 6 deletions tarpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ readme = "../README.md"
description = "An RPC framework for Rust with a focus on ease of use."

[dependencies]
bincode = "^0.5"
log = "^0.3"
scoped-pool = "^0.1"
serde = "^0.7"
bincode = "0.5"
log = "0.3"
scoped-pool = "0.1"
serde = "0.7"
unix_socket = "0.5"

[dev-dependencies]
lazy_static = "^0.1"
env_logger = "^0.3"
lazy_static = "0.1"
env_logger = "0.3"
tempdir = "0.3"
11 changes: 7 additions & 4 deletions tarpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,13 @@
//! }
//!
//! fn main() {
//! let addr = "127.0.0.1:9000";
//! let shutdown = Server.spawn(addr).unwrap();
//! let client = Client::new(addr).unwrap();
//! let serve_handle = Server.spawn("localhost:0").unwrap();
//! let client = Client::new(serve_handle.dialer()).unwrap();
//! assert_eq!(3, client.add(1, 2).unwrap());
//! assert_eq!("Hello, Mom!".to_string(),
//! client.hello("Mom".to_string()).unwrap());
//! drop(client);
//! shutdown.shutdown();
//! serve_handle.shutdown();
//! }
//! ```

Expand All @@ -48,6 +47,7 @@ extern crate bincode;
#[macro_use]
extern crate log;
extern crate scoped_pool;
extern crate unix_socket;

macro_rules! pos {
() => (concat!(file!(), ":", line!()))
Expand All @@ -60,4 +60,7 @@ pub mod protocol;
/// Provides the macro used for constructing rpc services and client stubs.
pub mod macros;

/// Provides transport traits and implementations.
pub mod transport;

pub use protocol::{Config, Error, Result, ServeHandle};
102 changes: 69 additions & 33 deletions tarpc/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ macro_rules! service {
#[doc(hidden)]
#[macro_export]
macro_rules! service_inner {
// Pattern for when the next rpc has an implicit unit return type
// Pattern for when the next rpc has an implicit unit return type
(
{
$(#[$attr:meta])*
Expand All @@ -281,7 +281,7 @@ macro_rules! service_inner {
rpc $fn_name( $( $arg : $in_ ),* ) -> ();
}
};
// Pattern for when the next rpc has an explicit return type
// Pattern for when the next rpc has an explicit return type
(
{
$(#[$attr:meta])*
Expand All @@ -300,7 +300,7 @@ macro_rules! service_inner {
rpc $fn_name( $( $arg : $in_ ),* ) -> $out;
}
};
// Pattern when all return types have been expanded
// Pattern when all return types have been expanded
(
{ } // none left to expand
$(
Expand All @@ -316,21 +316,30 @@ macro_rules! service_inner {
)*

#[doc="Spawn a running service."]
fn spawn<A>(self, addr: A) -> $crate::Result<$crate::protocol::ServeHandle>
where A: ::std::net::ToSocketAddrs,
fn spawn<T>(self,
transport: T)
-> $crate::Result<
$crate::protocol::ServeHandle<
<T::Listener as $crate::transport::Listener>::Dialer>>
where T: $crate::transport::Transport,
Self: 'static,
{
self.spawn_with_config(addr, $crate::Config::default())
self.spawn_with_config(transport, $crate::Config::default())
}

#[doc="Spawn a running service."]
fn spawn_with_config<A>(self, addr: A, config: $crate::Config)
-> $crate::Result<$crate::protocol::ServeHandle>
where A: ::std::net::ToSocketAddrs,
fn spawn_with_config<T>(self,
transport: T,
config: $crate::Config)
-> $crate::Result<
$crate::protocol::ServeHandle<
<T::Listener as $crate::transport::Listener>::Dialer>>
where T: $crate::transport::Transport,
Self: 'static,
{
let server = ::std::sync::Arc::new(__Server(self));
let handle = try!($crate::protocol::Serve::spawn_with_config(server, addr, config));
let server = __Server(self);
let result = $crate::protocol::Serve::spawn_with_config(server, transport, config);
let handle = try!(result);
::std::result::Result::Ok(handle)
}
}
Expand Down Expand Up @@ -386,25 +395,29 @@ macro_rules! service_inner {

#[allow(unused)]
#[doc="The client stub that makes RPC calls to the server."]
pub struct Client($crate::protocol::Client<__Request, __Reply>);
pub struct Client<S = ::std::net::TcpStream>(
$crate::protocol::Client<__Request, __Reply, S>
) where S: $crate::transport::Stream;

impl Client {
impl<S> Client<S>
where S: $crate::transport::Stream
{
#[allow(unused)]
#[doc="Create a new client with default configuration that connects to the given \
address."]
pub fn new<A>(addr: A) -> $crate::Result<Self>
where A: ::std::net::ToSocketAddrs,
pub fn new<D>(dialer: D) -> $crate::Result<Self>
where D: $crate::transport::Dialer<Stream=S>,
{
Self::with_config(addr, $crate::Config::default())
Self::with_config(dialer, $crate::Config::default())
}

#[allow(unused)]
#[doc="Create a new client with the specified configuration that connects to the \
given address."]
pub fn with_config<A>(addr: A, config: $crate::Config) -> $crate::Result<Self>
where A: ::std::net::ToSocketAddrs,
pub fn with_config<D>(dialer: D, config: $crate::Config) -> $crate::Result<Self>
where D: $crate::transport::Dialer<Stream=S>,
{
let inner = try!($crate::protocol::Client::with_config(addr, config));
let inner = try!($crate::protocol::Client::with_config(dialer, config));
::std::result::Result::Ok(Client(inner))
}

Expand All @@ -425,25 +438,27 @@ macro_rules! service_inner {

#[allow(unused)]
#[doc="The client stub that makes asynchronous RPC calls to the server."]
pub struct AsyncClient($crate::protocol::Client<__Request, __Reply>);
pub struct AsyncClient<S = ::std::net::TcpStream>(
$crate::protocol::Client<__Request, __Reply, S>
) where S: $crate::transport::Stream;

impl AsyncClient {
impl<S> AsyncClient<S>
where S: $crate::transport::Stream {
#[allow(unused)]
#[doc="Create a new asynchronous client with default configuration that connects to \
the given address."]
pub fn new<A>(addr: A) -> $crate::Result<Self>
where A: ::std::net::ToSocketAddrs,
pub fn new<D>(dialer: D) -> $crate::Result<Self>
where D: $crate::transport::Dialer<Stream=S>,
{
Self::with_config(addr, $crate::Config::default())
Self::with_config(dialer, $crate::Config::default())
}

#[allow(unused)]
#[doc="Create a new asynchronous client that connects to the given address."]
pub fn with_config<A>(addr: A, config: $crate::Config)
-> $crate::Result<Self>
where A: ::std::net::ToSocketAddrs,
pub fn with_config<D>(dialer: D, config: $crate::Config) -> $crate::Result<Self>
where D: $crate::transport::Dialer<Stream=S>,
{
let inner = try!($crate::protocol::Client::with_config(addr, config));
let inner = try!($crate::protocol::Client::with_config(dialer, config));
::std::result::Result::Ok(AsyncClient(inner))
}

Expand All @@ -463,7 +478,8 @@ macro_rules! service_inner {
}

#[allow(unused)]
struct __Server<S: 'static + Service>(S);
struct __Server<S>(S)
where S: 'static + Service;

impl<S> $crate::protocol::Serve for __Server<S>
where S: 'static + Service
Expand Down Expand Up @@ -513,6 +529,8 @@ mod syntax_test {
#[cfg(test)]
mod functional_test {
extern crate env_logger;
extern crate tempdir;
use transport::unix::UnixTransport;

service! {
rpc add(x: i32, y: i32) -> i32;
Expand All @@ -534,7 +552,7 @@ mod functional_test {
fn simple() {
let _ = env_logger::init();
let handle = Server.spawn("localhost:0").unwrap();
let client = Client::new(handle.local_addr()).unwrap();
let client = Client::new(handle.dialer()).unwrap();
assert_eq!(3, client.add(1, 2).unwrap());
assert_eq!("Hey, Tim.", client.hey("Tim".into()).unwrap());
drop(client);
Expand All @@ -545,7 +563,7 @@ mod functional_test {
fn simple_async() {
let _ = env_logger::init();
let handle = Server.spawn("localhost:0").unwrap();
let client = AsyncClient::new(handle.local_addr()).unwrap();
let client = AsyncClient::new(handle.dialer()).unwrap();
assert_eq!(3, client.add(1, 2).get().unwrap());
assert_eq!("Hey, Adam.", client.hey("Adam".into()).get().unwrap());
drop(client);
Expand All @@ -555,7 +573,7 @@ mod functional_test {
#[test]
fn try_clone() {
let handle = Server.spawn("localhost:0").unwrap();
let client1 = Client::new(handle.local_addr()).unwrap();
let client1 = Client::new(handle.dialer()).unwrap();
let client2 = client1.try_clone().unwrap();
assert_eq!(3, client1.add(1, 2).unwrap());
assert_eq!(3, client2.add(1, 2).unwrap());
Expand All @@ -564,7 +582,19 @@ mod functional_test {
#[test]
fn async_try_clone() {
let handle = Server.spawn("localhost:0").unwrap();
let client1 = AsyncClient::new(handle.local_addr()).unwrap();
let client1 = AsyncClient::new(handle.dialer()).unwrap();
let client2 = client1.try_clone().unwrap();
assert_eq!(3, client1.add(1, 2).get().unwrap());
assert_eq!(3, client2.add(1, 2).get().unwrap());
}

#[test]
fn async_try_clone_unix() {
let temp_dir = tempdir::TempDir::new("tarpc").unwrap();
let temp_file = temp_dir.path()
.join("async_try_clone_unix.tmp");
let handle = Server.spawn(UnixTransport(temp_file)).unwrap();
let client1 = AsyncClient::new(handle.dialer()).unwrap();
let client2 = client1.try_clone().unwrap();
assert_eq!(3, client1.add(1, 2).get().unwrap());
assert_eq!(3, client2.add(1, 2).get().unwrap());
Expand All @@ -576,6 +606,12 @@ mod functional_test {
let _ = ::std::sync::Arc::new(Server).spawn("localhost:0");
}

// Tests that a tcp client can be created from &str
#[allow(dead_code)]
fn test_client_str() {
let _ = Client::new("localhost:0");
}

#[test]
fn serde() {
use bincode;
Expand Down
Loading

0 comments on commit 6ce3a3d

Please sign in to comment.