mirror of
https://github.com/OMGeeky/tarpc.git
synced 2026-02-23 15:49:54 +01:00
Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6745cee72c | ||
|
|
31abea18b3 | ||
|
|
593ac135ce | ||
|
|
05a924d27f | ||
|
|
af9d71ed0d | ||
|
|
9b90f6ae51 | ||
|
|
bbfc8ac352 | ||
|
|
ad86a967ba | ||
|
|
58a0eced19 | ||
|
|
46fffd13e7 | ||
|
|
6c8d4be462 | ||
|
|
e3a517bf0d | ||
|
|
f4e22bdc2e | ||
|
|
46f56fbdc0 | ||
|
|
8665655592 | ||
|
|
4569d26d81 | ||
|
|
b8b92ddb5f | ||
|
|
8dd3390876 | ||
|
|
06c420b60c | ||
|
|
a7fb4d22cc | ||
|
|
b1cd5f34e5 | ||
|
|
088e5f8f2c | ||
|
|
4e0be5b626 | ||
|
|
5516034bbc | ||
|
|
06544faa5a | ||
|
|
39737b720a | ||
|
|
0f36985440 | ||
|
|
959bb691cd | ||
|
|
2a3162c5fa | ||
|
|
0cc976b729 | ||
|
|
4d2d3f24c6 | ||
|
|
2c7c64841f | ||
|
|
4ea142d0f3 | ||
|
|
00751d2518 | ||
|
|
4394a52b65 | ||
|
|
70938501d7 | ||
|
|
d5f5cf4300 | ||
|
|
e2c4164d8c | ||
|
|
78124ef7a8 | ||
|
|
096d354b7e | ||
|
|
7ad0e4b070 | ||
|
|
64755d5329 | ||
|
|
3071422132 | ||
|
|
8847330dbe | ||
|
|
6d396520f4 | ||
|
|
79a2f7fe2f | ||
|
|
af66841f68 | ||
|
|
1ab4cfdff9 | ||
|
|
f7e03eeeb7 |
@@ -9,4 +9,5 @@ os:
|
|||||||
- linux
|
- linux
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- cargo test --all --all-features
|
- cargo test --all-targets --all-features
|
||||||
|
- cargo test --doc --all-features
|
||||||
|
|||||||
43
README.md
43
README.md
@@ -1,6 +1,5 @@
|
|||||||
## tarpc: Tim & Adam's RPC lib
|
## tarpc: Tim & Adam's RPC lib
|
||||||
[](https://travis-ci.org/google/tarpc)
|
[](https://travis-ci.org/google/tarpc)
|
||||||
[](https://coveralls.io/github/google/tarpc?branch=master)
|
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
[](https://crates.io/crates/tarpc)
|
[](https://crates.io/crates/tarpc)
|
||||||
[](https://gitter.im/tarpc/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
[](https://gitter.im/tarpc/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
@@ -31,17 +30,12 @@ works with the community-backed library serde: any serde-serializable type can b
|
|||||||
arguments to tarpc fns.
|
arguments to tarpc fns.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
**NB**: *this example is for master. Are you looking for other
|
|
||||||
[versions](https://docs.rs/tarpc)?*
|
|
||||||
|
|
||||||
Add to your `Cargo.toml` dependencies:
|
Add to your `Cargo.toml` dependencies:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
tarpc = "0.12.0"
|
tarpc = "0.18.0"
|
||||||
tarpc-plugins = "0.4.0"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
The `service!` macro expands to a collection of items that form an
|
The `service!` macro expands to a collection of items that form an
|
||||||
rpc service. In the above example, the macro is called within the
|
rpc service. In the above example, the macro is called within the
|
||||||
`hello_service` module. This module will contain a `Client` stub and `Service` trait. There is
|
`hello_service` module. This module will contain a `Client` stub and `Service` trait. There is
|
||||||
@@ -49,29 +43,29 @@ These generated types make it easy and ergonomic to write servers without dealin
|
|||||||
directly. Simply implement one of the generated traits, and you're off to the
|
directly. Simply implement one of the generated traits, and you're off to the
|
||||||
races!
|
races!
|
||||||
|
|
||||||
## Example:
|
## Example
|
||||||
|
|
||||||
Here's a small service.
|
Here's a small service.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#![feature(plugin, futures_api, pin, arbitrary_self_types, await_macro, async_await)]
|
#![feature(arbitrary_self_types, await_macro, async_await, proc_macro_hygiene)]
|
||||||
#![plugin(tarpc_plugins)]
|
|
||||||
|
|
||||||
use futures::{
|
use futures::{
|
||||||
compat::TokioDefaultSpawner,
|
compat::Executor01CompatExt,
|
||||||
future::{self, Ready},
|
future::{self, Ready},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
spawn,
|
|
||||||
};
|
};
|
||||||
use tarpc::rpc::{
|
use tarpc::{
|
||||||
client, context,
|
client, context,
|
||||||
server::{self, Handler, Server},
|
server::{self, Handler},
|
||||||
};
|
};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
// This is the service definition. It looks a lot like a trait definition.
|
// This is the service definition. It looks a lot like a trait definition.
|
||||||
// It defines one RPC, hello, which takes one arg, name, and returns a String.
|
// It defines one RPC, hello, which takes one arg, name, and returns a String.
|
||||||
tarpc::service! {
|
tarpc::service! {
|
||||||
|
/// Returns a greeting for name.
|
||||||
rpc hello(name: String) -> String;
|
rpc hello(name: String) -> String;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +80,7 @@ impl Service for HelloServer {
|
|||||||
|
|
||||||
type HelloFut = Ready<String>;
|
type HelloFut = Ready<String>;
|
||||||
|
|
||||||
fn hello(&self, _: context::Context, name: String) -> Self::HelloFut {
|
fn hello(self, _: context::Context, name: String) -> Self::HelloFut {
|
||||||
future::ready(format!("Hello, {}!", name))
|
future::ready(format!("Hello, {}!", name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,7 +92,7 @@ async fn run() -> io::Result<()> {
|
|||||||
let addr = transport.local_addr();
|
let addr = transport.local_addr();
|
||||||
|
|
||||||
// The server is configured with the defaults.
|
// The server is configured with the defaults.
|
||||||
let server = Server::new(server::Config::default())
|
let server = server::new(server::Config::default())
|
||||||
// Server can listen on any type that implements the Transport trait.
|
// Server can listen on any type that implements the Transport trait.
|
||||||
.incoming(transport)
|
.incoming(transport)
|
||||||
// Close the stream after the client connects
|
// Close the stream after the client connects
|
||||||
@@ -107,14 +101,14 @@ async fn run() -> io::Result<()> {
|
|||||||
// the generated Service trait.
|
// the generated Service trait.
|
||||||
.respond_with(serve(HelloServer));
|
.respond_with(serve(HelloServer));
|
||||||
|
|
||||||
spawn!(server).unwrap();
|
tokio_executor::spawn(server.unit_error().boxed().compat());
|
||||||
|
|
||||||
let transport = await!(bincode_transport::connect(&addr))?;
|
let transport = await!(bincode_transport::connect(&addr))?;
|
||||||
|
|
||||||
// new_stub is generated by the service! macro. Like Server, it takes a config and any
|
// new_stub is generated by the service! macro. Like Server, it takes a config and any
|
||||||
// Transport as input, and returns a Client, also generated by the macro.
|
// Transport as input, and returns a Client, also generated by the macro.
|
||||||
// by the service mcro.
|
// by the service mcro.
|
||||||
let mut client = await!(new_stub(client::Config::default(), transport));
|
let mut client = await!(new_stub(client::Config::default(), transport))?;
|
||||||
|
|
||||||
// The client has an RPC method for each RPC defined in service!. It takes the same args
|
// The client has an RPC method for each RPC defined in service!. It takes the same args
|
||||||
// as defined, with the addition of a Context, which is always the first arg. The Context
|
// as defined, with the addition of a Context, which is always the first arg. The Context
|
||||||
@@ -127,10 +121,11 @@ async fn run() -> io::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
tarpc::init(tokio::executor::DefaultExecutor::current().compat());
|
||||||
tokio::run(run()
|
tokio::run(run()
|
||||||
.map_err(|e| eprintln!("Oh no: {}", e))
|
.map_err(|e| eprintln!("Oh no: {}", e))
|
||||||
.boxed()
|
.boxed()
|
||||||
.compat(TokioDefaultSpawner),
|
.compat(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -139,13 +134,3 @@ fn main() {
|
|||||||
|
|
||||||
Use `cargo doc` as you normally would to see the documentation created for all
|
Use `cargo doc` as you normally would to see the documentation created for all
|
||||||
items expanded by a `service!` invocation.
|
items expanded by a `service!` invocation.
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
To contribute to tarpc, please see [CONTRIBUTING](CONTRIBUTING.md).
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
tarpc is distributed under the terms of the MIT license.
|
|
||||||
|
|
||||||
See [LICENSE](LICENSE) for details.
|
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
cargo-features = ["rename-dependency"]
|
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "tarpc-bincode-transport"
|
name = "tarpc-bincode-transport"
|
||||||
version = "0.1.0"
|
version = "0.7.0"
|
||||||
authors = ["Tim Kuehn <tikue@google.com>"]
|
authors = ["Tim Kuehn <tikue@google.com>"]
|
||||||
edition = '2018'
|
edition = '2018'
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
@@ -15,29 +13,24 @@ readme = "../README.md"
|
|||||||
description = "A bincode-based transport for tarpc services."
|
description = "A bincode-based transport for tarpc services."
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bincode = { version = "1.0", features = ["i128"] }
|
bincode = "1"
|
||||||
bytes = "0.4"
|
futures-preview = { version = "0.3.0-alpha.16", features = ["compat"] }
|
||||||
futures_legacy = { version = "0.1", package = "futures" }
|
futures_legacy = { version = "0.1", package = "futures" }
|
||||||
pin-utils = "0.1.0-alpha.2"
|
pin-utils = "0.1.0-alpha.4"
|
||||||
rpc = { package = "tarpc-lib", version = "0.1", path = "../rpc", features = ["serde1"] }
|
rpc = { package = "tarpc-lib", version = "0.6", path = "../rpc", features = ["serde1"] }
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
tokio = "0.1"
|
|
||||||
tokio-io = "0.1"
|
tokio-io = "0.1"
|
||||||
tokio-serde-bincode = "0.1"
|
async-bincode = "0.4"
|
||||||
tokio-tcp = "0.1"
|
tokio-tcp = "0.1"
|
||||||
tokio-serde = "0.2"
|
|
||||||
|
|
||||||
[target.'cfg(not(test))'.dependencies]
|
|
||||||
futures-preview = { version = "0.3.0-alpha.8", features = ["compat"] }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
futures-preview = { version = "0.3.0-alpha.8", features = ["compat", "tokio-compat"] }
|
env_logger = "0.6"
|
||||||
env_logger = "0.5"
|
|
||||||
humantime = "1.0"
|
humantime = "1.0"
|
||||||
|
libtest = "0.0.1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
rand = "0.5"
|
rand = "0.6"
|
||||||
tokio = "0.1"
|
tokio = "0.1"
|
||||||
tokio-executor = "0.1"
|
tokio-executor = "0.1"
|
||||||
tokio-reactor = "0.1"
|
tokio-reactor = "0.1"
|
||||||
tokio-serde = "0.2"
|
tokio-serde = "0.3"
|
||||||
tokio-timer = "0.2"
|
tokio-timer = "0.2"
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
edition = "Edition2018"
|
edition = "2018"
|
||||||
|
|||||||
@@ -6,79 +6,132 @@
|
|||||||
|
|
||||||
//! A TCP [`Transport`] that serializes as bincode.
|
//! A TCP [`Transport`] that serializes as bincode.
|
||||||
|
|
||||||
#![feature(
|
#![feature(arbitrary_self_types, async_await)]
|
||||||
futures_api,
|
|
||||||
pin,
|
|
||||||
arbitrary_self_types,
|
|
||||||
underscore_imports,
|
|
||||||
await_macro,
|
|
||||||
async_await,
|
|
||||||
)]
|
|
||||||
#![deny(missing_docs, missing_debug_implementations)]
|
#![deny(missing_docs, missing_debug_implementations)]
|
||||||
|
|
||||||
mod vendored;
|
use async_bincode::{AsyncBincodeStream, AsyncDestination};
|
||||||
|
use futures::{compat::*, prelude::*, ready};
|
||||||
use bytes::{Bytes, BytesMut};
|
|
||||||
use crate::vendored::tokio_serde_bincode::{IoErrorWrapper, ReadBincode, WriteBincode};
|
|
||||||
use futures::{
|
|
||||||
Poll,
|
|
||||||
compat::{Compat01As03, Future01CompatExt, Stream01CompatExt},
|
|
||||||
prelude::*,
|
|
||||||
ready, task,
|
|
||||||
};
|
|
||||||
use futures_legacy::{
|
|
||||||
executor::{
|
|
||||||
self as executor01, Notify as Notify01, NotifyHandle as NotifyHandle01,
|
|
||||||
UnsafeNotify as UnsafeNotify01,
|
|
||||||
},
|
|
||||||
sink::SinkMapErr as SinkMapErr01,
|
|
||||||
sink::With as With01,
|
|
||||||
stream::MapErr as MapErr01,
|
|
||||||
Async as Async01, AsyncSink as AsyncSink01, Sink as Sink01, Stream as Stream01,
|
|
||||||
};
|
|
||||||
use pin_utils::unsafe_pinned;
|
use pin_utils::unsafe_pinned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{fmt, io, marker::PhantomData, net::SocketAddr, pin::Pin, task::LocalWaker};
|
use std::{
|
||||||
use tokio::codec::{Framed, LengthDelimitedCodec, length_delimited};
|
error::Error,
|
||||||
use tokio_tcp::{self, TcpListener, TcpStream};
|
io,
|
||||||
|
marker::PhantomData,
|
||||||
|
net::SocketAddr,
|
||||||
|
pin::Pin,
|
||||||
|
task::{Context, Poll},
|
||||||
|
};
|
||||||
|
use tokio_io::{AsyncRead, AsyncWrite};
|
||||||
|
use tokio_tcp::{TcpListener, TcpStream};
|
||||||
|
|
||||||
/// Returns a new bincode transport that reads from and writes to `io`.
|
/// A transport that serializes to, and deserializes from, a [`TcpStream`].
|
||||||
pub fn new<Item, SinkItem>(io: TcpStream) -> Transport<Item, SinkItem>
|
#[derive(Debug)]
|
||||||
|
pub struct Transport<S, Item, SinkItem> {
|
||||||
|
inner: Compat01As03Sink<AsyncBincodeStream<S, Item, SinkItem, AsyncDestination>, SinkItem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, Item, SinkItem> Transport<S, Item, SinkItem> {
|
||||||
|
unsafe_pinned!(
|
||||||
|
inner: Compat01As03Sink<AsyncBincodeStream<S, Item, SinkItem, AsyncDestination>, SinkItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, Item, SinkItem> Stream for Transport<S, Item, SinkItem>
|
||||||
where
|
where
|
||||||
Item: for<'de> Deserialize<'de>,
|
S: AsyncRead,
|
||||||
SinkItem: Serialize,
|
Item: for<'a> Deserialize<'a>,
|
||||||
{
|
{
|
||||||
let peer_addr = io.peer_addr();
|
type Item = io::Result<Item>;
|
||||||
let local_addr = io.local_addr();
|
|
||||||
let inner = length_delimited::Builder::new()
|
|
||||||
.max_frame_length(8_000_000)
|
|
||||||
.new_framed(io)
|
|
||||||
.map_err(IoErrorWrapper as _)
|
|
||||||
.sink_map_err(IoErrorWrapper as _)
|
|
||||||
.with(freeze as _);
|
|
||||||
let inner = WriteBincode::new(inner);
|
|
||||||
let inner = ReadBincode::new(inner);
|
|
||||||
|
|
||||||
Transport {
|
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<io::Result<Item>>> {
|
||||||
inner,
|
match self.inner().poll_next(cx) {
|
||||||
staged_item: None,
|
Poll::Pending => Poll::Pending,
|
||||||
peer_addr,
|
Poll::Ready(None) => Poll::Ready(None),
|
||||||
local_addr,
|
Poll::Ready(Some(Ok(next))) => Poll::Ready(Some(Ok(next))),
|
||||||
|
Poll::Ready(Some(Err(e))) => {
|
||||||
|
Poll::Ready(Some(Err(io::Error::new(io::ErrorKind::Other, e))))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn freeze(bytes: BytesMut) -> Result<Bytes, IoErrorWrapper> {
|
impl<S, Item, SinkItem> Sink<SinkItem> for Transport<S, Item, SinkItem>
|
||||||
Ok(bytes.freeze())
|
where
|
||||||
|
S: AsyncWrite,
|
||||||
|
SinkItem: Serialize,
|
||||||
|
{
|
||||||
|
type SinkError = io::Error;
|
||||||
|
|
||||||
|
fn start_send(self: Pin<&mut Self>, item: SinkItem) -> io::Result<()> {
|
||||||
|
self.inner()
|
||||||
|
.start_send(item)
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
|
convert(self.inner().poll_ready(cx))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
|
convert(self.inner().poll_flush(cx))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
|
convert(self.inner().poll_close(cx))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Connects to `addr`, wrapping the connection in a bincode transport.
|
fn convert<E: Into<Box<Error + Send + Sync>>>(poll: Poll<Result<(), E>>) -> Poll<io::Result<()>> {
|
||||||
pub async fn connect<Item, SinkItem>(addr: &SocketAddr) -> io::Result<Transport<Item, SinkItem>>
|
match poll {
|
||||||
|
Poll::Pending => Poll::Pending,
|
||||||
|
Poll::Ready(Ok(())) => Poll::Ready(Ok(())),
|
||||||
|
Poll::Ready(Err(e)) => Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, e))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Item, SinkItem> rpc::Transport for Transport<TcpStream, Item, SinkItem>
|
||||||
where
|
where
|
||||||
Item: for<'de> Deserialize<'de>,
|
Item: for<'de> Deserialize<'de>,
|
||||||
SinkItem: Serialize,
|
SinkItem: Serialize,
|
||||||
{
|
{
|
||||||
let stream = await!(TcpStream::connect(addr).compat())?;
|
type Item = Item;
|
||||||
Ok(new(stream))
|
type SinkItem = SinkItem;
|
||||||
|
|
||||||
|
fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||||
|
self.inner.get_ref().get_ref().peer_addr()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||||
|
self.inner.get_ref().get_ref().local_addr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new bincode transport that reads from and writes to `io`.
|
||||||
|
pub fn new<Item, SinkItem>(io: TcpStream) -> Transport<TcpStream, Item, SinkItem>
|
||||||
|
where
|
||||||
|
Item: for<'de> Deserialize<'de>,
|
||||||
|
SinkItem: Serialize,
|
||||||
|
{
|
||||||
|
Transport::from(io)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, Item, SinkItem> From<S> for Transport<S, Item, SinkItem> {
|
||||||
|
fn from(inner: S) -> Self {
|
||||||
|
Transport {
|
||||||
|
inner: Compat01As03Sink::new(AsyncBincodeStream::from(inner).for_async()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connects to `addr`, wrapping the connection in a bincode transport.
|
||||||
|
pub async fn connect<Item, SinkItem>(
|
||||||
|
addr: &SocketAddr,
|
||||||
|
) -> io::Result<Transport<TcpStream, Item, SinkItem>>
|
||||||
|
where
|
||||||
|
Item: for<'de> Deserialize<'de>,
|
||||||
|
SinkItem: Serialize,
|
||||||
|
{
|
||||||
|
Ok(new(TcpStream::connect(addr).compat().await?))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Listens on `addr`, wrapping accepted connections in bincode transports.
|
/// Listens on `addr`, wrapping accepted connections in bincode transports.
|
||||||
@@ -119,168 +172,10 @@ where
|
|||||||
Item: for<'a> Deserialize<'a>,
|
Item: for<'a> Deserialize<'a>,
|
||||||
SinkItem: Serialize,
|
SinkItem: Serialize,
|
||||||
{
|
{
|
||||||
type Item = io::Result<Transport<Item, SinkItem>>;
|
type Item = io::Result<Transport<TcpStream, Item, SinkItem>>;
|
||||||
|
|
||||||
fn poll_next(mut self: Pin<&mut Self>, waker: &LocalWaker) -> Poll<Option<Self::Item>> {
|
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
let next = ready!(self.incoming().poll_next(waker)?);
|
let next = ready!(self.incoming().poll_next(cx)?);
|
||||||
Poll::Ready(next.map(|conn| Ok(new(conn))))
|
Poll::Ready(next.map(|conn| Ok(new(conn))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A transport that serializes to, and deserializes from, a [`TcpStream`].
|
|
||||||
pub struct Transport<Item, SinkItem> {
|
|
||||||
inner: ReadBincode<
|
|
||||||
WriteBincode<
|
|
||||||
With01<
|
|
||||||
SinkMapErr01<
|
|
||||||
MapErr01<
|
|
||||||
Framed<tokio_tcp::TcpStream, LengthDelimitedCodec>,
|
|
||||||
fn(std::io::Error) -> IoErrorWrapper,
|
|
||||||
>,
|
|
||||||
fn(std::io::Error) -> IoErrorWrapper,
|
|
||||||
>,
|
|
||||||
BytesMut,
|
|
||||||
fn(BytesMut) -> Result<Bytes, IoErrorWrapper>,
|
|
||||||
Result<Bytes, IoErrorWrapper>
|
|
||||||
>,
|
|
||||||
SinkItem,
|
|
||||||
>,
|
|
||||||
Item,
|
|
||||||
>,
|
|
||||||
staged_item: Option<SinkItem>,
|
|
||||||
peer_addr: io::Result<SocketAddr>,
|
|
||||||
local_addr: io::Result<SocketAddr>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Item, SinkItem> fmt::Debug for Transport<Item, SinkItem> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(f, "Transport")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Item, SinkItem> Stream for Transport<Item, SinkItem>
|
|
||||||
where
|
|
||||||
Item: for<'a> Deserialize<'a>,
|
|
||||||
{
|
|
||||||
type Item = io::Result<Item>;
|
|
||||||
|
|
||||||
fn poll_next(self: Pin<&mut Self>, waker: &LocalWaker) -> Poll<Option<io::Result<Item>>> {
|
|
||||||
unsafe {
|
|
||||||
let inner = &mut Pin::get_mut_unchecked(self).inner;
|
|
||||||
let mut compat = inner.compat();
|
|
||||||
let compat = Pin::new_unchecked(&mut compat);
|
|
||||||
match ready!(compat.poll_next(waker)) {
|
|
||||||
None => Poll::Ready(None),
|
|
||||||
Some(Ok(next)) => Poll::Ready(Some(Ok(next))),
|
|
||||||
Some(Err(e)) => Poll::Ready(Some(Err(e.0))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Item, SinkItem> Sink for Transport<Item, SinkItem>
|
|
||||||
where
|
|
||||||
SinkItem: Serialize,
|
|
||||||
{
|
|
||||||
type SinkItem = SinkItem;
|
|
||||||
type SinkError = io::Error;
|
|
||||||
|
|
||||||
fn start_send(self: Pin<&mut Self>, item: SinkItem) -> io::Result<()> {
|
|
||||||
let me = unsafe { Pin::get_mut_unchecked(self) };
|
|
||||||
assert!(me.staged_item.is_none());
|
|
||||||
me.staged_item = Some(item);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_ready(self: Pin<&mut Self>, waker: &LocalWaker) -> Poll<io::Result<()>> {
|
|
||||||
let notify = &WakerToHandle(waker);
|
|
||||||
|
|
||||||
executor01::with_notify(notify, 0, move || {
|
|
||||||
let me = unsafe { Pin::get_mut_unchecked(self) };
|
|
||||||
match me.staged_item.take() {
|
|
||||||
Some(staged_item) => match me.inner.start_send(staged_item)? {
|
|
||||||
AsyncSink01::Ready => Poll::Ready(Ok(())),
|
|
||||||
AsyncSink01::NotReady(item) => {
|
|
||||||
me.staged_item = Some(item);
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => Poll::Ready(Ok(())),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_flush(self: Pin<&mut Self>, waker: &LocalWaker) -> Poll<io::Result<()>> {
|
|
||||||
let notify = &WakerToHandle(waker);
|
|
||||||
|
|
||||||
executor01::with_notify(notify, 0, move || {
|
|
||||||
let me = unsafe { Pin::get_mut_unchecked(self) };
|
|
||||||
match me.inner.poll_complete()? {
|
|
||||||
Async01::Ready(()) => Poll::Ready(Ok(())),
|
|
||||||
Async01::NotReady => Poll::Pending,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_close(self: Pin<&mut Self>, waker: &LocalWaker) -> Poll<io::Result<()>> {
|
|
||||||
let notify = &WakerToHandle(waker);
|
|
||||||
|
|
||||||
executor01::with_notify(notify, 0, move || {
|
|
||||||
let me = unsafe { Pin::get_mut_unchecked(self) };
|
|
||||||
match me.inner.get_mut().close()? {
|
|
||||||
Async01::Ready(()) => Poll::Ready(Ok(())),
|
|
||||||
Async01::NotReady => Poll::Pending,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Item, SinkItem> rpc::Transport for Transport<Item, SinkItem>
|
|
||||||
where
|
|
||||||
Item: for<'de> Deserialize<'de>,
|
|
||||||
SinkItem: Serialize,
|
|
||||||
{
|
|
||||||
type Item = Item;
|
|
||||||
type SinkItem = SinkItem;
|
|
||||||
|
|
||||||
fn peer_addr(&self) -> io::Result<SocketAddr> {
|
|
||||||
// TODO: should just access from the inner transport.
|
|
||||||
// https://github.com/alexcrichton/tokio-serde-bincode/issues/4
|
|
||||||
Ok(*self.peer_addr.as_ref().unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn local_addr(&self) -> io::Result<SocketAddr> {
|
|
||||||
Ok(*self.local_addr.as_ref().unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
struct WakerToHandle<'a>(&'a LocalWaker);
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct NotifyWaker(task::Waker);
|
|
||||||
|
|
||||||
impl Notify01 for NotifyWaker {
|
|
||||||
fn notify(&self, _: usize) {
|
|
||||||
self.0.wake();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl UnsafeNotify01 for NotifyWaker {
|
|
||||||
unsafe fn clone_raw(&self) -> NotifyHandle01 {
|
|
||||||
let ptr = Box::new(NotifyWaker(self.0.clone()));
|
|
||||||
|
|
||||||
NotifyHandle01::new(Box::into_raw(ptr))
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn drop_raw(&self) {
|
|
||||||
let ptr: *const dyn UnsafeNotify01 = self;
|
|
||||||
drop(Box::from_raw(ptr as *mut dyn UnsafeNotify01));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<WakerToHandle<'a>> for NotifyHandle01 {
|
|
||||||
fn from(handle: WakerToHandle<'a>) -> NotifyHandle01 {
|
|
||||||
unsafe { NotifyWaker(handle.0.clone().into_waker()).clone_raw() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
// Copyright 2018 Google LLC
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by an MIT-style
|
|
||||||
// license that can be found in the LICENSE file or at
|
|
||||||
// https://opensource.org/licenses/MIT.
|
|
||||||
|
|
||||||
pub(crate) mod tokio_serde_bincode;
|
|
||||||
@@ -1,224 +0,0 @@
|
|||||||
//! `Stream` and `Sink` adaptors for serializing and deserializing values using
|
|
||||||
//! Bincode.
|
|
||||||
//!
|
|
||||||
//! This crate provides adaptors for going from a stream or sink of buffers
|
|
||||||
//! ([`Bytes`]) to a stream or sink of values by performing Bincode encoding or
|
|
||||||
//! decoding. It is expected that each yielded buffer contains a single
|
|
||||||
//! serialized Bincode value. The specific strategy by which this is done is left
|
|
||||||
//! up to the user. One option is to use using [`length_delimited`] from
|
|
||||||
//! [tokio-io].
|
|
||||||
//!
|
|
||||||
//! [`Bytes`]: https://docs.rs/bytes/0.4/bytes/struct.Bytes.html
|
|
||||||
//! [`length_delimited`]: http://alexcrichton.com/tokio-io/tokio_io/codec/length_delimited/index.html
|
|
||||||
//! [tokio-io]: http://github.com/alexcrichton/tokio-io
|
|
||||||
//! [examples]: https://github.com/carllerche/tokio-serde-json/tree/master/examples
|
|
||||||
|
|
||||||
#![allow(missing_debug_implementations)]
|
|
||||||
|
|
||||||
use bincode::Error;
|
|
||||||
use bytes::{Bytes, BytesMut};
|
|
||||||
use futures_legacy::{Poll, Sink, StartSend, Stream};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::io;
|
|
||||||
use tokio_serde::{Deserializer, FramedRead, FramedWrite, Serializer};
|
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
/// Adapts a stream of Bincode encoded buffers to a stream of values by
|
|
||||||
/// deserializing them.
|
|
||||||
///
|
|
||||||
/// `ReadBincode` implements `Stream` by polling the inner buffer stream and
|
|
||||||
/// deserializing the buffer as Bincode. It expects that each yielded buffer
|
|
||||||
/// represents a single Bincode value and does not contain any extra trailing
|
|
||||||
/// bytes.
|
|
||||||
pub(crate) struct ReadBincode<T, U> {
|
|
||||||
inner: FramedRead<T, U, Bincode<U>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adapts a buffer sink to a value sink by serializing the values as Bincode.
|
|
||||||
///
|
|
||||||
/// `WriteBincode` implements `Sink` by serializing the submitted values to a
|
|
||||||
/// buffer. The buffer is then sent to the inner stream, which is responsible
|
|
||||||
/// for handling framing on the wire.
|
|
||||||
pub(crate) struct WriteBincode<T: Sink, U> {
|
|
||||||
inner: FramedWrite<T, U, Bincode<U>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Bincode<T> {
|
|
||||||
ghost: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, U> ReadBincode<T, U>
|
|
||||||
where
|
|
||||||
T: Stream<Error = IoErrorWrapper>,
|
|
||||||
U: for<'de> Deserialize<'de>,
|
|
||||||
Bytes: From<T::Item>,
|
|
||||||
{
|
|
||||||
/// Creates a new `ReadBincode` with the given buffer stream.
|
|
||||||
pub fn new(inner: T) -> ReadBincode<T, U> {
|
|
||||||
let json = Bincode { ghost: PhantomData };
|
|
||||||
ReadBincode {
|
|
||||||
inner: FramedRead::new(inner, json),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, U> ReadBincode<T, U> {
|
|
||||||
/// Returns a mutable reference to the underlying stream wrapped by
|
|
||||||
/// `ReadBincode`.
|
|
||||||
///
|
|
||||||
/// Note that care should be taken to not tamper with the underlying stream
|
|
||||||
/// of data coming in as it may corrupt the stream of frames otherwise
|
|
||||||
/// being worked with.
|
|
||||||
pub fn get_mut(&mut self) -> &mut T {
|
|
||||||
self.inner.get_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, U> Stream for ReadBincode<T, U>
|
|
||||||
where
|
|
||||||
T: Stream<Error = IoErrorWrapper>,
|
|
||||||
U: for<'de> Deserialize<'de>,
|
|
||||||
Bytes: From<T::Item>,
|
|
||||||
{
|
|
||||||
type Item = U;
|
|
||||||
type Error = <T as Stream>::Error;
|
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Option<U>, Self::Error> {
|
|
||||||
self.inner.poll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, U> Sink for ReadBincode<T, U>
|
|
||||||
where
|
|
||||||
T: Sink,
|
|
||||||
{
|
|
||||||
type SinkItem = T::SinkItem;
|
|
||||||
type SinkError = T::SinkError;
|
|
||||||
|
|
||||||
fn start_send(&mut self, item: T::SinkItem) -> StartSend<T::SinkItem, T::SinkError> {
|
|
||||||
self.get_mut().start_send(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_complete(&mut self) -> Poll<(), T::SinkError> {
|
|
||||||
self.get_mut().poll_complete()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn close(&mut self) -> Poll<(), T::SinkError> {
|
|
||||||
self.get_mut().close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct IoErrorWrapper(pub io::Error);
|
|
||||||
impl From<Box<bincode::ErrorKind>> for IoErrorWrapper {
|
|
||||||
fn from(e: Box<bincode::ErrorKind>) -> Self {
|
|
||||||
IoErrorWrapper(match *e {
|
|
||||||
bincode::ErrorKind::Io(e) => e,
|
|
||||||
bincode::ErrorKind::InvalidUtf8Encoding(e) => {
|
|
||||||
io::Error::new(io::ErrorKind::InvalidInput, e)
|
|
||||||
}
|
|
||||||
bincode::ErrorKind::InvalidBoolEncoding(e) => {
|
|
||||||
io::Error::new(io::ErrorKind::InvalidInput, e.to_string())
|
|
||||||
}
|
|
||||||
bincode::ErrorKind::InvalidTagEncoding(e) => {
|
|
||||||
io::Error::new(io::ErrorKind::InvalidInput, e.to_string())
|
|
||||||
}
|
|
||||||
bincode::ErrorKind::InvalidCharEncoding => {
|
|
||||||
io::Error::new(io::ErrorKind::InvalidInput, "Invalid char encoding")
|
|
||||||
}
|
|
||||||
bincode::ErrorKind::DeserializeAnyNotSupported => {
|
|
||||||
io::Error::new(io::ErrorKind::InvalidInput, "Deserialize Any not supported")
|
|
||||||
}
|
|
||||||
bincode::ErrorKind::SizeLimit => {
|
|
||||||
io::Error::new(io::ErrorKind::InvalidInput, "Size limit exceeded")
|
|
||||||
}
|
|
||||||
bincode::ErrorKind::SequenceMustHaveLength => {
|
|
||||||
io::Error::new(io::ErrorKind::InvalidInput, "Sequence must have length")
|
|
||||||
}
|
|
||||||
bincode::ErrorKind::Custom(s) => io::Error::new(io::ErrorKind::Other, s),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<IoErrorWrapper> for io::Error {
|
|
||||||
fn from(wrapper: IoErrorWrapper) -> io::Error {
|
|
||||||
wrapper.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, U> WriteBincode<T, U>
|
|
||||||
where
|
|
||||||
T: Sink<SinkItem = BytesMut, SinkError = IoErrorWrapper>,
|
|
||||||
U: Serialize,
|
|
||||||
{
|
|
||||||
/// Creates a new `WriteBincode` with the given buffer sink.
|
|
||||||
pub fn new(inner: T) -> WriteBincode<T, U> {
|
|
||||||
let json = Bincode { ghost: PhantomData };
|
|
||||||
WriteBincode {
|
|
||||||
inner: FramedWrite::new(inner, json),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Sink, U> WriteBincode<T, U> {
|
|
||||||
/// Returns a mutable reference to the underlying sink wrapped by
|
|
||||||
/// `WriteBincode`.
|
|
||||||
///
|
|
||||||
/// Note that care should be taken to not tamper with the underlying sink as
|
|
||||||
/// it may corrupt the sequence of frames otherwise being worked with.
|
|
||||||
pub fn get_mut(&mut self) -> &mut T {
|
|
||||||
self.inner.get_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, U> Sink for WriteBincode<T, U>
|
|
||||||
where
|
|
||||||
T: Sink<SinkItem = BytesMut, SinkError = IoErrorWrapper>,
|
|
||||||
U: Serialize,
|
|
||||||
{
|
|
||||||
type SinkItem = U;
|
|
||||||
type SinkError = <T as Sink>::SinkError;
|
|
||||||
|
|
||||||
fn start_send(&mut self, item: U) -> StartSend<U, Self::SinkError> {
|
|
||||||
self.inner.start_send(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
|
|
||||||
self.inner.poll_complete()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn close(&mut self) -> Poll<(), Self::SinkError> {
|
|
||||||
self.inner.poll_complete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, U> Stream for WriteBincode<T, U>
|
|
||||||
where
|
|
||||||
T: Stream + Sink,
|
|
||||||
{
|
|
||||||
type Item = T::Item;
|
|
||||||
type Error = T::Error;
|
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Option<T::Item>, T::Error> {
|
|
||||||
self.get_mut().poll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Deserializer<T> for Bincode<T>
|
|
||||||
where
|
|
||||||
T: for<'de> Deserialize<'de>,
|
|
||||||
{
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn deserialize(&mut self, src: &Bytes) -> Result<T, Error> {
|
|
||||||
bincode::deserialize(src)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Serialize> Serializer<T> for Bincode<T> {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn serialize(&mut self, item: &T) -> Result<BytesMut, Self::Error> {
|
|
||||||
bincode::serialize(item).map(Into::into)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,23 +6,13 @@
|
|||||||
|
|
||||||
//! Tests client/server control flow.
|
//! Tests client/server control flow.
|
||||||
|
|
||||||
#![feature(
|
#![feature(test, integer_atomics, async_await)]
|
||||||
test,
|
|
||||||
integer_atomics,
|
|
||||||
futures_api,
|
|
||||||
generators,
|
|
||||||
await_macro,
|
|
||||||
async_await
|
|
||||||
)]
|
|
||||||
|
|
||||||
extern crate test;
|
use futures::{compat::Executor01CompatExt, prelude::*};
|
||||||
|
use libtest::stats::Stats;
|
||||||
use self::test::stats::Stats;
|
|
||||||
use futures::{compat::TokioDefaultSpawner, prelude::*};
|
|
||||||
use rpc::{
|
use rpc::{
|
||||||
client::{self, Client},
|
client, context,
|
||||||
context,
|
server::{Handler, Server},
|
||||||
server::{self, Handler, Server},
|
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
io,
|
io,
|
||||||
@@ -34,17 +24,17 @@ async fn bench() -> io::Result<()> {
|
|||||||
let addr = listener.local_addr();
|
let addr = listener.local_addr();
|
||||||
|
|
||||||
tokio_executor::spawn(
|
tokio_executor::spawn(
|
||||||
Server::<u32, u32>::new(server::Config::default())
|
Server::<u32, u32>::default()
|
||||||
.incoming(listener)
|
.incoming(listener)
|
||||||
.take(1)
|
.take(1)
|
||||||
.respond_with(|_ctx, request| futures::future::ready(Ok(request)))
|
.respond_with(|_ctx, request| futures::future::ready(Ok(request)))
|
||||||
.unit_error()
|
.unit_error()
|
||||||
.boxed()
|
.boxed()
|
||||||
.compat()
|
.compat(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let conn = await!(tarpc_bincode_transport::connect(&addr))?;
|
let conn = tarpc_bincode_transport::connect(&addr).await?;
|
||||||
let client = &mut await!(Client::<u32, u32>::new(client::Config::default(), conn))?;
|
let client = &mut client::new::<u32, u32, _>(client::Config::default(), conn).await?;
|
||||||
|
|
||||||
let total = 10_000usize;
|
let total = 10_000usize;
|
||||||
let mut successful = 0u32;
|
let mut successful = 0u32;
|
||||||
@@ -52,7 +42,7 @@ async fn bench() -> io::Result<()> {
|
|||||||
let mut durations = vec![];
|
let mut durations = vec![];
|
||||||
for _ in 1..=total {
|
for _ in 1..=total {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let response = await!(client.call(context::current(), 0u32));
|
let response = client.call(context::current(), 0u32).await;
|
||||||
let elapsed = now.elapsed();
|
let elapsed = now.elapsed();
|
||||||
|
|
||||||
match response {
|
match response {
|
||||||
@@ -102,14 +92,9 @@ async fn bench() -> io::Result<()> {
|
|||||||
#[test]
|
#[test]
|
||||||
fn bench_small_packet() -> io::Result<()> {
|
fn bench_small_packet() -> io::Result<()> {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
rpc::init(TokioDefaultSpawner);
|
rpc::init(tokio::executor::DefaultExecutor::current().compat());
|
||||||
|
|
||||||
tokio::run(
|
tokio::run(bench().map_err(|e| panic!(e.to_string())).boxed().compat());
|
||||||
bench()
|
|
||||||
.map_err(|e| panic!(e.to_string()))
|
|
||||||
.boxed()
|
|
||||||
.compat(),
|
|
||||||
);
|
|
||||||
println!("done");
|
println!("done");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -6,20 +6,16 @@
|
|||||||
|
|
||||||
//! Tests client/server control flow.
|
//! Tests client/server control flow.
|
||||||
|
|
||||||
#![feature(generators, await_macro, async_await, futures_api,)]
|
#![feature(async_await)]
|
||||||
|
|
||||||
use futures::{
|
use futures::{
|
||||||
compat::{Future01CompatExt, TokioDefaultSpawner},
|
compat::{Executor01CompatExt, Future01CompatExt},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
stream,
|
stream::FuturesUnordered,
|
||||||
};
|
};
|
||||||
use log::{info, trace};
|
use log::{info, trace};
|
||||||
use rand::distributions::{Distribution, Normal};
|
use rand::distributions::{Distribution, Normal};
|
||||||
use rpc::{
|
use rpc::{client, context, server::Server};
|
||||||
client::{self, Client},
|
|
||||||
context,
|
|
||||||
server::{self, Server},
|
|
||||||
};
|
|
||||||
use std::{
|
use std::{
|
||||||
io,
|
io,
|
||||||
time::{Duration, Instant, SystemTime},
|
time::{Duration, Instant, SystemTime},
|
||||||
@@ -40,7 +36,7 @@ impl AsDuration for SystemTime {
|
|||||||
async fn run() -> io::Result<()> {
|
async fn run() -> io::Result<()> {
|
||||||
let listener = tarpc_bincode_transport::listen(&"0.0.0.0:0".parse().unwrap())?;
|
let listener = tarpc_bincode_transport::listen(&"0.0.0.0:0".parse().unwrap())?;
|
||||||
let addr = listener.local_addr();
|
let addr = listener.local_addr();
|
||||||
let server = Server::<String, String>::new(server::Config::default())
|
let server = Server::<String, String>::default()
|
||||||
.incoming(listener)
|
.incoming(listener)
|
||||||
.take(1)
|
.take(1)
|
||||||
.for_each(async move |channel| {
|
.for_each(async move |channel| {
|
||||||
@@ -70,7 +66,7 @@ async fn run() -> io::Result<()> {
|
|||||||
|
|
||||||
let wait = Delay::new(Instant::now() + delay).compat();
|
let wait = Delay::new(Instant::now() + delay).compat();
|
||||||
async move {
|
async move {
|
||||||
await!(wait).unwrap();
|
wait.await.unwrap();
|
||||||
Ok(request)
|
Ok(request)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -79,16 +75,13 @@ async fn run() -> io::Result<()> {
|
|||||||
|
|
||||||
tokio_executor::spawn(server.unit_error().boxed().compat());
|
tokio_executor::spawn(server.unit_error().boxed().compat());
|
||||||
|
|
||||||
let conn = await!(tarpc_bincode_transport::connect(&addr))?;
|
let conn = tarpc_bincode_transport::connect(&addr).await?;
|
||||||
let client = await!(Client::<String, String>::new(
|
let client = client::new::<String, String, _>(client::Config::default(), conn).await?;
|
||||||
client::Config::default(),
|
|
||||||
conn
|
|
||||||
))?;
|
|
||||||
|
|
||||||
// Proxy service
|
// Proxy service
|
||||||
let listener = tarpc_bincode_transport::listen(&"0.0.0.0:0".parse().unwrap())?;
|
let listener = tarpc_bincode_transport::listen(&"0.0.0.0:0".parse().unwrap())?;
|
||||||
let addr = listener.local_addr();
|
let addr = listener.local_addr();
|
||||||
let proxy_server = Server::<String, String>::new(server::Config::default())
|
let proxy_server = Server::<String, String>::default()
|
||||||
.incoming(listener)
|
.incoming(listener)
|
||||||
.take(1)
|
.take(1)
|
||||||
.for_each(move |channel| {
|
.for_each(move |channel| {
|
||||||
@@ -103,7 +96,7 @@ async fn run() -> io::Result<()> {
|
|||||||
let handler = channel.respond_with(move |ctx, request| {
|
let handler = channel.respond_with(move |ctx, request| {
|
||||||
trace!("[{}/{}] Proxying request.", ctx.trace_id(), client_addr);
|
trace!("[{}/{}] Proxying request.", ctx.trace_id(), client_addr);
|
||||||
let mut client = client.clone();
|
let mut client = client.clone();
|
||||||
async move { await!(client.call(ctx, request)) }
|
async move { client.call(ctx, request).await }
|
||||||
});
|
});
|
||||||
tokio_executor::spawn(handler.unit_error().boxed().compat());
|
tokio_executor::spawn(handler.unit_error().boxed().compat());
|
||||||
}
|
}
|
||||||
@@ -115,10 +108,9 @@ async fn run() -> io::Result<()> {
|
|||||||
config.max_in_flight_requests = 10;
|
config.max_in_flight_requests = 10;
|
||||||
config.pending_request_buffer = 10;
|
config.pending_request_buffer = 10;
|
||||||
|
|
||||||
let client = await!(Client::<String, String>::new(
|
let client =
|
||||||
config,
|
client::new::<String, String, _>(config, tarpc_bincode_transport::connect(&addr).await?)
|
||||||
await!(tarpc_bincode_transport::connect(&addr))?
|
.await?;
|
||||||
))?;
|
|
||||||
|
|
||||||
// Make 3 speculative requests, returning only the quickest.
|
// Make 3 speculative requests, returning only the quickest.
|
||||||
let mut clients: Vec<_> = (1..=3u32).map(|_| client.clone()).collect();
|
let mut clients: Vec<_> = (1..=3u32).map(|_| client.clone()).collect();
|
||||||
@@ -130,7 +122,11 @@ async fn run() -> io::Result<()> {
|
|||||||
let response = client.call(ctx, "ping".into());
|
let response = client.call(ctx, "ping".into());
|
||||||
requests.push(response.map(move |r| (trace_id, r)));
|
requests.push(response.map(move |r| (trace_id, r)));
|
||||||
}
|
}
|
||||||
let (fastest_response, _) = await!(stream::futures_unordered(requests).into_future());
|
let (fastest_response, _) = requests
|
||||||
|
.into_iter()
|
||||||
|
.collect::<FuturesUnordered<_>>()
|
||||||
|
.into_future()
|
||||||
|
.await;
|
||||||
let (trace_id, resp) = fastest_response.unwrap();
|
let (trace_id, resp) = fastest_response.unwrap();
|
||||||
info!("[{}] fastest_response = {:?}", trace_id, resp);
|
info!("[{}] fastest_response = {:?}", trace_id, resp);
|
||||||
|
|
||||||
@@ -140,13 +136,8 @@ async fn run() -> io::Result<()> {
|
|||||||
#[test]
|
#[test]
|
||||||
fn cancel_slower() -> io::Result<()> {
|
fn cancel_slower() -> io::Result<()> {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
rpc::init(TokioDefaultSpawner);
|
rpc::init(tokio::executor::DefaultExecutor::current().compat());
|
||||||
|
|
||||||
tokio::run(
|
tokio::run(run().boxed().map_err(|e| panic!(e)).compat());
|
||||||
run()
|
|
||||||
.boxed()
|
|
||||||
.map_err(|e| panic!(e))
|
|
||||||
.compat(),
|
|
||||||
);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,19 +6,15 @@
|
|||||||
|
|
||||||
//! Tests client/server control flow.
|
//! Tests client/server control flow.
|
||||||
|
|
||||||
#![feature(generators, await_macro, async_await, futures_api,)]
|
#![feature(async_await)]
|
||||||
|
|
||||||
use futures::{
|
use futures::{
|
||||||
compat::{Future01CompatExt, TokioDefaultSpawner},
|
compat::{Executor01CompatExt, Future01CompatExt},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
use log::{error, info, trace};
|
use log::{error, info, trace};
|
||||||
use rand::distributions::{Distribution, Normal};
|
use rand::distributions::{Distribution, Normal};
|
||||||
use rpc::{
|
use rpc::{client, context, server::Server};
|
||||||
client::{self, Client},
|
|
||||||
context,
|
|
||||||
server::{self, Server},
|
|
||||||
};
|
|
||||||
use std::{
|
use std::{
|
||||||
io,
|
io,
|
||||||
time::{Duration, Instant, SystemTime},
|
time::{Duration, Instant, SystemTime},
|
||||||
@@ -39,7 +35,7 @@ impl AsDuration for SystemTime {
|
|||||||
async fn run() -> io::Result<()> {
|
async fn run() -> io::Result<()> {
|
||||||
let listener = tarpc_bincode_transport::listen(&"0.0.0.0:0".parse().unwrap())?;
|
let listener = tarpc_bincode_transport::listen(&"0.0.0.0:0".parse().unwrap())?;
|
||||||
let addr = listener.local_addr();
|
let addr = listener.local_addr();
|
||||||
let server = Server::<String, String>::new(server::Config::default())
|
let server = Server::<String, String>::default()
|
||||||
.incoming(listener)
|
.incoming(listener)
|
||||||
.take(1)
|
.take(1)
|
||||||
.for_each(async move |channel| {
|
.for_each(async move |channel| {
|
||||||
@@ -69,7 +65,7 @@ async fn run() -> io::Result<()> {
|
|||||||
|
|
||||||
let sleep = Delay::new(Instant::now() + delay).compat();
|
let sleep = Delay::new(Instant::now() + delay).compat();
|
||||||
async {
|
async {
|
||||||
await!(sleep).unwrap();
|
sleep.await.unwrap();
|
||||||
Ok(request)
|
Ok(request)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -82,8 +78,8 @@ async fn run() -> io::Result<()> {
|
|||||||
config.max_in_flight_requests = 10;
|
config.max_in_flight_requests = 10;
|
||||||
config.pending_request_buffer = 10;
|
config.pending_request_buffer = 10;
|
||||||
|
|
||||||
let conn = await!(tarpc_bincode_transport::connect(&addr))?;
|
let conn = tarpc_bincode_transport::connect(&addr).await?;
|
||||||
let client = await!(Client::<String, String>::new(config, conn))?;
|
let client = client::new::<String, String, _>(config, conn).await?;
|
||||||
|
|
||||||
let clients = (1..=100u32).map(|_| client.clone()).collect::<Vec<_>>();
|
let clients = (1..=100u32).map(|_| client.clone()).collect::<Vec<_>>();
|
||||||
for mut client in clients {
|
for mut client in clients {
|
||||||
@@ -92,11 +88,14 @@ async fn run() -> io::Result<()> {
|
|||||||
async move {
|
async move {
|
||||||
let trace_id = *ctx.trace_id();
|
let trace_id = *ctx.trace_id();
|
||||||
let response = client.call(ctx, "ping".into());
|
let response = client.call(ctx, "ping".into());
|
||||||
match await!(response) {
|
match response.await {
|
||||||
Ok(response) => info!("[{}] response: {}", trace_id, response),
|
Ok(response) => info!("[{}] response: {}", trace_id, response),
|
||||||
Err(e) => error!("[{}] request error: {:?}: {}", trace_id, e.kind(), e),
|
Err(e) => error!("[{}] request error: {:?}: {}", trace_id, e.kind(), e),
|
||||||
}
|
}
|
||||||
}.unit_error().boxed().compat()
|
}
|
||||||
|
.unit_error()
|
||||||
|
.boxed()
|
||||||
|
.compat(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +105,7 @@ async fn run() -> io::Result<()> {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ping_pong() -> io::Result<()> {
|
fn ping_pong() -> io::Result<()> {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
rpc::init(TokioDefaultSpawner);
|
rpc::init(tokio::executor::DefaultExecutor::current().compat());
|
||||||
|
|
||||||
tokio::run(
|
tokio::run(
|
||||||
run()
|
run()
|
||||||
|
|||||||
@@ -1,24 +1,23 @@
|
|||||||
cargo-features = ["rename-dependency"]
|
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "tarpc-example-service"
|
name = "tarpc-example-service"
|
||||||
version = "0.1.0"
|
version = "0.6.0"
|
||||||
authors = ["Tim Kuehn <tikue@google.com>"]
|
authors = ["Tim Kuehn <tikue@google.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
documentation = "https://docs.rs/tarpc-example-service"
|
documentation = "https://docs.rs/tarpc-example-service"
|
||||||
homepage = "https://github.com/google/tarpc"
|
homepage = "https://github.com/google/tarpc"
|
||||||
repository = "https://github.com/google/tarpc"
|
repository = "https://github.com/google/tarpc"
|
||||||
keywords = ["rpc", "network", "server", "api", "microservices", "example"]
|
keywords = ["rpc", "network", "server", "microservices", "example"]
|
||||||
categories = ["asynchronous", "network-programming"]
|
categories = ["asynchronous", "network-programming"]
|
||||||
readme = "../README.md"
|
readme = "../README.md"
|
||||||
description = "An example server built on tarpc."
|
description = "An example server built on tarpc."
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bincode-transport = { package = "tarpc-bincode-transport", version = "0.1", path = "../bincode-transport" }
|
bincode-transport = { package = "tarpc-bincode-transport", version = "0.7", path = "../bincode-transport" }
|
||||||
futures-preview = { version = "0.3.0-alpha.8", features = ["compat", "tokio-compat"] }
|
clap = "2.0"
|
||||||
|
futures-preview = { version = "0.3.0-alpha.16", features = ["compat"] }
|
||||||
serde = { version = "1.0" }
|
serde = { version = "1.0" }
|
||||||
tarpc = { version = "0.13", path = "../tarpc", features = ["serde1"] }
|
tarpc = { version = "0.18", path = "../tarpc", features = ["serde1"] }
|
||||||
tokio = "0.1"
|
tokio = "0.1"
|
||||||
tokio-executor = "0.1"
|
tokio-executor = "0.1"
|
||||||
|
|
||||||
@@ -28,4 +27,8 @@ path = "src/lib.rs"
|
|||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "server"
|
name = "server"
|
||||||
path = "src/main.rs"
|
path = "src/server.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "client"
|
||||||
|
path = "src/client.rs"
|
||||||
|
|||||||
73
example-service/src/client.rs
Normal file
73
example-service/src/client.rs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
// Copyright 2018 Google LLC
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by an MIT-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://opensource.org/licenses/MIT.
|
||||||
|
|
||||||
|
#![feature(arbitrary_self_types, async_await)]
|
||||||
|
|
||||||
|
use clap::{App, Arg};
|
||||||
|
use futures::{compat::Executor01CompatExt, prelude::*};
|
||||||
|
use std::{io, net::SocketAddr};
|
||||||
|
use tarpc::{client, context};
|
||||||
|
|
||||||
|
async fn run(server_addr: SocketAddr, name: String) -> io::Result<()> {
|
||||||
|
let transport = bincode_transport::connect(&server_addr).await?;
|
||||||
|
|
||||||
|
// new_stub is generated by the service! macro. Like Server, it takes a config and any
|
||||||
|
// Transport as input, and returns a Client, also generated by the macro.
|
||||||
|
// by the service mcro.
|
||||||
|
let mut client = service::new_stub(client::Config::default(), transport).await?;
|
||||||
|
|
||||||
|
// The client has an RPC method for each RPC defined in service!. It takes the same args
|
||||||
|
// as defined, with the addition of a Context, which is always the first arg. The Context
|
||||||
|
// specifies a deadline and trace information which can be helpful in debugging requests.
|
||||||
|
let hello = client.hello(context::current(), name).await?;
|
||||||
|
|
||||||
|
println!("{}", hello);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let flags = App::new("Hello Client")
|
||||||
|
.version("0.1")
|
||||||
|
.author("Tim <tikue@google.com>")
|
||||||
|
.about("Say hello!")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("server_addr")
|
||||||
|
.long("server_addr")
|
||||||
|
.value_name("ADDRESS")
|
||||||
|
.help("Sets the server address to connect to.")
|
||||||
|
.required(true)
|
||||||
|
.takes_value(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("name")
|
||||||
|
.short("n")
|
||||||
|
.long("name")
|
||||||
|
.value_name("STRING")
|
||||||
|
.help("Sets the name to say hello to.")
|
||||||
|
.required(true)
|
||||||
|
.takes_value(true),
|
||||||
|
)
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
|
tarpc::init(tokio::executor::DefaultExecutor::current().compat());
|
||||||
|
|
||||||
|
let server_addr = flags.value_of("server_addr").unwrap();
|
||||||
|
let server_addr = server_addr
|
||||||
|
.parse()
|
||||||
|
.unwrap_or_else(|e| panic!(r#"--server_addr value "{}" invalid: {}"#, server_addr, e));
|
||||||
|
|
||||||
|
let name = flags.value_of("name").unwrap();
|
||||||
|
|
||||||
|
tarpc::init(tokio::executor::DefaultExecutor::current().compat());
|
||||||
|
|
||||||
|
tokio::run(
|
||||||
|
run(server_addr, name.into())
|
||||||
|
.map_err(|e| eprintln!("Oh no: {}", e))
|
||||||
|
.boxed()
|
||||||
|
.compat(),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -4,14 +4,7 @@
|
|||||||
// license that can be found in the LICENSE file or at
|
// license that can be found in the LICENSE file or at
|
||||||
// https://opensource.org/licenses/MIT.
|
// https://opensource.org/licenses/MIT.
|
||||||
|
|
||||||
#![feature(
|
#![feature(arbitrary_self_types, async_await, proc_macro_hygiene)]
|
||||||
futures_api,
|
|
||||||
pin,
|
|
||||||
arbitrary_self_types,
|
|
||||||
await_macro,
|
|
||||||
async_await,
|
|
||||||
proc_macro_hygiene,
|
|
||||||
)]
|
|
||||||
|
|
||||||
// This is the service definition. It looks a lot like a trait definition.
|
// This is the service definition. It looks a lot like a trait definition.
|
||||||
// It defines one RPC, hello, which takes one arg, name, and returns a String.
|
// It defines one RPC, hello, which takes one arg, name, and returns a String.
|
||||||
|
|||||||
@@ -1,85 +0,0 @@
|
|||||||
// Copyright 2018 Google LLC
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by an MIT-style
|
|
||||||
// license that can be found in the LICENSE file or at
|
|
||||||
// https://opensource.org/licenses/MIT.
|
|
||||||
|
|
||||||
#![feature(
|
|
||||||
futures_api,
|
|
||||||
pin,
|
|
||||||
arbitrary_self_types,
|
|
||||||
await_macro,
|
|
||||||
async_await,
|
|
||||||
)]
|
|
||||||
|
|
||||||
use futures::{
|
|
||||||
compat::TokioDefaultSpawner,
|
|
||||||
future::{self, Ready},
|
|
||||||
prelude::*,
|
|
||||||
};
|
|
||||||
use tarpc::{
|
|
||||||
client, context,
|
|
||||||
server::{self, Handler, Server},
|
|
||||||
};
|
|
||||||
use std::io;
|
|
||||||
|
|
||||||
// This is the type that implements the generated Service trait. It is the business logic
|
|
||||||
// and is used to start the server.
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct HelloServer;
|
|
||||||
|
|
||||||
impl service::Service for HelloServer {
|
|
||||||
// Each defined rpc generates two items in the trait, a fn that serves the RPC, and
|
|
||||||
// an associated type representing the future output by the fn.
|
|
||||||
|
|
||||||
type HelloFut = Ready<String>;
|
|
||||||
|
|
||||||
fn hello(&self, _: context::Context, name: String) -> Self::HelloFut {
|
|
||||||
future::ready(format!("Hello, {}!", name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run() -> io::Result<()> {
|
|
||||||
// bincode_transport is provided by the associated crate bincode-transport. It makes it easy
|
|
||||||
// to start up a serde-powered bincode serialization strategy over TCP.
|
|
||||||
let transport = bincode_transport::listen(&"0.0.0.0:0".parse().unwrap())?;
|
|
||||||
let addr = transport.local_addr();
|
|
||||||
|
|
||||||
// The server is configured with the defaults.
|
|
||||||
let server = Server::new(server::Config::default())
|
|
||||||
// Server can listen on any type that implements the Transport trait.
|
|
||||||
.incoming(transport)
|
|
||||||
// Close the stream after the client connects
|
|
||||||
.take(1)
|
|
||||||
// serve is generated by the service! macro. It takes as input any type implementing
|
|
||||||
// the generated Service trait.
|
|
||||||
.respond_with(service::serve(HelloServer));
|
|
||||||
|
|
||||||
tokio_executor::spawn(server.unit_error().boxed().compat());
|
|
||||||
|
|
||||||
let transport = await!(bincode_transport::connect(&addr))?;
|
|
||||||
|
|
||||||
// new_stub is generated by the service! macro. Like Server, it takes a config and any
|
|
||||||
// Transport as input, and returns a Client, also generated by the macro.
|
|
||||||
// by the service mcro.
|
|
||||||
let mut client = await!(service::new_stub(client::Config::default(), transport))?;
|
|
||||||
|
|
||||||
// The client has an RPC method for each RPC defined in service!. It takes the same args
|
|
||||||
// as defined, with the addition of a Context, which is always the first arg. The Context
|
|
||||||
// specifies a deadline and trace information which can be helpful in debugging requests.
|
|
||||||
let hello = await!(client.hello(context::current(), "Stim".to_string()))?;
|
|
||||||
|
|
||||||
println!("{}", hello);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
tarpc::init(TokioDefaultSpawner);
|
|
||||||
|
|
||||||
tokio::run(run()
|
|
||||||
.map_err(|e| eprintln!("Oh no: {}", e))
|
|
||||||
.boxed()
|
|
||||||
.compat()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
84
example-service/src/server.rs
Normal file
84
example-service/src/server.rs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
// Copyright 2018 Google LLC
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by an MIT-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://opensource.org/licenses/MIT.
|
||||||
|
|
||||||
|
#![feature(arbitrary_self_types, async_await)]
|
||||||
|
|
||||||
|
use clap::{App, Arg};
|
||||||
|
use futures::{
|
||||||
|
compat::Executor01CompatExt,
|
||||||
|
future::{self, Ready},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
use std::{io, net::SocketAddr};
|
||||||
|
use tarpc::{
|
||||||
|
context,
|
||||||
|
server::{Handler, Server},
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is the type that implements the generated Service trait. It is the business logic
|
||||||
|
// and is used to start the server.
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct HelloServer;
|
||||||
|
|
||||||
|
impl service::Service for HelloServer {
|
||||||
|
// Each defined rpc generates two items in the trait, a fn that serves the RPC, and
|
||||||
|
// an associated type representing the future output by the fn.
|
||||||
|
|
||||||
|
type HelloFut = Ready<String>;
|
||||||
|
|
||||||
|
fn hello(self, _: context::Context, name: String) -> Self::HelloFut {
|
||||||
|
future::ready(format!("Hello, {}!", name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run(server_addr: SocketAddr) -> io::Result<()> {
|
||||||
|
// bincode_transport is provided by the associated crate bincode-transport. It makes it easy
|
||||||
|
// to start up a serde-powered bincode serialization strategy over TCP.
|
||||||
|
let transport = bincode_transport::listen(&server_addr)?;
|
||||||
|
|
||||||
|
// The server is configured with the defaults.
|
||||||
|
let server = Server::default()
|
||||||
|
// Server can listen on any type that implements the Transport trait.
|
||||||
|
.incoming(transport)
|
||||||
|
// serve is generated by the service! macro. It takes as input any type implementing
|
||||||
|
// the generated Service trait.
|
||||||
|
.respond_with(service::serve(HelloServer));
|
||||||
|
|
||||||
|
server.await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let flags = App::new("Hello Server")
|
||||||
|
.version("0.1")
|
||||||
|
.author("Tim <tikue@google.com>")
|
||||||
|
.about("Say hello!")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("port")
|
||||||
|
.short("p")
|
||||||
|
.long("port")
|
||||||
|
.value_name("NUMBER")
|
||||||
|
.help("Sets the port number to listen on")
|
||||||
|
.required(true)
|
||||||
|
.takes_value(true),
|
||||||
|
)
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
|
let port = flags.value_of("port").unwrap();
|
||||||
|
let port = port
|
||||||
|
.parse()
|
||||||
|
.unwrap_or_else(|e| panic!(r#"--port value "{}" invalid: {}"#, port, e));
|
||||||
|
|
||||||
|
tarpc::init(tokio::executor::DefaultExecutor::current().compat());
|
||||||
|
|
||||||
|
tokio::run(
|
||||||
|
run(([0, 0, 0, 0], port).into())
|
||||||
|
.map_err(|e| eprintln!("Oh no: {}", e))
|
||||||
|
.boxed()
|
||||||
|
.compat(),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -96,7 +96,7 @@ do
|
|||||||
diff="$diff$(cargo fmt -- --skip-children --write-mode=diff $file)"
|
diff="$diff$(cargo fmt -- --skip-children --write-mode=diff $file)"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
if grep --quiet "^Diff at line" <<< "$diff"; then
|
if grep --quiet "^[-+]" <<< "$diff"; then
|
||||||
FMTRESULT=1
|
FMTRESULT=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tarpc-plugins"
|
name = "tarpc-plugins"
|
||||||
version = "0.5.0"
|
version = "0.5.1"
|
||||||
authors = ["Adam Wright <adam.austin.wright@gmail.com>", "Tim Kuehn <timothy.j.kuehn@gmail.com>"]
|
authors = ["Adam Wright <adam.austin.wright@gmail.com>", "Tim Kuehn <timothy.j.kuehn@gmail.com>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
documentation = "https://docs.rs/tarpc-plugins"
|
documentation = "https://docs.rs/tarpc-plugins"
|
||||||
@@ -15,7 +15,7 @@ description = "Proc macros for tarpc."
|
|||||||
travis-ci = { repository = "google/tarpc" }
|
travis-ci = { repository = "google/tarpc" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
itertools = "0.7"
|
itertools = "0.8"
|
||||||
syn = { version = "0.15", features = ["full", "extra-traits"] }
|
syn = { version = "0.15", features = ["full", "extra-traits"] }
|
||||||
quote = "0.6"
|
quote = "0.6"
|
||||||
proc-macro2 = "0.4"
|
proc-macro2 = "0.4"
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
edition = "Edition2018"
|
edition = "2018"
|
||||||
|
|||||||
@@ -4,31 +4,35 @@
|
|||||||
// license that can be found in the LICENSE file or at
|
// license that can be found in the LICENSE file or at
|
||||||
// https://opensource.org/licenses/MIT.
|
// https://opensource.org/licenses/MIT.
|
||||||
|
|
||||||
|
extern crate itertools;
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
extern crate proc_macro2;
|
extern crate proc_macro2;
|
||||||
extern crate syn;
|
|
||||||
extern crate itertools;
|
|
||||||
extern crate quote;
|
extern crate quote;
|
||||||
|
extern crate syn;
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use quote::ToTokens;
|
|
||||||
use syn::{Ident, TraitItemType, TypePath, parse};
|
|
||||||
use proc_macro2::Span;
|
use proc_macro2::Span;
|
||||||
|
use quote::ToTokens;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use syn::{parse, Ident, TraitItemType, TypePath};
|
||||||
|
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn snake_to_camel(input: TokenStream) -> TokenStream {
|
pub fn snake_to_camel(input: TokenStream) -> TokenStream {
|
||||||
let i = input.clone();
|
let i = input.clone();
|
||||||
let mut assoc_type = parse::<TraitItemType>(input).unwrap_or_else(|_| panic!("Could not parse trait item from:\n{}", i));
|
let mut assoc_type = parse::<TraitItemType>(input)
|
||||||
|
.unwrap_or_else(|_| panic!("Could not parse trait item from:\n{}", i));
|
||||||
|
|
||||||
let old_ident = convert(&mut assoc_type.ident);
|
let old_ident = convert(&mut assoc_type.ident);
|
||||||
|
|
||||||
for mut attr in &mut assoc_type.attrs {
|
for mut attr in &mut assoc_type.attrs {
|
||||||
if let Some(pair) = attr.path.segments.first() {
|
if let Some(pair) = attr.path.segments.first() {
|
||||||
if pair.value().ident == "doc" {
|
if pair.value().ident == "doc" {
|
||||||
attr.tts = proc_macro2::TokenStream::from_str(&attr.tts.to_string().replace("{}", &old_ident)).unwrap();
|
attr.tts = proc_macro2::TokenStream::from_str(
|
||||||
|
&attr.tts.to_string().replace("{}", &old_ident),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -41,12 +45,7 @@ pub fn ty_snake_to_camel(input: TokenStream) -> TokenStream {
|
|||||||
let mut path = parse::<TypePath>(input).unwrap();
|
let mut path = parse::<TypePath>(input).unwrap();
|
||||||
|
|
||||||
// Only capitalize the final segment
|
// Only capitalize the final segment
|
||||||
convert(&mut path.path
|
convert(&mut path.path.segments.last_mut().unwrap().into_value().ident);
|
||||||
.segments
|
|
||||||
.last_mut()
|
|
||||||
.unwrap()
|
|
||||||
.into_value()
|
|
||||||
.ident);
|
|
||||||
|
|
||||||
path.into_token_stream().into()
|
path.into_token_stream().into()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
cargo-features = ["rename-dependency"]
|
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "tarpc-lib"
|
name = "tarpc-lib"
|
||||||
version = "0.1.0"
|
version = "0.6.0"
|
||||||
authors = ["Tim Kuehn <tikue@google.com>"]
|
authors = ["Tim Kuehn <tikue@google.com>"]
|
||||||
edition = '2018'
|
edition = '2018'
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
@@ -20,19 +18,16 @@ serde1 = ["trace/serde", "serde", "serde/derive"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
fnv = "1.0"
|
fnv = "1.0"
|
||||||
|
futures-preview = { version = "0.3.0-alpha.16", features = ["compat"] }
|
||||||
humantime = "1.0"
|
humantime = "1.0"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
pin-utils = "0.1.0-alpha.2"
|
pin-utils = "0.1.0-alpha.4"
|
||||||
rand = "0.5"
|
rand = "0.6"
|
||||||
tokio-timer = "0.2"
|
tokio-timer = "0.2"
|
||||||
trace = { package = "tarpc-trace", version = "0.1", path = "../trace" }
|
trace = { package = "tarpc-trace", version = "0.2", path = "../trace" }
|
||||||
serde = { optional = true, version = "1.0" }
|
serde = { optional = true, version = "1.0" }
|
||||||
|
|
||||||
[target.'cfg(not(test))'.dependencies]
|
|
||||||
futures-preview = { version = "0.3.0-alpha.8", features = ["compat"] }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
futures-preview = { version = "0.3.0-alpha.8", features = ["compat", "tokio-compat"] }
|
futures-test-preview = { version = "0.3.0-alpha.16" }
|
||||||
futures-test-preview = { version = "0.3.0-alpha.8" }
|
env_logger = "0.6"
|
||||||
env_logger = "0.5"
|
|
||||||
tokio = "0.1"
|
tokio = "0.1"
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
edition = "Edition2018"
|
edition = "2018"
|
||||||
|
|||||||
@@ -7,22 +7,23 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
context,
|
context,
|
||||||
util::{deadline_compat, AsDuration, Compact},
|
util::{deadline_compat, AsDuration, Compact},
|
||||||
ClientMessage, ClientMessageKind, Request, Response, Transport,
|
ClientMessage, ClientMessageKind, PollIo, Request, Response, Transport,
|
||||||
};
|
};
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use futures::{
|
use futures::{
|
||||||
Poll,
|
|
||||||
channel::{mpsc, oneshot},
|
channel::{mpsc, oneshot},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
ready,
|
ready,
|
||||||
stream::Fuse,
|
stream::Fuse,
|
||||||
task::LocalWaker,
|
task::Context,
|
||||||
|
Poll,
|
||||||
};
|
};
|
||||||
use humantime::format_rfc3339;
|
use humantime::format_rfc3339;
|
||||||
use log::{debug, error, info, trace};
|
use log::{debug, error, info, trace};
|
||||||
use pin_utils::unsafe_pinned;
|
use pin_utils::{unsafe_pinned, unsafe_unpinned};
|
||||||
use std::{
|
use std::{
|
||||||
io,
|
io,
|
||||||
|
marker::{self, Unpin},
|
||||||
net::SocketAddr,
|
net::SocketAddr,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
sync::{
|
sync::{
|
||||||
@@ -37,7 +38,7 @@ use super::Config;
|
|||||||
|
|
||||||
/// Handles communication from the client to request dispatch.
|
/// Handles communication from the client to request dispatch.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Channel<Req, Resp> {
|
pub struct Channel<Req, Resp> {
|
||||||
to_dispatch: mpsc::Sender<DispatchRequest<Req, Resp>>,
|
to_dispatch: mpsc::Sender<DispatchRequest<Req, Resp>>,
|
||||||
/// Channel to send a cancel message to the dispatcher.
|
/// Channel to send a cancel message to the dispatcher.
|
||||||
cancellation: RequestCancellation,
|
cancellation: RequestCancellation,
|
||||||
@@ -57,14 +58,63 @@ impl<Req, Resp> Clone for Channel<Req, Resp> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A future returned by [`Channel::send`] that resolves to a server response.
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[must_use = "futures do nothing unless polled"]
|
||||||
|
struct Send<'a, Req, Resp> {
|
||||||
|
fut: MapOkDispatchResponse<SendMapErrConnectionReset<'a, Req, Resp>, Resp>,
|
||||||
|
}
|
||||||
|
|
||||||
|
type SendMapErrConnectionReset<'a, Req, Resp> = MapErrConnectionReset<
|
||||||
|
futures::sink::Send<'a, mpsc::Sender<DispatchRequest<Req, Resp>>, DispatchRequest<Req, Resp>>,
|
||||||
|
>;
|
||||||
|
|
||||||
|
impl<'a, Req, Resp> Send<'a, Req, Resp> {
|
||||||
|
unsafe_pinned!(
|
||||||
|
fut: MapOkDispatchResponse<
|
||||||
|
MapErrConnectionReset<
|
||||||
|
futures::sink::Send<
|
||||||
|
'a,
|
||||||
|
mpsc::Sender<DispatchRequest<Req, Resp>>,
|
||||||
|
DispatchRequest<Req, Resp>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
Resp,
|
||||||
|
>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Req, Resp> Future for Send<'a, Req, Resp> {
|
||||||
|
type Output = io::Result<DispatchResponse<Resp>>;
|
||||||
|
|
||||||
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
self.as_mut().fut().poll(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A future returned by [`Channel::call`] that resolves to a server response.
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[must_use = "futures do nothing unless polled"]
|
||||||
|
pub struct Call<'a, Req, Resp> {
|
||||||
|
fut: AndThenIdent<Send<'a, Req, Resp>, DispatchResponse<Resp>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Req, Resp> Call<'a, Req, Resp> {
|
||||||
|
unsafe_pinned!(fut: AndThenIdent<Send<'a, Req, Resp>, DispatchResponse<Resp>>);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Req, Resp> Future for Call<'a, Req, Resp> {
|
||||||
|
type Output = io::Result<Resp>;
|
||||||
|
|
||||||
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
self.as_mut().fut().poll(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<Req, Resp> Channel<Req, Resp> {
|
impl<Req, Resp> Channel<Req, Resp> {
|
||||||
/// Sends a request to the dispatch task to forward to the server, returning a [`Future`] that
|
/// Sends a request to the dispatch task to forward to the server, returning a [`Future`] that
|
||||||
/// resolves when the request is sent (not when the response is received).
|
/// resolves when the request is sent (not when the response is received).
|
||||||
pub(crate) async fn send(
|
fn send(&mut self, mut ctx: context::Context, request: Req) -> Send<Req, Resp> {
|
||||||
&mut self,
|
|
||||||
mut ctx: context::Context,
|
|
||||||
request: Req,
|
|
||||||
) -> io::Result<DispatchResponse<Resp>> {
|
|
||||||
// Convert the context to the call context.
|
// Convert the context to the call context.
|
||||||
ctx.trace_context.parent_id = Some(ctx.trace_context.span_id);
|
ctx.trace_context.parent_id = Some(ctx.trace_context.span_id);
|
||||||
ctx.trace_context.span_id = SpanId::random(&mut rand::thread_rng());
|
ctx.trace_context.span_id = SpanId::random(&mut rand::thread_rng());
|
||||||
@@ -82,38 +132,40 @@ impl<Req, Resp> Channel<Req, Resp> {
|
|||||||
let (response_completion, response) = oneshot::channel();
|
let (response_completion, response) = oneshot::channel();
|
||||||
let cancellation = self.cancellation.clone();
|
let cancellation = self.cancellation.clone();
|
||||||
let request_id = self.next_request_id.fetch_add(1, Ordering::Relaxed);
|
let request_id = self.next_request_id.fetch_add(1, Ordering::Relaxed);
|
||||||
await!(self.to_dispatch.send(DispatchRequest {
|
let server_addr = self.server_addr;
|
||||||
ctx,
|
Send {
|
||||||
request_id,
|
fut: MapOkDispatchResponse::new(
|
||||||
request,
|
MapErrConnectionReset::new(self.to_dispatch.send(DispatchRequest {
|
||||||
response_completion,
|
ctx,
|
||||||
})).map_err(|_| io::Error::from(io::ErrorKind::ConnectionReset))?;
|
request_id,
|
||||||
Ok(DispatchResponse {
|
request,
|
||||||
response: deadline_compat::Deadline::new(response, deadline),
|
response_completion,
|
||||||
complete: false,
|
})),
|
||||||
request_id,
|
DispatchResponse {
|
||||||
cancellation,
|
response: deadline_compat::Deadline::new(response, deadline),
|
||||||
ctx,
|
complete: false,
|
||||||
server_addr: self.server_addr,
|
request_id,
|
||||||
})
|
cancellation,
|
||||||
|
ctx,
|
||||||
|
server_addr,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a request to the dispatch task to forward to the server, returning a [`Future`] that
|
/// Sends a request to the dispatch task to forward to the server, returning a [`Future`] that
|
||||||
/// resolves to the response.
|
/// resolves to the response.
|
||||||
pub(crate) async fn call(
|
pub fn call(&mut self, context: context::Context, request: Req) -> Call<Req, Resp> {
|
||||||
&mut self,
|
Call {
|
||||||
context: context::Context,
|
fut: AndThenIdent::new(self.send(context, request)),
|
||||||
request: Req,
|
}
|
||||||
) -> io::Result<Resp> {
|
|
||||||
let response_future = await!(self.send(context, request))?;
|
|
||||||
await!(response_future)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A server response that is completed by request dispatch when the corresponding response
|
/// A server response that is completed by request dispatch when the corresponding response
|
||||||
/// arrives off the wire.
|
/// arrives off the wire.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DispatchResponse<Resp> {
|
struct DispatchResponse<Resp> {
|
||||||
response: deadline_compat::Deadline<oneshot::Receiver<Response<Resp>>>,
|
response: deadline_compat::Deadline<oneshot::Receiver<Response<Resp>>>,
|
||||||
ctx: context::Context,
|
ctx: context::Context,
|
||||||
complete: bool,
|
complete: bool,
|
||||||
@@ -130,16 +182,16 @@ impl<Resp> DispatchResponse<Resp> {
|
|||||||
impl<Resp> Future for DispatchResponse<Resp> {
|
impl<Resp> Future for DispatchResponse<Resp> {
|
||||||
type Output = io::Result<Resp>;
|
type Output = io::Result<Resp>;
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, waker: &LocalWaker) -> Poll<io::Result<Resp>> {
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<Resp>> {
|
||||||
let resp = ready!(self.response.poll_unpin(waker));
|
let resp = ready!(self.response.poll_unpin(cx));
|
||||||
|
|
||||||
self.complete = true;
|
self.complete = true;
|
||||||
|
|
||||||
Poll::Ready(match resp {
|
Poll::Ready(match resp {
|
||||||
Ok(resp) => Ok(resp.message?),
|
Ok(resp) => Ok(resp.message?),
|
||||||
Err(e) => Err({
|
Err(e) => Err({
|
||||||
let trace_id = *self.ctx().trace_id();
|
let trace_id = *self.as_mut().ctx().trace_id();
|
||||||
let server_addr = *self.server_addr();
|
let server_addr = *self.as_mut().server_addr();
|
||||||
|
|
||||||
if e.is_elapsed() {
|
if e.is_elapsed() {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
@@ -205,12 +257,13 @@ pub async fn spawn<Req, Resp, C>(
|
|||||||
server_addr: SocketAddr,
|
server_addr: SocketAddr,
|
||||||
) -> io::Result<Channel<Req, Resp>>
|
) -> io::Result<Channel<Req, Resp>>
|
||||||
where
|
where
|
||||||
Req: Send,
|
Req: marker::Send + 'static,
|
||||||
Resp: Send,
|
Resp: marker::Send + 'static,
|
||||||
C: Transport<Item = Response<Resp>, SinkItem = ClientMessage<Req>> + Send,
|
C: Transport<Item = Response<Resp>, SinkItem = ClientMessage<Req>> + marker::Send + 'static,
|
||||||
{
|
{
|
||||||
let (to_dispatch, pending_requests) = mpsc::channel(config.pending_request_buffer);
|
let (to_dispatch, pending_requests) = mpsc::channel(config.pending_request_buffer);
|
||||||
let (cancellation, canceled_requests) = cancellations();
|
let (cancellation, canceled_requests) = cancellations();
|
||||||
|
let canceled_requests = canceled_requests.fuse();
|
||||||
|
|
||||||
crate::spawn(
|
crate::spawn(
|
||||||
RequestDispatch {
|
RequestDispatch {
|
||||||
@@ -220,16 +273,18 @@ where
|
|||||||
transport: transport.fuse(),
|
transport: transport.fuse(),
|
||||||
in_flight_requests: FnvHashMap::default(),
|
in_flight_requests: FnvHashMap::default(),
|
||||||
pending_requests: pending_requests.fuse(),
|
pending_requests: pending_requests.fuse(),
|
||||||
}.unwrap_or_else(move |e| error!("[{}] Connection broken: {}", server_addr, e))
|
}
|
||||||
).map_err(|e| {
|
.unwrap_or_else(move |e| error!("[{}] Connection broken: {}", server_addr, e)),
|
||||||
io::Error::new(
|
)
|
||||||
io::ErrorKind::Other,
|
.map_err(|e| {
|
||||||
format!(
|
io::Error::new(
|
||||||
"Could not spawn client dispatch task. Is shutdown: {}",
|
io::ErrorKind::Other,
|
||||||
e.is_shutdown()
|
format!(
|
||||||
),
|
"Could not spawn client dispatch task. Is shutdown: {}",
|
||||||
)
|
e.is_shutdown()
|
||||||
})?;
|
),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(Channel {
|
Ok(Channel {
|
||||||
to_dispatch,
|
to_dispatch,
|
||||||
@@ -247,7 +302,7 @@ struct RequestDispatch<Req, Resp, C> {
|
|||||||
/// Requests waiting to be written to the wire.
|
/// Requests waiting to be written to the wire.
|
||||||
pending_requests: Fuse<mpsc::Receiver<DispatchRequest<Req, Resp>>>,
|
pending_requests: Fuse<mpsc::Receiver<DispatchRequest<Req, Resp>>>,
|
||||||
/// Requests that were dropped.
|
/// Requests that were dropped.
|
||||||
canceled_requests: CanceledRequests,
|
canceled_requests: Fuse<CanceledRequests>,
|
||||||
/// Requests already written to the wire that haven't yet received responses.
|
/// Requests already written to the wire that haven't yet received responses.
|
||||||
in_flight_requests: FnvHashMap<u64, InFlightData<Resp>>,
|
in_flight_requests: FnvHashMap<u64, InFlightData<Resp>>,
|
||||||
/// Configures limits to prevent unlimited resource usage.
|
/// Configures limits to prevent unlimited resource usage.
|
||||||
@@ -258,36 +313,36 @@ struct RequestDispatch<Req, Resp, C> {
|
|||||||
|
|
||||||
impl<Req, Resp, C> RequestDispatch<Req, Resp, C>
|
impl<Req, Resp, C> RequestDispatch<Req, Resp, C>
|
||||||
where
|
where
|
||||||
Req: Send,
|
Req: marker::Send,
|
||||||
Resp: Send,
|
Resp: marker::Send,
|
||||||
C: Transport<Item = Response<Resp>, SinkItem = ClientMessage<Req>>,
|
C: Transport<Item = Response<Resp>, SinkItem = ClientMessage<Req>>,
|
||||||
{
|
{
|
||||||
unsafe_pinned!(server_addr: SocketAddr);
|
unsafe_pinned!(server_addr: SocketAddr);
|
||||||
unsafe_pinned!(in_flight_requests: FnvHashMap<u64, InFlightData<Resp>>);
|
unsafe_pinned!(in_flight_requests: FnvHashMap<u64, InFlightData<Resp>>);
|
||||||
unsafe_pinned!(canceled_requests: CanceledRequests);
|
unsafe_pinned!(canceled_requests: Fuse<CanceledRequests>);
|
||||||
unsafe_pinned!(pending_requests: Fuse<mpsc::Receiver<DispatchRequest<Req, Resp>>>);
|
unsafe_pinned!(pending_requests: Fuse<mpsc::Receiver<DispatchRequest<Req, Resp>>>);
|
||||||
unsafe_pinned!(transport: Fuse<C>);
|
unsafe_pinned!(transport: Fuse<C>);
|
||||||
|
|
||||||
fn pump_read(self: &mut Pin<&mut Self>, waker: &LocalWaker) -> Poll<Option<io::Result<()>>> {
|
fn pump_read(self: &mut Pin<&mut Self>, cx: &mut Context<'_>) -> PollIo<()> {
|
||||||
Poll::Ready(match ready!(self.transport().poll_next(waker)?) {
|
Poll::Ready(match ready!(self.as_mut().transport().poll_next(cx)?) {
|
||||||
Some(response) => {
|
Some(response) => {
|
||||||
self.complete(response);
|
self.complete(response);
|
||||||
Some(Ok(()))
|
Some(Ok(()))
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
trace!("[{}] read half closed", self.server_addr());
|
trace!("[{}] read half closed", self.as_mut().server_addr());
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pump_write(self: &mut Pin<&mut Self>, waker: &LocalWaker) -> Poll<Option<io::Result<()>>> {
|
fn pump_write(self: &mut Pin<&mut Self>, cx: &mut Context<'_>) -> PollIo<()> {
|
||||||
enum ReceiverStatus {
|
enum ReceiverStatus {
|
||||||
NotReady,
|
NotReady,
|
||||||
Closed,
|
Closed,
|
||||||
}
|
}
|
||||||
|
|
||||||
let pending_requests_status = match self.poll_next_request(waker)? {
|
let pending_requests_status = match self.poll_next_request(cx)? {
|
||||||
Poll::Ready(Some(dispatch_request)) => {
|
Poll::Ready(Some(dispatch_request)) => {
|
||||||
self.write_request(dispatch_request)?;
|
self.write_request(dispatch_request)?;
|
||||||
return Poll::Ready(Some(Ok(())));
|
return Poll::Ready(Some(Ok(())));
|
||||||
@@ -296,7 +351,7 @@ where
|
|||||||
Poll::Pending => ReceiverStatus::NotReady,
|
Poll::Pending => ReceiverStatus::NotReady,
|
||||||
};
|
};
|
||||||
|
|
||||||
let canceled_requests_status = match self.poll_next_cancellation(waker)? {
|
let canceled_requests_status = match self.poll_next_cancellation(cx)? {
|
||||||
Poll::Ready(Some((context, request_id))) => {
|
Poll::Ready(Some((context, request_id))) => {
|
||||||
self.write_cancel(context, request_id)?;
|
self.write_cancel(context, request_id)?;
|
||||||
return Poll::Ready(Some(Ok(())));
|
return Poll::Ready(Some(Ok(())));
|
||||||
@@ -307,12 +362,12 @@ where
|
|||||||
|
|
||||||
match (pending_requests_status, canceled_requests_status) {
|
match (pending_requests_status, canceled_requests_status) {
|
||||||
(ReceiverStatus::Closed, ReceiverStatus::Closed) => {
|
(ReceiverStatus::Closed, ReceiverStatus::Closed) => {
|
||||||
ready!(self.transport().poll_flush(waker)?);
|
ready!(self.as_mut().transport().poll_flush(cx)?);
|
||||||
Poll::Ready(None)
|
Poll::Ready(None)
|
||||||
}
|
}
|
||||||
(ReceiverStatus::NotReady, _) | (_, ReceiverStatus::NotReady) => {
|
(ReceiverStatus::NotReady, _) | (_, ReceiverStatus::NotReady) => {
|
||||||
// No more messages to process, so flush any messages buffered in the transport.
|
// No more messages to process, so flush any messages buffered in the transport.
|
||||||
ready!(self.transport().poll_flush(waker)?);
|
ready!(self.as_mut().transport().poll_flush(cx)?);
|
||||||
|
|
||||||
// Even if we fully-flush, we return Pending, because we have no more requests
|
// Even if we fully-flush, we return Pending, because we have no more requests
|
||||||
// or cancellations right now.
|
// or cancellations right now.
|
||||||
@@ -324,12 +379,12 @@ where
|
|||||||
/// Yields the next pending request, if one is ready to be sent.
|
/// Yields the next pending request, if one is ready to be sent.
|
||||||
fn poll_next_request(
|
fn poll_next_request(
|
||||||
self: &mut Pin<&mut Self>,
|
self: &mut Pin<&mut Self>,
|
||||||
waker: &LocalWaker,
|
cx: &mut Context<'_>,
|
||||||
) -> Poll<Option<io::Result<DispatchRequest<Req, Resp>>>> {
|
) -> PollIo<DispatchRequest<Req, Resp>> {
|
||||||
if self.in_flight_requests().len() >= self.config.max_in_flight_requests {
|
if self.as_mut().in_flight_requests().len() >= self.config.max_in_flight_requests {
|
||||||
info!(
|
info!(
|
||||||
"At in-flight request capacity ({}/{}).",
|
"At in-flight request capacity ({}/{}).",
|
||||||
self.in_flight_requests().len(),
|
self.as_mut().in_flight_requests().len(),
|
||||||
self.config.max_in_flight_requests
|
self.config.max_in_flight_requests
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -338,13 +393,13 @@ where
|
|||||||
return Poll::Pending;
|
return Poll::Pending;
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Poll::Pending = self.transport().poll_ready(waker)? {
|
while let Poll::Pending = self.as_mut().transport().poll_ready(cx)? {
|
||||||
// We can't yield a request-to-be-sent before the transport is capable of buffering it.
|
// We can't yield a request-to-be-sent before the transport is capable of buffering it.
|
||||||
ready!(self.transport().poll_flush(waker)?);
|
ready!(self.as_mut().transport().poll_flush(cx)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match ready!(self.pending_requests().poll_next_unpin(waker)) {
|
match ready!(self.as_mut().pending_requests().poll_next_unpin(cx)) {
|
||||||
Some(request) => {
|
Some(request) => {
|
||||||
if request.response_completion.is_canceled() {
|
if request.response_completion.is_canceled() {
|
||||||
trace!(
|
trace!(
|
||||||
@@ -357,7 +412,7 @@ where
|
|||||||
return Poll::Ready(Some(Ok(request)));
|
return Poll::Ready(Some(Ok(request)));
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
trace!("[{}] pending_requests closed", self.server_addr());
|
trace!("[{}] pending_requests closed", self.as_mut().server_addr());
|
||||||
return Poll::Ready(None);
|
return Poll::Ready(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -367,29 +422,35 @@ where
|
|||||||
/// Yields the next pending cancellation, and, if one is ready, cancels the associated request.
|
/// Yields the next pending cancellation, and, if one is ready, cancels the associated request.
|
||||||
fn poll_next_cancellation(
|
fn poll_next_cancellation(
|
||||||
self: &mut Pin<&mut Self>,
|
self: &mut Pin<&mut Self>,
|
||||||
waker: &LocalWaker,
|
cx: &mut Context<'_>,
|
||||||
) -> Poll<Option<io::Result<(context::Context, u64)>>> {
|
) -> PollIo<(context::Context, u64)> {
|
||||||
while let Poll::Pending = self.transport().poll_ready(waker)? {
|
while let Poll::Pending = self.as_mut().transport().poll_ready(cx)? {
|
||||||
ready!(self.transport().poll_flush(waker)?);
|
ready!(self.as_mut().transport().poll_flush(cx)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match ready!(self.canceled_requests().poll_next_unpin(waker)) {
|
let cancellation = self.as_mut().canceled_requests().poll_next_unpin(cx);
|
||||||
|
match ready!(cancellation) {
|
||||||
Some(request_id) => {
|
Some(request_id) => {
|
||||||
if let Some(in_flight_data) = self.in_flight_requests().remove(&request_id) {
|
if let Some(in_flight_data) =
|
||||||
self.in_flight_requests().compact(0.1);
|
self.as_mut().in_flight_requests().remove(&request_id)
|
||||||
|
{
|
||||||
|
self.as_mut().in_flight_requests().compact(0.1);
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"[{}/{}] Removed request.",
|
"[{}/{}] Removed request.",
|
||||||
in_flight_data.ctx.trace_id(),
|
in_flight_data.ctx.trace_id(),
|
||||||
self.server_addr()
|
self.as_mut().server_addr()
|
||||||
);
|
);
|
||||||
|
|
||||||
return Poll::Ready(Some(Ok((in_flight_data.ctx, request_id))));
|
return Poll::Ready(Some(Ok((in_flight_data.ctx, request_id))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
trace!("[{}] canceled_requests closed.", self.server_addr());
|
trace!(
|
||||||
|
"[{}] canceled_requests closed.",
|
||||||
|
self.as_mut().server_addr()
|
||||||
|
);
|
||||||
return Poll::Ready(None);
|
return Poll::Ready(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -409,8 +470,8 @@ where
|
|||||||
deadline: dispatch_request.ctx.deadline,
|
deadline: dispatch_request.ctx.deadline,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
self.transport().start_send(request)?;
|
self.as_mut().transport().start_send(request)?;
|
||||||
self.in_flight_requests().insert(
|
self.as_mut().in_flight_requests().insert(
|
||||||
request_id,
|
request_id,
|
||||||
InFlightData {
|
InFlightData {
|
||||||
ctx: dispatch_request.ctx,
|
ctx: dispatch_request.ctx,
|
||||||
@@ -430,20 +491,28 @@ where
|
|||||||
trace_context: context.trace_context,
|
trace_context: context.trace_context,
|
||||||
message: ClientMessageKind::Cancel { request_id },
|
message: ClientMessageKind::Cancel { request_id },
|
||||||
};
|
};
|
||||||
self.transport().start_send(cancel)?;
|
self.as_mut().transport().start_send(cancel)?;
|
||||||
trace!("[{}/{}] Cancel message sent.", trace_id, self.server_addr());
|
trace!(
|
||||||
return Ok(());
|
"[{}/{}] Cancel message sent.",
|
||||||
|
trace_id,
|
||||||
|
self.as_mut().server_addr()
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a server response to the client task that initiated the associated request.
|
/// Sends a server response to the client task that initiated the associated request.
|
||||||
fn complete(self: &mut Pin<&mut Self>, response: Response<Resp>) -> bool {
|
fn complete(self: &mut Pin<&mut Self>, response: Response<Resp>) -> bool {
|
||||||
if let Some(in_flight_data) = self.in_flight_requests().remove(&response.request_id) {
|
if let Some(in_flight_data) = self
|
||||||
self.in_flight_requests().compact(0.1);
|
.as_mut()
|
||||||
|
.in_flight_requests()
|
||||||
|
.remove(&response.request_id)
|
||||||
|
{
|
||||||
|
self.as_mut().in_flight_requests().compact(0.1);
|
||||||
|
|
||||||
trace!(
|
trace!(
|
||||||
"[{}/{}] Received response.",
|
"[{}/{}] Received response.",
|
||||||
in_flight_data.ctx.trace_id(),
|
in_flight_data.ctx.trace_id(),
|
||||||
self.server_addr()
|
self.as_mut().server_addr()
|
||||||
);
|
);
|
||||||
let _ = in_flight_data.response_completion.send(response);
|
let _ = in_flight_data.response_completion.send(response);
|
||||||
return true;
|
return true;
|
||||||
@@ -451,7 +520,7 @@ where
|
|||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"[{}] No in-flight request found for request_id = {}.",
|
"[{}] No in-flight request found for request_id = {}.",
|
||||||
self.server_addr(),
|
self.as_mut().server_addr(),
|
||||||
response.request_id
|
response.request_id
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -462,21 +531,21 @@ where
|
|||||||
|
|
||||||
impl<Req, Resp, C> Future for RequestDispatch<Req, Resp, C>
|
impl<Req, Resp, C> Future for RequestDispatch<Req, Resp, C>
|
||||||
where
|
where
|
||||||
Req: Send,
|
Req: marker::Send,
|
||||||
Resp: Send,
|
Resp: marker::Send,
|
||||||
C: Transport<Item = Response<Resp>, SinkItem = ClientMessage<Req>>,
|
C: Transport<Item = Response<Resp>, SinkItem = ClientMessage<Req>>,
|
||||||
{
|
{
|
||||||
type Output = io::Result<()>;
|
type Output = io::Result<()>;
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, waker: &LocalWaker) -> Poll<io::Result<()>> {
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
trace!("[{}] RequestDispatch::poll", self.server_addr());
|
trace!("[{}] RequestDispatch::poll", self.as_mut().server_addr());
|
||||||
loop {
|
loop {
|
||||||
match (self.pump_read(waker)?, self.pump_write(waker)?) {
|
match (self.pump_read(cx)?, self.pump_write(cx)?) {
|
||||||
(read, write @ Poll::Ready(None)) => {
|
(read, write @ Poll::Ready(None)) => {
|
||||||
if self.in_flight_requests().is_empty() {
|
if self.as_mut().in_flight_requests().is_empty() {
|
||||||
info!(
|
info!(
|
||||||
"[{}] Shutdown: write half closed, and no requests in flight.",
|
"[{}] Shutdown: write half closed, and no requests in flight.",
|
||||||
self.server_addr()
|
self.as_mut().server_addr()
|
||||||
);
|
);
|
||||||
return Poll::Ready(Ok(()));
|
return Poll::Ready(Ok(()));
|
||||||
}
|
}
|
||||||
@@ -485,7 +554,7 @@ where
|
|||||||
_ => {
|
_ => {
|
||||||
trace!(
|
trace!(
|
||||||
"[{}] read: {:?}, write: {:?}, (not ready)",
|
"[{}] read: {:?}, write: {:?}, (not ready)",
|
||||||
self.server_addr(),
|
self.as_mut().server_addr(),
|
||||||
read,
|
read,
|
||||||
write,
|
write,
|
||||||
);
|
);
|
||||||
@@ -496,7 +565,7 @@ where
|
|||||||
(read @ Poll::Ready(Some(())), write) | (read, write @ Poll::Ready(Some(()))) => {
|
(read @ Poll::Ready(Some(())), write) | (read, write @ Poll::Ready(Some(()))) => {
|
||||||
trace!(
|
trace!(
|
||||||
"[{}] read: {:?}, write: {:?}",
|
"[{}] read: {:?}, write: {:?}",
|
||||||
self.server_addr(),
|
self.as_mut().server_addr(),
|
||||||
read,
|
read,
|
||||||
write,
|
write,
|
||||||
)
|
)
|
||||||
@@ -504,7 +573,7 @@ where
|
|||||||
(read, write) => {
|
(read, write) => {
|
||||||
trace!(
|
trace!(
|
||||||
"[{}] read: {:?}, write: {:?} (not ready)",
|
"[{}] read: {:?}, write: {:?} (not ready)",
|
||||||
self.server_addr(),
|
self.as_mut().server_addr(),
|
||||||
read,
|
read,
|
||||||
write,
|
write,
|
||||||
);
|
);
|
||||||
@@ -558,14 +627,200 @@ impl RequestCancellation {
|
|||||||
impl Stream for CanceledRequests {
|
impl Stream for CanceledRequests {
|
||||||
type Item = u64;
|
type Item = u64;
|
||||||
|
|
||||||
fn poll_next(mut self: Pin<&mut Self>, waker: &LocalWaker) -> Poll<Option<u64>> {
|
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<u64>> {
|
||||||
self.0.poll_next_unpin(waker)
|
self.0.poll_next_unpin(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[must_use = "futures do nothing unless polled"]
|
||||||
|
struct MapErrConnectionReset<Fut> {
|
||||||
|
future: Fut,
|
||||||
|
finished: Option<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Fut> MapErrConnectionReset<Fut> {
|
||||||
|
unsafe_pinned!(future: Fut);
|
||||||
|
unsafe_unpinned!(finished: Option<()>);
|
||||||
|
|
||||||
|
fn new(future: Fut) -> MapErrConnectionReset<Fut> {
|
||||||
|
MapErrConnectionReset {
|
||||||
|
future,
|
||||||
|
finished: Some(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Fut: Unpin> Unpin for MapErrConnectionReset<Fut> {}
|
||||||
|
|
||||||
|
impl<Fut> Future for MapErrConnectionReset<Fut>
|
||||||
|
where
|
||||||
|
Fut: TryFuture,
|
||||||
|
{
|
||||||
|
type Output = io::Result<Fut::Ok>;
|
||||||
|
|
||||||
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
match self.as_mut().future().try_poll(cx) {
|
||||||
|
Poll::Pending => Poll::Pending,
|
||||||
|
Poll::Ready(result) => {
|
||||||
|
self.finished().take().expect(
|
||||||
|
"MapErrConnectionReset must not be polled after it returned `Poll::Ready`",
|
||||||
|
);
|
||||||
|
Poll::Ready(result.map_err(|_| io::Error::from(io::ErrorKind::ConnectionReset)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[must_use = "futures do nothing unless polled"]
|
||||||
|
struct MapOkDispatchResponse<Fut, Resp> {
|
||||||
|
future: Fut,
|
||||||
|
response: Option<DispatchResponse<Resp>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Fut, Resp> MapOkDispatchResponse<Fut, Resp> {
|
||||||
|
unsafe_pinned!(future: Fut);
|
||||||
|
unsafe_unpinned!(response: Option<DispatchResponse<Resp>>);
|
||||||
|
|
||||||
|
fn new(future: Fut, response: DispatchResponse<Resp>) -> MapOkDispatchResponse<Fut, Resp> {
|
||||||
|
MapOkDispatchResponse {
|
||||||
|
future,
|
||||||
|
response: Some(response),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Fut: Unpin, Resp> Unpin for MapOkDispatchResponse<Fut, Resp> {}
|
||||||
|
|
||||||
|
impl<Fut, Resp> Future for MapOkDispatchResponse<Fut, Resp>
|
||||||
|
where
|
||||||
|
Fut: TryFuture,
|
||||||
|
{
|
||||||
|
type Output = Result<DispatchResponse<Resp>, Fut::Error>;
|
||||||
|
|
||||||
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
match self.as_mut().future().try_poll(cx) {
|
||||||
|
Poll::Pending => Poll::Pending,
|
||||||
|
Poll::Ready(result) => {
|
||||||
|
let response = self
|
||||||
|
.as_mut()
|
||||||
|
.response()
|
||||||
|
.take()
|
||||||
|
.expect("MapOk must not be polled after it returned `Poll::Ready`");
|
||||||
|
Poll::Ready(result.map(|_| response))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[must_use = "futures do nothing unless polled"]
|
||||||
|
struct AndThenIdent<Fut1, Fut2> {
|
||||||
|
try_chain: TryChain<Fut1, Fut2>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Fut1, Fut2> AndThenIdent<Fut1, Fut2>
|
||||||
|
where
|
||||||
|
Fut1: TryFuture<Ok = Fut2>,
|
||||||
|
Fut2: TryFuture,
|
||||||
|
{
|
||||||
|
unsafe_pinned!(try_chain: TryChain<Fut1, Fut2>);
|
||||||
|
|
||||||
|
/// Creates a new `Then`.
|
||||||
|
fn new(future: Fut1) -> AndThenIdent<Fut1, Fut2> {
|
||||||
|
AndThenIdent {
|
||||||
|
try_chain: TryChain::new(future),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Fut1, Fut2> Future for AndThenIdent<Fut1, Fut2>
|
||||||
|
where
|
||||||
|
Fut1: TryFuture<Ok = Fut2>,
|
||||||
|
Fut2: TryFuture<Error = Fut1::Error>,
|
||||||
|
{
|
||||||
|
type Output = Result<Fut2::Ok, Fut2::Error>;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
self.try_chain().poll(cx, |result| match result {
|
||||||
|
Ok(ok) => TryChainAction::Future(ok),
|
||||||
|
Err(err) => TryChainAction::Output(Err(err)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use = "futures do nothing unless polled"]
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum TryChain<Fut1, Fut2> {
|
||||||
|
First(Fut1),
|
||||||
|
Second(Fut2),
|
||||||
|
Empty,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TryChainAction<Fut2>
|
||||||
|
where
|
||||||
|
Fut2: TryFuture,
|
||||||
|
{
|
||||||
|
Future(Fut2),
|
||||||
|
Output(Result<Fut2::Ok, Fut2::Error>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Fut1, Fut2> TryChain<Fut1, Fut2>
|
||||||
|
where
|
||||||
|
Fut1: TryFuture<Ok = Fut2>,
|
||||||
|
Fut2: TryFuture,
|
||||||
|
{
|
||||||
|
fn new(fut1: Fut1) -> TryChain<Fut1, Fut2> {
|
||||||
|
TryChain::First(fut1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll<F>(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
f: F,
|
||||||
|
) -> Poll<Result<Fut2::Ok, Fut2::Error>>
|
||||||
|
where
|
||||||
|
F: FnOnce(Result<Fut1::Ok, Fut1::Error>) -> TryChainAction<Fut2>,
|
||||||
|
{
|
||||||
|
let mut f = Some(f);
|
||||||
|
|
||||||
|
// Safe to call `get_unchecked_mut` because we won't move the futures.
|
||||||
|
let this = unsafe { Pin::get_unchecked_mut(self) };
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let output = match this {
|
||||||
|
TryChain::First(fut1) => {
|
||||||
|
// Poll the first future
|
||||||
|
match unsafe { Pin::new_unchecked(fut1) }.try_poll(cx) {
|
||||||
|
Poll::Pending => return Poll::Pending,
|
||||||
|
Poll::Ready(output) => output,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TryChain::Second(fut2) => {
|
||||||
|
// Poll the second future
|
||||||
|
return unsafe { Pin::new_unchecked(fut2) }.try_poll(cx);
|
||||||
|
}
|
||||||
|
TryChain::Empty => {
|
||||||
|
panic!("future must not be polled after it returned `Poll::Ready`");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
*this = TryChain::Empty; // Drop fut1
|
||||||
|
let f = f.take().unwrap();
|
||||||
|
match f(output) {
|
||||||
|
TryChainAction::Future(fut2) => *this = TryChain::Second(fut2),
|
||||||
|
TryChainAction::Output(output) => return Poll::Ready(output),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{CanceledRequests, Channel, RequestCancellation, RequestDispatch};
|
use super::{
|
||||||
|
CanceledRequests, Channel, DispatchResponse, RequestCancellation, RequestDispatch,
|
||||||
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
client::Config,
|
client::Config,
|
||||||
context,
|
context,
|
||||||
@@ -573,9 +828,10 @@ mod tests {
|
|||||||
ClientMessage, Response,
|
ClientMessage, Response,
|
||||||
};
|
};
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use futures::{Poll, channel::mpsc, prelude::*};
|
use futures::{channel::mpsc, prelude::*, task::Context, Poll};
|
||||||
use futures_test::task::{noop_local_waker_ref};
|
use futures_test::task::noop_waker_ref;
|
||||||
use std::{
|
use std::{
|
||||||
|
marker,
|
||||||
net::{IpAddr, Ipv4Addr, SocketAddr},
|
net::{IpAddr, Ipv4Addr, SocketAddr},
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
sync::atomic::AtomicU64,
|
sync::atomic::AtomicU64,
|
||||||
@@ -585,20 +841,12 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn stage_request() {
|
fn stage_request() {
|
||||||
let (mut dispatch, mut channel, _server_channel) = set_up();
|
let (mut dispatch, mut channel, _server_channel) = set_up();
|
||||||
|
|
||||||
// Test that a request future dropped before it's processed by dispatch will cause the request
|
|
||||||
// to not be added to the in-flight request map.
|
|
||||||
let _resp = tokio::runtime::current_thread::block_on_all(
|
|
||||||
channel
|
|
||||||
.send(context::current(), "hi".to_string())
|
|
||||||
.boxed()
|
|
||||||
.compat(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut dispatch = Pin::new(&mut dispatch);
|
let mut dispatch = Pin::new(&mut dispatch);
|
||||||
let waker = &noop_local_waker_ref();
|
let cx = &mut Context::from_waker(&noop_waker_ref());
|
||||||
|
|
||||||
let req = dispatch.poll_next_request(waker).ready();
|
let _resp = send_request(&mut channel, "hi");
|
||||||
|
|
||||||
|
let req = dispatch.poll_next_request(cx).ready();
|
||||||
assert!(req.is_some());
|
assert!(req.is_some());
|
||||||
|
|
||||||
let req = req.unwrap();
|
let req = req.unwrap();
|
||||||
@@ -606,47 +854,77 @@ mod tests {
|
|||||||
assert_eq!(req.request, "hi".to_string());
|
assert_eq!(req.request, "hi".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Regression test for https://github.com/google/tarpc/issues/220
|
||||||
#[test]
|
#[test]
|
||||||
fn stage_request_response_future_dropped() {
|
fn stage_request_channel_dropped_doesnt_panic() {
|
||||||
let (mut dispatch, mut channel, _server_channel) = set_up();
|
let (mut dispatch, mut channel, mut server_channel) = set_up();
|
||||||
|
let mut dispatch = Pin::new(&mut dispatch);
|
||||||
|
let cx = &mut Context::from_waker(&noop_waker_ref());
|
||||||
|
|
||||||
// Test that a request future dropped before it's processed by dispatch will cause the request
|
let _ = send_request(&mut channel, "hi");
|
||||||
// to not be added to the in-flight request map.
|
|
||||||
let resp = tokio::runtime::current_thread::block_on_all(
|
|
||||||
channel
|
|
||||||
.send(context::current(), "hi".into())
|
|
||||||
.boxed()
|
|
||||||
.compat(),
|
|
||||||
).unwrap();
|
|
||||||
drop(resp);
|
|
||||||
drop(channel);
|
drop(channel);
|
||||||
|
|
||||||
let mut dispatch = Pin::new(&mut dispatch);
|
assert!(dispatch.as_mut().poll(cx).is_ready());
|
||||||
let waker = &noop_local_waker_ref();
|
send_response(
|
||||||
|
&mut server_channel,
|
||||||
dispatch.poll_next_cancellation(waker).unwrap();
|
Response {
|
||||||
assert!(dispatch.poll_next_request(waker).ready().is_none());
|
request_id: 0,
|
||||||
|
message: Ok("hello".into()),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
tokio::runtime::current_thread::block_on_all(dispatch.boxed().compat()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn stage_request_response_future_closed() {
|
fn stage_request_response_future_dropped_is_canceled_before_sending() {
|
||||||
let (mut dispatch, mut channel, _server_channel) = set_up();
|
let (mut dispatch, mut channel, _server_channel) = set_up();
|
||||||
|
let mut dispatch = Pin::new(&mut dispatch);
|
||||||
|
let cx = &mut Context::from_waker(&noop_waker_ref());
|
||||||
|
|
||||||
|
let _ = send_request(&mut channel, "hi");
|
||||||
|
|
||||||
|
// Drop the channel so polling returns none if no requests are currently ready.
|
||||||
|
drop(channel);
|
||||||
|
// Test that a request future dropped before it's processed by dispatch will cause the request
|
||||||
|
// to not be added to the in-flight request map.
|
||||||
|
assert!(dispatch.poll_next_request(cx).ready().is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stage_request_response_future_dropped_is_canceled_after_sending() {
|
||||||
|
let (mut dispatch, mut channel, _server_channel) = set_up();
|
||||||
|
let cx = &mut Context::from_waker(&noop_waker_ref());
|
||||||
|
let mut dispatch = Pin::new(&mut dispatch);
|
||||||
|
|
||||||
|
let req = send_request(&mut channel, "hi");
|
||||||
|
|
||||||
|
assert!(dispatch.as_mut().pump_write(cx).ready().is_some());
|
||||||
|
assert!(!dispatch.as_mut().in_flight_requests().is_empty());
|
||||||
|
|
||||||
|
// Test that a request future dropped after it's processed by dispatch will cause the request
|
||||||
|
// to be removed from the in-flight request map.
|
||||||
|
drop(req);
|
||||||
|
if let Poll::Ready(Some(_)) = dispatch.as_mut().poll_next_cancellation(cx).unwrap() {
|
||||||
|
// ok
|
||||||
|
} else {
|
||||||
|
panic!("Expected request to be cancelled")
|
||||||
|
};
|
||||||
|
assert!(dispatch.in_flight_requests().is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stage_request_response_closed_skipped() {
|
||||||
|
let (mut dispatch, mut channel, _server_channel) = set_up();
|
||||||
|
let mut dispatch = Pin::new(&mut dispatch);
|
||||||
|
let cx = &mut Context::from_waker(&noop_waker_ref());
|
||||||
|
|
||||||
// Test that a request future that's closed its receiver but not yet canceled its request --
|
// Test that a request future that's closed its receiver but not yet canceled its request --
|
||||||
// i.e. still in `drop fn` -- will cause the request to not be added to the in-flight request
|
// i.e. still in `drop fn` -- will cause the request to not be added to the in-flight request
|
||||||
// map.
|
// map.
|
||||||
let resp = tokio::runtime::current_thread::block_on_all(
|
let mut resp = send_request(&mut channel, "hi");
|
||||||
channel
|
resp.response.get_mut().close();
|
||||||
.send(context::current(), "hi".into())
|
|
||||||
.boxed()
|
|
||||||
.compat(),
|
|
||||||
).unwrap();
|
|
||||||
drop(resp);
|
|
||||||
drop(channel);
|
|
||||||
|
|
||||||
let mut dispatch = Pin::new(&mut dispatch);
|
assert!(dispatch.poll_next_request(cx).is_pending());
|
||||||
let waker = &noop_local_waker_ref();
|
|
||||||
assert!(dispatch.poll_next_request(waker).ready().is_none());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_up() -> (
|
fn set_up() -> (
|
||||||
@@ -663,7 +941,7 @@ mod tests {
|
|||||||
let dispatch = RequestDispatch::<String, String, _> {
|
let dispatch = RequestDispatch::<String, String, _> {
|
||||||
transport: client_channel.fuse(),
|
transport: client_channel.fuse(),
|
||||||
pending_requests: pending_requests.fuse(),
|
pending_requests: pending_requests.fuse(),
|
||||||
canceled_requests: CanceledRequests(canceled_requests),
|
canceled_requests: CanceledRequests(canceled_requests).fuse(),
|
||||||
in_flight_requests: FnvHashMap::default(),
|
in_flight_requests: FnvHashMap::default(),
|
||||||
config: Config::default(),
|
config: Config::default(),
|
||||||
server_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0),
|
server_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0),
|
||||||
@@ -680,6 +958,27 @@ mod tests {
|
|||||||
(dispatch, channel, server_channel)
|
(dispatch, channel, server_channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn send_request(
|
||||||
|
channel: &mut Channel<String, String>,
|
||||||
|
request: &str,
|
||||||
|
) -> DispatchResponse<String> {
|
||||||
|
tokio::runtime::current_thread::block_on_all(
|
||||||
|
channel
|
||||||
|
.send(context::current(), request.to_string())
|
||||||
|
.boxed()
|
||||||
|
.compat(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_response(
|
||||||
|
channel: &mut UnboundedChannel<ClientMessage<String>, Response<String>>,
|
||||||
|
response: Response<String>,
|
||||||
|
) {
|
||||||
|
tokio::runtime::current_thread::block_on_all(channel.send(response).boxed().compat())
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
trait PollTest {
|
trait PollTest {
|
||||||
type T;
|
type T;
|
||||||
fn unwrap(self) -> Poll<Self::T>;
|
fn unwrap(self) -> Poll<Self::T>;
|
||||||
@@ -688,7 +987,7 @@ mod tests {
|
|||||||
|
|
||||||
impl<T, E> PollTest for Poll<Option<Result<T, E>>>
|
impl<T, E> PollTest for Poll<Option<Result<T, E>>>
|
||||||
where
|
where
|
||||||
E: ::std::fmt::Display + Send + 'static,
|
E: ::std::fmt::Display + marker::Send + 'static,
|
||||||
{
|
{
|
||||||
type T = Option<T>;
|
type T = Option<T>;
|
||||||
|
|
||||||
@@ -710,5 +1009,4 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -6,27 +6,103 @@
|
|||||||
|
|
||||||
//! Provides a client that connects to a server and sends multiplexed requests.
|
//! Provides a client that connects to a server and sends multiplexed requests.
|
||||||
|
|
||||||
use crate::{context::Context, ClientMessage, Response, Transport};
|
use crate::{context, ClientMessage, Response, Transport};
|
||||||
|
use futures::prelude::*;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use std::{
|
use std::{
|
||||||
io,
|
io,
|
||||||
net::{Ipv4Addr, SocketAddr},
|
net::{Ipv4Addr, SocketAddr},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod dispatch;
|
/// Provides a [`Client`] backed by a transport.
|
||||||
|
pub mod channel;
|
||||||
|
pub use self::channel::Channel;
|
||||||
|
|
||||||
/// Sends multiplexed requests to, and receives responses from, a server.
|
/// Sends multiplexed requests to, and receives responses from, a server.
|
||||||
#[derive(Debug)]
|
pub trait Client<'a, Req> {
|
||||||
pub struct Client<Req, Resp> {
|
/// The response type.
|
||||||
/// Channel to send requests to the dispatch task.
|
type Response;
|
||||||
channel: dispatch::Channel<Req, Resp>,
|
|
||||||
|
/// The future response.
|
||||||
|
type Future: Future<Output = io::Result<Self::Response>> + 'a;
|
||||||
|
|
||||||
|
/// Initiates a request, sending it to the dispatch task.
|
||||||
|
///
|
||||||
|
/// Returns a [`Future`] that resolves to this client and the future response
|
||||||
|
/// once the request is successfully enqueued.
|
||||||
|
///
|
||||||
|
/// [`Future`]: futures::Future
|
||||||
|
fn call(&'a mut self, ctx: context::Context, request: Req) -> Self::Future;
|
||||||
|
|
||||||
|
/// Returns a Client that applies a post-processing function to the returned response.
|
||||||
|
fn map_response<F, R>(self, f: F) -> MapResponse<Self, F>
|
||||||
|
where
|
||||||
|
F: FnMut(Self::Response) -> R,
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
MapResponse { inner: self, f }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a Client that applies a pre-processing function to the request.
|
||||||
|
fn with_request<F, Req2>(self, f: F) -> WithRequest<Self, F>
|
||||||
|
where
|
||||||
|
F: FnMut(Req2) -> Req,
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
WithRequest { inner: self, f }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Req, Resp> Clone for Client<Req, Resp> {
|
/// A Client that applies a function to the returned response.
|
||||||
fn clone(&self) -> Self {
|
#[derive(Clone, Debug)]
|
||||||
Client {
|
pub struct MapResponse<C, F> {
|
||||||
channel: self.channel.clone(),
|
inner: C,
|
||||||
}
|
f: F,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, C, F, Req, Resp, Resp2> Client<'a, Req> for MapResponse<C, F>
|
||||||
|
where
|
||||||
|
C: Client<'a, Req, Response = Resp>,
|
||||||
|
F: FnMut(Resp) -> Resp2 + 'a,
|
||||||
|
{
|
||||||
|
type Response = Resp2;
|
||||||
|
type Future = futures::future::MapOk<<C as Client<'a, Req>>::Future, &'a mut F>;
|
||||||
|
|
||||||
|
fn call(&'a mut self, ctx: context::Context, request: Req) -> Self::Future {
|
||||||
|
self.inner.call(ctx, request).map_ok(&mut self.f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Client that applies a pre-processing function to the request.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct WithRequest<C, F> {
|
||||||
|
inner: C,
|
||||||
|
f: F,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, C, F, Req, Req2, Resp> Client<'a, Req2> for WithRequest<C, F>
|
||||||
|
where
|
||||||
|
C: Client<'a, Req, Response = Resp>,
|
||||||
|
F: FnMut(Req2) -> Req,
|
||||||
|
{
|
||||||
|
type Response = Resp;
|
||||||
|
type Future = <C as Client<'a, Req>>::Future;
|
||||||
|
|
||||||
|
fn call(&'a mut self, ctx: context::Context, request: Req2) -> Self::Future {
|
||||||
|
self.inner.call(ctx, (self.f)(request))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Req, Resp> Client<'a, Req> for Channel<Req, Resp>
|
||||||
|
where
|
||||||
|
Req: 'a,
|
||||||
|
Resp: 'a,
|
||||||
|
{
|
||||||
|
type Response = Resp;
|
||||||
|
type Future = channel::Call<'a, Req, Resp>;
|
||||||
|
|
||||||
|
fn call(&'a mut self, ctx: context::Context, request: Req) -> channel::Call<'a, Req, Resp> {
|
||||||
|
self.call(ctx, request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,39 +129,23 @@ impl Default for Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Req, Resp> Client<Req, Resp>
|
/// Creates a new Client by wrapping a [`Transport`] and spawning a dispatch task
|
||||||
|
/// that manages the lifecycle of requests.
|
||||||
|
///
|
||||||
|
/// Must only be called from on an executor.
|
||||||
|
pub async fn new<Req, Resp, T>(config: Config, transport: T) -> io::Result<Channel<Req, Resp>>
|
||||||
where
|
where
|
||||||
Req: Send,
|
Req: Send + 'static,
|
||||||
Resp: Send,
|
Resp: Send + 'static,
|
||||||
|
T: Transport<Item = Response<Resp>, SinkItem = ClientMessage<Req>> + Send + 'static,
|
||||||
{
|
{
|
||||||
/// Creates a new Client by wrapping a [`Transport`] and spawning a dispatch task
|
let server_addr = transport.peer_addr().unwrap_or_else(|e| {
|
||||||
/// that manages the lifecycle of requests.
|
warn!(
|
||||||
///
|
"Setting peer to unspecified because peer could not be determined: {}",
|
||||||
/// Must only be called from on an executor.
|
e
|
||||||
pub async fn new<T>(config: Config, transport: T) -> io::Result<Self>
|
);
|
||||||
where
|
SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0)
|
||||||
T: Transport<Item = Response<Resp>, SinkItem = ClientMessage<Req>> + Send,
|
});
|
||||||
{
|
|
||||||
let server_addr = transport.peer_addr().unwrap_or_else(|e| {
|
|
||||||
warn!(
|
|
||||||
"Setting peer to unspecified because peer could not be determined: {}",
|
|
||||||
e
|
|
||||||
);
|
|
||||||
SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0)
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(Client {
|
Ok(channel::spawn(config, transport, server_addr).await?)
|
||||||
channel: await!(dispatch::spawn(config, transport, server_addr))?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initiates a request, sending it to the dispatch task.
|
|
||||||
///
|
|
||||||
/// Returns a [`Future`] that resolves to this client and the future response
|
|
||||||
/// once the request is successfully enqueued.
|
|
||||||
///
|
|
||||||
/// [`Future`]: futures::Future
|
|
||||||
pub async fn call(&mut self, ctx: Context, request: Req) -> io::Result<Resp> {
|
|
||||||
await!(self.channel.call(ctx, request))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,21 +5,11 @@
|
|||||||
// https://opensource.org/licenses/MIT.
|
// https://opensource.org/licenses/MIT.
|
||||||
|
|
||||||
#![feature(
|
#![feature(
|
||||||
const_fn,
|
|
||||||
non_exhaustive,
|
non_exhaustive,
|
||||||
integer_atomics,
|
integer_atomics,
|
||||||
try_trait,
|
try_trait,
|
||||||
nll,
|
|
||||||
futures_api,
|
|
||||||
pin,
|
|
||||||
arbitrary_self_types,
|
arbitrary_self_types,
|
||||||
await_macro,
|
async_await
|
||||||
async_await,
|
|
||||||
generators,
|
|
||||||
optin_builtin_traits,
|
|
||||||
generator_trait,
|
|
||||||
gen_future,
|
|
||||||
decl_macro,
|
|
||||||
)]
|
)]
|
||||||
#![deny(missing_docs, missing_debug_implementations)]
|
#![deny(missing_docs, missing_debug_implementations)]
|
||||||
|
|
||||||
@@ -49,15 +39,15 @@ pub(crate) mod util;
|
|||||||
|
|
||||||
pub use crate::{client::Client, server::Server, transport::Transport};
|
pub use crate::{client::Client, server::Server, transport::Transport};
|
||||||
|
|
||||||
use futures::{Future, task::{Spawn, SpawnExt, SpawnError}};
|
use futures::{
|
||||||
|
task::{Poll, Spawn, SpawnError, SpawnExt},
|
||||||
|
Future,
|
||||||
|
};
|
||||||
use std::{cell::RefCell, io, sync::Once, time::SystemTime};
|
use std::{cell::RefCell, io, sync::Once, time::SystemTime};
|
||||||
|
|
||||||
/// A message from a client to a server.
|
/// A message from a client to a server.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[cfg_attr(
|
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
|
||||||
feature = "serde1",
|
|
||||||
derive(serde::Serialize, serde::Deserialize)
|
|
||||||
)]
|
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct ClientMessage<T> {
|
pub struct ClientMessage<T> {
|
||||||
/// The trace context associates the message with a specific chain of causally-related actions,
|
/// The trace context associates the message with a specific chain of causally-related actions,
|
||||||
@@ -69,10 +59,7 @@ pub struct ClientMessage<T> {
|
|||||||
|
|
||||||
/// Different messages that can be sent from a client to a server.
|
/// Different messages that can be sent from a client to a server.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[cfg_attr(
|
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
|
||||||
feature = "serde1",
|
|
||||||
derive(serde::Serialize, serde::Deserialize)
|
|
||||||
)]
|
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum ClientMessageKind<T> {
|
pub enum ClientMessageKind<T> {
|
||||||
/// A request initiated by a user. The server responds to a request by invoking a
|
/// A request initiated by a user. The server responds to a request by invoking a
|
||||||
@@ -94,10 +81,7 @@ pub enum ClientMessageKind<T> {
|
|||||||
|
|
||||||
/// A request from a client to a server.
|
/// A request from a client to a server.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[cfg_attr(
|
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
|
||||||
feature = "serde1",
|
|
||||||
derive(serde::Serialize, serde::Deserialize)
|
|
||||||
)]
|
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct Request<T> {
|
pub struct Request<T> {
|
||||||
/// Uniquely identifies the request across all requests sent over a single channel.
|
/// Uniquely identifies the request across all requests sent over a single channel.
|
||||||
@@ -119,10 +103,7 @@ pub struct Request<T> {
|
|||||||
|
|
||||||
/// A response from a server to a client.
|
/// A response from a server to a client.
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(
|
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
|
||||||
feature = "serde1",
|
|
||||||
derive(serde::Serialize, serde::Deserialize)
|
|
||||||
)]
|
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct Response<T> {
|
pub struct Response<T> {
|
||||||
/// The ID of the request being responded to.
|
/// The ID of the request being responded to.
|
||||||
@@ -133,10 +114,7 @@ pub struct Response<T> {
|
|||||||
|
|
||||||
/// An error response from a server to a client.
|
/// An error response from a server to a client.
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(
|
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
|
||||||
feature = "serde1",
|
|
||||||
derive(serde::Serialize, serde::Deserialize)
|
|
||||||
)]
|
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct ServerError {
|
pub struct ServerError {
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
@@ -166,6 +144,8 @@ impl<T> Request<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) type PollIo<T> = Poll<Option<io::Result<T>>>;
|
||||||
|
|
||||||
static INIT: Once = Once::new();
|
static INIT: Once = Once::new();
|
||||||
static mut SEED_SPAWN: Option<Box<dyn CloneSpawn>> = None;
|
static mut SEED_SPAWN: Option<Box<dyn CloneSpawn>> = None;
|
||||||
thread_local! {
|
thread_local! {
|
||||||
@@ -174,7 +154,7 @@ thread_local! {
|
|||||||
// INIT must always be called before accessing SPAWN.
|
// INIT must always be called before accessing SPAWN.
|
||||||
// Otherwise, accessing SPAWN can trigger undefined behavior due to race conditions.
|
// Otherwise, accessing SPAWN can trigger undefined behavior due to race conditions.
|
||||||
INIT.call_once(|| {});
|
INIT.call_once(|| {});
|
||||||
RefCell::new(SEED_SPAWN.clone().expect("init() must be called."))
|
RefCell::new(SEED_SPAWN.as_ref().expect("init() must be called.").box_clone())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -193,21 +173,13 @@ pub fn init(spawn: impl Spawn + Clone + 'static) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn spawn(future: impl Future<Output = ()> + Send + 'static) -> Result<(), SpawnError> {
|
pub(crate) fn spawn(future: impl Future<Output = ()> + Send + 'static) -> Result<(), SpawnError> {
|
||||||
SPAWN.with(|spawn| {
|
SPAWN.with(|spawn| spawn.borrow_mut().spawn(future))
|
||||||
spawn.borrow_mut().spawn(future)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trait CloneSpawn: Spawn {
|
trait CloneSpawn: Spawn {
|
||||||
fn box_clone(&self) -> Box<dyn CloneSpawn>;
|
fn box_clone(&self) -> Box<dyn CloneSpawn>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for Box<dyn CloneSpawn> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
self.box_clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: Spawn + Clone + 'static> CloneSpawn for S {
|
impl<S: Spawn + Clone + 'static> CloneSpawn for S {
|
||||||
fn box_clone(&self) -> Box<dyn CloneSpawn> {
|
fn box_clone(&self) -> Box<dyn CloneSpawn> {
|
||||||
Box::new(self.clone())
|
Box::new(self.clone())
|
||||||
|
|||||||
@@ -7,10 +7,16 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
server::{Channel, Config},
|
server::{Channel, Config},
|
||||||
util::Compact,
|
util::Compact,
|
||||||
ClientMessage, Response, Transport,
|
ClientMessage, PollIo, Response, Transport,
|
||||||
};
|
};
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use futures::{channel::mpsc, prelude::*, ready, stream::Fuse, task::{LocalWaker, Poll}};
|
use futures::{
|
||||||
|
channel::mpsc,
|
||||||
|
prelude::*,
|
||||||
|
ready,
|
||||||
|
stream::Fuse,
|
||||||
|
task::{Context, Poll},
|
||||||
|
};
|
||||||
use log::{debug, error, info, trace, warn};
|
use log::{debug, error, info, trace, warn};
|
||||||
use pin_utils::unsafe_pinned;
|
use pin_utils::unsafe_pinned;
|
||||||
use std::{
|
use std::{
|
||||||
@@ -101,28 +107,28 @@ impl<S, Req, Resp> ConnectionFilter<S, Req, Resp> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let open_connections = *self.open_connections();
|
let open_connections = *self.as_mut().open_connections();
|
||||||
if open_connections >= self.config().max_connections {
|
if open_connections >= self.as_mut().config().max_connections {
|
||||||
warn!(
|
warn!(
|
||||||
"[{}] Shedding connection because the maximum open connections \
|
"[{}] Shedding connection because the maximum open connections \
|
||||||
limit is reached ({}/{}).",
|
limit is reached ({}/{}).",
|
||||||
peer,
|
peer,
|
||||||
open_connections,
|
open_connections,
|
||||||
self.config().max_connections
|
self.as_mut().config().max_connections
|
||||||
);
|
);
|
||||||
return NewConnection::Filtered;
|
return NewConnection::Filtered;
|
||||||
}
|
}
|
||||||
|
|
||||||
let config = self.config.clone();
|
let config = self.config.clone();
|
||||||
let open_connections_for_ip = self.increment_connections_for_ip(&peer)?;
|
let open_connections_for_ip = self.increment_connections_for_ip(&peer)?;
|
||||||
*self.open_connections() += 1;
|
*self.as_mut().open_connections() += 1;
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"[{}] Opening channel ({}/{} connections for IP, {} total).",
|
"[{}] Opening channel ({}/{} connections for IP, {} total).",
|
||||||
peer,
|
peer,
|
||||||
open_connections_for_ip,
|
open_connections_for_ip,
|
||||||
config.max_connections_per_ip,
|
config.max_connections_per_ip,
|
||||||
self.open_connections(),
|
self.as_mut().open_connections(),
|
||||||
);
|
);
|
||||||
|
|
||||||
NewConnection::Accepted(Channel {
|
NewConnection::Accepted(Channel {
|
||||||
@@ -135,19 +141,19 @@ impl<S, Req, Resp> ConnectionFilter<S, Req, Resp> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_closed_connection(self: &mut Pin<&mut Self>, addr: &SocketAddr) {
|
fn handle_closed_connection(self: &mut Pin<&mut Self>, addr: &SocketAddr) {
|
||||||
*self.open_connections() -= 1;
|
*self.as_mut().open_connections() -= 1;
|
||||||
debug!(
|
debug!(
|
||||||
"[{}] Closing channel. {} open connections remaining.",
|
"[{}] Closing channel. {} open connections remaining.",
|
||||||
addr, self.open_connections
|
addr, self.open_connections
|
||||||
);
|
);
|
||||||
self.decrement_connections_for_ip(&addr);
|
self.decrement_connections_for_ip(&addr);
|
||||||
self.connections_per_ip().compact(0.1);
|
self.as_mut().connections_per_ip().compact(0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn increment_connections_for_ip(self: &mut Pin<&mut Self>, peer: &SocketAddr) -> Option<usize> {
|
fn increment_connections_for_ip(self: &mut Pin<&mut Self>, peer: &SocketAddr) -> Option<usize> {
|
||||||
let max_connections_per_ip = self.config().max_connections_per_ip;
|
let max_connections_per_ip = self.as_mut().config().max_connections_per_ip;
|
||||||
let mut occupied;
|
let mut occupied;
|
||||||
let mut connections_per_ip = self.connections_per_ip();
|
let mut connections_per_ip = self.as_mut().connections_per_ip();
|
||||||
let occupied = match connections_per_ip.entry(peer.ip()) {
|
let occupied = match connections_per_ip.entry(peer.ip()) {
|
||||||
Entry::Vacant(vacant) => vacant.insert(0),
|
Entry::Vacant(vacant) => vacant.insert(0),
|
||||||
Entry::Occupied(o) => {
|
Entry::Occupied(o) => {
|
||||||
@@ -171,7 +177,7 @@ impl<S, Req, Resp> ConnectionFilter<S, Req, Resp> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn decrement_connections_for_ip(self: &mut Pin<&mut Self>, addr: &SocketAddr) {
|
fn decrement_connections_for_ip(self: &mut Pin<&mut Self>, addr: &SocketAddr) {
|
||||||
let should_compact = match self.connections_per_ip().entry(addr.ip()) {
|
let should_compact = match self.as_mut().connections_per_ip().entry(addr.ip()) {
|
||||||
Entry::Vacant(_) => {
|
Entry::Vacant(_) => {
|
||||||
error!("[{}] Got vacant entry when closing connection.", addr);
|
error!("[{}] Got vacant entry when closing connection.", addr);
|
||||||
return;
|
return;
|
||||||
@@ -187,19 +193,19 @@ impl<S, Req, Resp> ConnectionFilter<S, Req, Resp> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
if should_compact {
|
if should_compact {
|
||||||
self.connections_per_ip().compact(0.1);
|
self.as_mut().connections_per_ip().compact(0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_listener<C>(
|
fn poll_listener<C>(
|
||||||
self: &mut Pin<&mut Self>,
|
mut self: Pin<&mut Self>,
|
||||||
cx: &LocalWaker,
|
cx: &mut Context<'_>,
|
||||||
) -> Poll<Option<io::Result<NewConnection<Req, Resp, C>>>>
|
) -> PollIo<NewConnection<Req, Resp, C>>
|
||||||
where
|
where
|
||||||
S: Stream<Item = Result<C, io::Error>>,
|
S: Stream<Item = Result<C, io::Error>>,
|
||||||
C: Transport<Item = ClientMessage<Req>, SinkItem = Response<Resp>> + Send,
|
C: Transport<Item = ClientMessage<Req>, SinkItem = Response<Resp>> + Send,
|
||||||
{
|
{
|
||||||
match ready!(self.listener().poll_next_unpin(cx)?) {
|
match ready!(self.as_mut().listener().poll_next_unpin(cx)?) {
|
||||||
Some(codec) => Poll::Ready(Some(Ok(self.handle_new_connection(codec)))),
|
Some(codec) => Poll::Ready(Some(Ok(self.handle_new_connection(codec)))),
|
||||||
None => Poll::Ready(None),
|
None => Poll::Ready(None),
|
||||||
}
|
}
|
||||||
@@ -207,9 +213,9 @@ impl<S, Req, Resp> ConnectionFilter<S, Req, Resp> {
|
|||||||
|
|
||||||
fn poll_closed_connections(
|
fn poll_closed_connections(
|
||||||
self: &mut Pin<&mut Self>,
|
self: &mut Pin<&mut Self>,
|
||||||
cx: &LocalWaker,
|
cx: &mut Context<'_>,
|
||||||
) -> Poll<io::Result<()>> {
|
) -> Poll<io::Result<()>> {
|
||||||
match ready!(self.closed_connections_rx().poll_next_unpin(cx)) {
|
match ready!(self.as_mut().closed_connections_rx().poll_next_unpin(cx)) {
|
||||||
Some(addr) => {
|
Some(addr) => {
|
||||||
self.handle_closed_connection(&addr);
|
self.handle_closed_connection(&addr);
|
||||||
Poll::Ready(Ok(()))
|
Poll::Ready(Ok(()))
|
||||||
@@ -226,25 +232,28 @@ where
|
|||||||
{
|
{
|
||||||
type Item = io::Result<Channel<Req, Resp, T>>;
|
type Item = io::Result<Channel<Req, Resp, T>>;
|
||||||
|
|
||||||
fn poll_next(
|
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> PollIo<Channel<Req, Resp, T>> {
|
||||||
mut self: Pin<&mut Self>,
|
|
||||||
cx: &LocalWaker,
|
|
||||||
) -> Poll<Option<io::Result<Channel<Req, Resp, T>>>> {
|
|
||||||
loop {
|
loop {
|
||||||
match (self.poll_listener(cx)?, self.poll_closed_connections(cx)?) {
|
match (
|
||||||
|
self.as_mut().poll_listener(cx)?,
|
||||||
|
self.poll_closed_connections(cx)?,
|
||||||
|
) {
|
||||||
(Poll::Ready(Some(NewConnection::Accepted(channel))), _) => {
|
(Poll::Ready(Some(NewConnection::Accepted(channel))), _) => {
|
||||||
return Poll::Ready(Some(Ok(channel)))
|
return Poll::Ready(Some(Ok(channel)));
|
||||||
}
|
}
|
||||||
(Poll::Ready(Some(NewConnection::Filtered)), _) | (_, Poll::Ready(())) => {
|
(Poll::Ready(Some(NewConnection::Filtered)), _) | (_, Poll::Ready(())) => {
|
||||||
trace!("Filtered a connection; {} open.", self.open_connections());
|
trace!(
|
||||||
|
"Filtered a connection; {} open.",
|
||||||
|
self.as_mut().open_connections()
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
(Poll::Pending, Poll::Pending) => return Poll::Pending,
|
(Poll::Pending, Poll::Pending) => return Poll::Pending,
|
||||||
(Poll::Ready(None), Poll::Pending) => {
|
(Poll::Ready(None), Poll::Pending) => {
|
||||||
if *self.open_connections() > 0 {
|
if *self.as_mut().open_connections() > 0 {
|
||||||
trace!(
|
trace!(
|
||||||
"Listener closed; {} open connections.",
|
"Listener closed; {} open connections.",
|
||||||
self.open_connections()
|
self.as_mut().open_connections()
|
||||||
);
|
);
|
||||||
return Poll::Pending;
|
return Poll::Pending;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
//! Provides a server that concurrently handles many connections sending multiplexed requests.
|
//! Provides a server that concurrently handles many connections sending multiplexed requests.
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::Context, util::deadline_compat, util::AsDuration, util::Compact, ClientMessage,
|
context, util::deadline_compat, util::AsDuration, util::Compact, ClientMessage,
|
||||||
ClientMessageKind, Request, Response, ServerError, Transport,
|
ClientMessageKind, PollIo, Request, Response, ServerError, Transport,
|
||||||
};
|
};
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use futures::{
|
use futures::{
|
||||||
@@ -17,7 +17,7 @@ use futures::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
ready,
|
ready,
|
||||||
stream::Fuse,
|
stream::Fuse,
|
||||||
task::{LocalWaker, Poll},
|
task::{Context, Poll},
|
||||||
try_ready,
|
try_ready,
|
||||||
};
|
};
|
||||||
use humantime::format_rfc3339;
|
use humantime::format_rfc3339;
|
||||||
@@ -43,6 +43,12 @@ pub struct Server<Req, Resp> {
|
|||||||
ghost: PhantomData<(Req, Resp)>,
|
ghost: PhantomData<(Req, Resp)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<Req, Resp> Default for Server<Req, Resp> {
|
||||||
|
fn default() -> Self {
|
||||||
|
new(Config::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Settings that control the behavior of the server.
|
/// Settings that control the behavior of the server.
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@@ -75,15 +81,15 @@ impl Default for Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Req, Resp> Server<Req, Resp> {
|
/// Returns a new server with configuration specified `config`.
|
||||||
/// Returns a new server with configuration specified `config`.
|
pub fn new<Req, Resp>(config: Config) -> Server<Req, Resp> {
|
||||||
pub fn new(config: Config) -> Self {
|
Server {
|
||||||
Server {
|
config,
|
||||||
config,
|
ghost: PhantomData,
|
||||||
ghost: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Req, Resp> Server<Req, Resp> {
|
||||||
/// Returns the config for this server.
|
/// Returns the config for this server.
|
||||||
pub fn config(&self) -> &Config {
|
pub fn config(&self) -> &Config {
|
||||||
&self.config
|
&self.config
|
||||||
@@ -122,17 +128,18 @@ where
|
|||||||
Req: Send + 'static,
|
Req: Send + 'static,
|
||||||
Resp: Send + 'static,
|
Resp: Send + 'static,
|
||||||
T: Transport<Item = ClientMessage<Req>, SinkItem = Response<Resp>> + Send + 'static,
|
T: Transport<Item = ClientMessage<Req>, SinkItem = Response<Resp>> + Send + 'static,
|
||||||
F: FnMut(Context, Req) -> Fut + Send + 'static + Clone,
|
F: FnOnce(context::Context, Req) -> Fut + Send + 'static + Clone,
|
||||||
Fut: Future<Output = io::Result<Resp>> + Send + 'static,
|
Fut: Future<Output = io::Result<Resp>> + Send + 'static,
|
||||||
{
|
{
|
||||||
type Output = ();
|
type Output = ();
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &LocalWaker) -> Poll<()> {
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
|
||||||
while let Some(channel) = ready!(self.incoming().poll_next(cx)) {
|
while let Some(channel) = ready!(self.as_mut().incoming().poll_next(cx)) {
|
||||||
match channel {
|
match channel {
|
||||||
Ok(channel) => {
|
Ok(channel) => {
|
||||||
let peer = channel.client_addr;
|
let peer = channel.client_addr;
|
||||||
if let Err(e) = crate::spawn(channel.respond_with(self.request_handler().clone()))
|
if let Err(e) =
|
||||||
|
crate::spawn(channel.respond_with(self.as_mut().request_handler().clone()))
|
||||||
{
|
{
|
||||||
warn!("[{}] Failed to spawn connection handler: {:?}", peer, e);
|
warn!("[{}] Failed to spawn connection handler: {:?}", peer, e);
|
||||||
}
|
}
|
||||||
@@ -143,7 +150,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
info!("Server shutting down.");
|
info!("Server shutting down.");
|
||||||
return Poll::Ready(());
|
Poll::Ready(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,7 +165,7 @@ where
|
|||||||
/// Responds to all requests with `request_handler`.
|
/// Responds to all requests with `request_handler`.
|
||||||
fn respond_with<F, Fut>(self, request_handler: F) -> Running<Self, F>
|
fn respond_with<F, Fut>(self, request_handler: F) -> Running<Self, F>
|
||||||
where
|
where
|
||||||
F: FnMut(Context, Req) -> Fut + Send + 'static + Clone,
|
F: FnOnce(context::Context, Req) -> Fut + Send + 'static + Clone,
|
||||||
Fut: Future<Output = io::Result<Resp>> + Send + 'static,
|
Fut: Future<Output = io::Result<Resp>> + Send + 'static,
|
||||||
{
|
{
|
||||||
Running {
|
Running {
|
||||||
@@ -174,7 +181,8 @@ where
|
|||||||
Req: Send,
|
Req: Send,
|
||||||
Resp: Send,
|
Resp: Send,
|
||||||
T: Transport<Item = ClientMessage<Req>, SinkItem = Response<Resp>> + Send,
|
T: Transport<Item = ClientMessage<Req>, SinkItem = Response<Resp>> + Send,
|
||||||
{}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// Responds to all requests with `request_handler`.
|
/// Responds to all requests with `request_handler`.
|
||||||
/// The server end of an open connection with a client.
|
/// The server end of an open connection with a client.
|
||||||
@@ -222,29 +230,29 @@ where
|
|||||||
Req: Send,
|
Req: Send,
|
||||||
Resp: Send,
|
Resp: Send,
|
||||||
{
|
{
|
||||||
pub(crate) fn start_send(self: &mut Pin<&mut Self>, response: Response<Resp>) -> io::Result<()> {
|
pub(crate) fn start_send(mut self: Pin<&mut Self>, response: Response<Resp>) -> io::Result<()> {
|
||||||
self.transport().start_send(response)
|
self.as_mut().transport().start_send(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn poll_ready(
|
pub(crate) fn poll_ready(
|
||||||
self: &mut Pin<&mut Self>,
|
mut self: Pin<&mut Self>,
|
||||||
cx: &LocalWaker,
|
cx: &mut Context<'_>,
|
||||||
) -> Poll<io::Result<()>> {
|
) -> Poll<io::Result<()>> {
|
||||||
self.transport().poll_ready(cx)
|
self.as_mut().transport().poll_ready(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn poll_flush(
|
pub(crate) fn poll_flush(
|
||||||
self: &mut Pin<&mut Self>,
|
mut self: Pin<&mut Self>,
|
||||||
cx: &LocalWaker,
|
cx: &mut Context<'_>,
|
||||||
) -> Poll<io::Result<()>> {
|
) -> Poll<io::Result<()>> {
|
||||||
self.transport().poll_flush(cx)
|
self.as_mut().transport().poll_flush(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn poll_next(
|
pub(crate) fn poll_next(
|
||||||
self: &mut Pin<&mut Self>,
|
mut self: Pin<&mut Self>,
|
||||||
cx: &LocalWaker,
|
cx: &mut Context<'_>,
|
||||||
) -> Poll<Option<io::Result<ClientMessage<Req>>>> {
|
) -> PollIo<ClientMessage<Req>> {
|
||||||
self.transport().poll_next(cx)
|
self.as_mut().transport().poll_next(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the address of the client connected to the channel.
|
/// Returns the address of the client connected to the channel.
|
||||||
@@ -256,7 +264,7 @@ where
|
|||||||
/// responses and resolves when the connection is closed.
|
/// responses and resolves when the connection is closed.
|
||||||
pub fn respond_with<F, Fut>(self, f: F) -> impl Future<Output = ()>
|
pub fn respond_with<F, Fut>(self, f: F) -> impl Future<Output = ()>
|
||||||
where
|
where
|
||||||
F: FnMut(Context, Req) -> Fut + Send + 'static,
|
F: FnOnce(context::Context, Req) -> Fut + Send + 'static + Clone,
|
||||||
Fut: Future<Output = io::Result<Resp>> + Send + 'static,
|
Fut: Future<Output = io::Result<Resp>> + Send + 'static,
|
||||||
Req: 'static,
|
Req: 'static,
|
||||||
Resp: 'static,
|
Resp: 'static,
|
||||||
@@ -271,7 +279,8 @@ where
|
|||||||
pending_responses: responses,
|
pending_responses: responses,
|
||||||
responses_tx,
|
responses_tx,
|
||||||
in_flight_requests: FnvHashMap::default(),
|
in_flight_requests: FnvHashMap::default(),
|
||||||
}.unwrap_or_else(move |e| {
|
}
|
||||||
|
.unwrap_or_else(move |e| {
|
||||||
info!("[{}] ClientHandler errored out: {}", peer, e);
|
info!("[{}] ClientHandler errored out: {}", peer, e);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -281,9 +290,9 @@ where
|
|||||||
struct ClientHandler<Req, Resp, T, F> {
|
struct ClientHandler<Req, Resp, T, F> {
|
||||||
channel: Channel<Req, Resp, T>,
|
channel: Channel<Req, Resp, T>,
|
||||||
/// Responses waiting to be written to the wire.
|
/// Responses waiting to be written to the wire.
|
||||||
pending_responses: Fuse<mpsc::Receiver<(Context, Response<Resp>)>>,
|
pending_responses: Fuse<mpsc::Receiver<(context::Context, Response<Resp>)>>,
|
||||||
/// Handed out to request handlers to fan in responses.
|
/// Handed out to request handlers to fan in responses.
|
||||||
responses_tx: mpsc::Sender<(Context, Response<Resp>)>,
|
responses_tx: mpsc::Sender<(context::Context, Response<Resp>)>,
|
||||||
/// Number of requests currently being responded to.
|
/// Number of requests currently being responded to.
|
||||||
in_flight_requests: FnvHashMap<u64, AbortHandle>,
|
in_flight_requests: FnvHashMap<u64, AbortHandle>,
|
||||||
/// Request handler.
|
/// Request handler.
|
||||||
@@ -293,8 +302,8 @@ struct ClientHandler<Req, Resp, T, F> {
|
|||||||
impl<Req, Resp, T, F> ClientHandler<Req, Resp, T, F> {
|
impl<Req, Resp, T, F> ClientHandler<Req, Resp, T, F> {
|
||||||
unsafe_pinned!(channel: Channel<Req, Resp, T>);
|
unsafe_pinned!(channel: Channel<Req, Resp, T>);
|
||||||
unsafe_pinned!(in_flight_requests: FnvHashMap<u64, AbortHandle>);
|
unsafe_pinned!(in_flight_requests: FnvHashMap<u64, AbortHandle>);
|
||||||
unsafe_pinned!(pending_responses: Fuse<mpsc::Receiver<(Context, Response<Resp>)>>);
|
unsafe_pinned!(pending_responses: Fuse<mpsc::Receiver<(context::Context, Response<Resp>)>>);
|
||||||
unsafe_pinned!(responses_tx: mpsc::Sender<(Context, Response<Resp>)>);
|
unsafe_pinned!(responses_tx: mpsc::Sender<(context::Context, Response<Resp>)>);
|
||||||
// For this to be safe, field f must be private, and code in this module must never
|
// For this to be safe, field f must be private, and code in this module must never
|
||||||
// construct PinMut<F>.
|
// construct PinMut<F>.
|
||||||
unsafe_unpinned!(f: F);
|
unsafe_unpinned!(f: F);
|
||||||
@@ -305,36 +314,36 @@ where
|
|||||||
Req: Send + 'static,
|
Req: Send + 'static,
|
||||||
Resp: Send + 'static,
|
Resp: Send + 'static,
|
||||||
T: Transport<Item = ClientMessage<Req>, SinkItem = Response<Resp>> + Send,
|
T: Transport<Item = ClientMessage<Req>, SinkItem = Response<Resp>> + Send,
|
||||||
F: FnMut(Context, Req) -> Fut + Send + 'static,
|
F: FnOnce(context::Context, Req) -> Fut + Send + 'static + Clone,
|
||||||
Fut: Future<Output = io::Result<Resp>> + Send + 'static,
|
Fut: Future<Output = io::Result<Resp>> + Send + 'static,
|
||||||
{
|
{
|
||||||
/// If at max in-flight requests, check that there's room to immediately write a throttled
|
/// If at max in-flight requests, check that there's room to immediately write a throttled
|
||||||
/// response.
|
/// response.
|
||||||
fn poll_ready_if_throttling(
|
fn poll_ready_if_throttling(
|
||||||
self: &mut Pin<&mut Self>,
|
mut self: Pin<&mut Self>,
|
||||||
cx: &LocalWaker,
|
cx: &mut Context<'_>,
|
||||||
) -> Poll<io::Result<()>> {
|
) -> Poll<io::Result<()>> {
|
||||||
if self.in_flight_requests.len()
|
if self.in_flight_requests.len()
|
||||||
>= self.channel.config.max_in_flight_requests_per_connection
|
>= self.channel.config.max_in_flight_requests_per_connection
|
||||||
{
|
{
|
||||||
let peer = self.channel().client_addr;
|
let peer = self.as_mut().channel().client_addr;
|
||||||
|
|
||||||
while let Poll::Pending = self.channel().poll_ready(cx)? {
|
while let Poll::Pending = self.as_mut().channel().poll_ready(cx)? {
|
||||||
info!(
|
info!(
|
||||||
"[{}] In-flight requests at max ({}), and transport is not ready.",
|
"[{}] In-flight requests at max ({}), and transport is not ready.",
|
||||||
peer,
|
peer,
|
||||||
self.in_flight_requests().len(),
|
self.as_mut().in_flight_requests().len(),
|
||||||
);
|
);
|
||||||
try_ready!(self.channel().poll_flush(cx));
|
try_ready!(self.as_mut().channel().poll_flush(cx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Poll::Ready(Ok(()))
|
Poll::Ready(Ok(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pump_read(self: &mut Pin<&mut Self>, cx: &LocalWaker) -> Poll<Option<io::Result<()>>> {
|
fn pump_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> PollIo<()> {
|
||||||
ready!(self.poll_ready_if_throttling(cx)?);
|
ready!(self.as_mut().poll_ready_if_throttling(cx)?);
|
||||||
|
|
||||||
Poll::Ready(match ready!(self.channel().poll_next(cx)?) {
|
Poll::Ready(match ready!(self.as_mut().channel().poll_next(cx)?) {
|
||||||
Some(message) => {
|
Some(message) => {
|
||||||
match message.message {
|
match message.message {
|
||||||
ClientMessageKind::Request(request) => {
|
ClientMessageKind::Request(request) => {
|
||||||
@@ -354,28 +363,28 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn pump_write(
|
fn pump_write(
|
||||||
self: &mut Pin<&mut Self>,
|
mut self: Pin<&mut Self>,
|
||||||
cx: &LocalWaker,
|
cx: &mut Context<'_>,
|
||||||
read_half_closed: bool,
|
read_half_closed: bool,
|
||||||
) -> Poll<Option<io::Result<()>>> {
|
) -> PollIo<()> {
|
||||||
match self.poll_next_response(cx)? {
|
match self.as_mut().poll_next_response(cx)? {
|
||||||
Poll::Ready(Some((_, response))) => {
|
Poll::Ready(Some((_, response))) => {
|
||||||
self.channel().start_send(response)?;
|
self.as_mut().channel().start_send(response)?;
|
||||||
Poll::Ready(Some(Ok(())))
|
Poll::Ready(Some(Ok(())))
|
||||||
}
|
}
|
||||||
Poll::Ready(None) => {
|
Poll::Ready(None) => {
|
||||||
// Shutdown can't be done before we finish pumping out remaining responses.
|
// Shutdown can't be done before we finish pumping out remaining responses.
|
||||||
ready!(self.channel().poll_flush(cx)?);
|
ready!(self.as_mut().channel().poll_flush(cx)?);
|
||||||
Poll::Ready(None)
|
Poll::Ready(None)
|
||||||
}
|
}
|
||||||
Poll::Pending => {
|
Poll::Pending => {
|
||||||
// No more requests to process, so flush any requests buffered in the transport.
|
// No more requests to process, so flush any requests buffered in the transport.
|
||||||
ready!(self.channel().poll_flush(cx)?);
|
ready!(self.as_mut().channel().poll_flush(cx)?);
|
||||||
|
|
||||||
// Being here means there are no staged requests and all written responses are
|
// Being here means there are no staged requests and all written responses are
|
||||||
// fully flushed. So, if the read half is closed and there are no in-flight
|
// fully flushed. So, if the read half is closed and there are no in-flight
|
||||||
// requests, then we can close the write half.
|
// requests, then we can close the write half.
|
||||||
if read_half_closed && self.in_flight_requests().is_empty() {
|
if read_half_closed && self.as_mut().in_flight_requests().is_empty() {
|
||||||
Poll::Ready(None)
|
Poll::Ready(None)
|
||||||
} else {
|
} else {
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
@@ -385,28 +394,33 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn poll_next_response(
|
fn poll_next_response(
|
||||||
self: &mut Pin<&mut Self>,
|
mut self: Pin<&mut Self>,
|
||||||
cx: &LocalWaker,
|
cx: &mut Context<'_>,
|
||||||
) -> Poll<Option<io::Result<(Context, Response<Resp>)>>> {
|
) -> PollIo<(context::Context, Response<Resp>)> {
|
||||||
// Ensure there's room to write a response.
|
// Ensure there's room to write a response.
|
||||||
while let Poll::Pending = self.channel().poll_ready(cx)? {
|
while let Poll::Pending = self.as_mut().channel().poll_ready(cx)? {
|
||||||
ready!(self.channel().poll_flush(cx)?);
|
ready!(self.as_mut().channel().poll_flush(cx)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
let peer = self.channel().client_addr;
|
let peer = self.as_mut().channel().client_addr;
|
||||||
|
|
||||||
match ready!(self.pending_responses().poll_next(cx)) {
|
match ready!(self.as_mut().pending_responses().poll_next(cx)) {
|
||||||
Some((ctx, response)) => {
|
Some((ctx, response)) => {
|
||||||
if let Some(_) = self.in_flight_requests().remove(&response.request_id) {
|
if self
|
||||||
self.in_flight_requests().compact(0.1);
|
.as_mut()
|
||||||
|
.in_flight_requests()
|
||||||
|
.remove(&response.request_id)
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
self.as_mut().in_flight_requests().compact(0.1);
|
||||||
}
|
}
|
||||||
trace!(
|
trace!(
|
||||||
"[{}/{}] Staging response. In-flight requests = {}.",
|
"[{}/{}] Staging response. In-flight requests = {}.",
|
||||||
ctx.trace_id(),
|
ctx.trace_id(),
|
||||||
peer,
|
peer,
|
||||||
self.in_flight_requests().len(),
|
self.as_mut().in_flight_requests().len(),
|
||||||
);
|
);
|
||||||
return Poll::Ready(Some(Ok((ctx, response))));
|
Poll::Ready(Some(Ok((ctx, response))))
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// This branch likely won't happen, since the ClientHandler is holding a Sender.
|
// This branch likely won't happen, since the ClientHandler is holding a Sender.
|
||||||
@@ -417,30 +431,37 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_request(
|
fn handle_request(
|
||||||
self: &mut Pin<&mut Self>,
|
mut self: Pin<&mut Self>,
|
||||||
trace_context: trace::Context,
|
trace_context: trace::Context,
|
||||||
request: Request<Req>,
|
request: Request<Req>,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
let request_id = request.id;
|
let request_id = request.id;
|
||||||
let peer = self.channel().client_addr;
|
let peer = self.as_mut().channel().client_addr;
|
||||||
let ctx = Context {
|
let ctx = context::Context {
|
||||||
deadline: request.deadline,
|
deadline: request.deadline,
|
||||||
trace_context,
|
trace_context,
|
||||||
};
|
};
|
||||||
let request = request.message;
|
let request = request.message;
|
||||||
|
|
||||||
if self.in_flight_requests().len()
|
if self.as_mut().in_flight_requests().len()
|
||||||
>= self.channel().config.max_in_flight_requests_per_connection
|
>= self
|
||||||
|
.as_mut()
|
||||||
|
.channel()
|
||||||
|
.config
|
||||||
|
.max_in_flight_requests_per_connection
|
||||||
{
|
{
|
||||||
debug!(
|
debug!(
|
||||||
"[{}/{}] Client has reached in-flight request limit ({}/{}).",
|
"[{}/{}] Client has reached in-flight request limit ({}/{}).",
|
||||||
ctx.trace_id(),
|
ctx.trace_id(),
|
||||||
peer,
|
peer,
|
||||||
self.in_flight_requests().len(),
|
self.as_mut().in_flight_requests().len(),
|
||||||
self.channel().config.max_in_flight_requests_per_connection
|
self.as_mut()
|
||||||
|
.channel()
|
||||||
|
.config
|
||||||
|
.max_in_flight_requests_per_connection
|
||||||
);
|
);
|
||||||
|
|
||||||
self.channel().start_send(Response {
|
self.as_mut().channel().start_send(Response {
|
||||||
request_id,
|
request_id,
|
||||||
message: Err(ServerError {
|
message: Err(ServerError {
|
||||||
kind: io::ErrorKind::WouldBlock,
|
kind: io::ErrorKind::WouldBlock,
|
||||||
@@ -459,10 +480,10 @@ where
|
|||||||
format_rfc3339(deadline),
|
format_rfc3339(deadline),
|
||||||
timeout,
|
timeout,
|
||||||
);
|
);
|
||||||
let mut response_tx = self.responses_tx().clone();
|
let mut response_tx = self.as_mut().responses_tx().clone();
|
||||||
|
|
||||||
let trace_id = *ctx.trace_id();
|
let trace_id = *ctx.trace_id();
|
||||||
let response = self.f()(ctx.clone(), request);
|
let response = self.as_mut().f().clone()(ctx, request);
|
||||||
let response = deadline_compat::Deadline::new(response, Instant::now() + timeout).then(
|
let response = deadline_compat::Deadline::new(response, Instant::now() + timeout).then(
|
||||||
async move |result| {
|
async move |result| {
|
||||||
let response = Response {
|
let response = Response {
|
||||||
@@ -473,32 +494,36 @@ where
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
trace!("[{}/{}] Sending response.", trace_id, peer);
|
trace!("[{}/{}] Sending response.", trace_id, peer);
|
||||||
await!(response_tx.send((ctx, response)).unwrap_or_else(|_| ()));
|
response_tx
|
||||||
|
.send((ctx, response))
|
||||||
|
.unwrap_or_else(|_| ())
|
||||||
|
.await;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
let (abortable_response, abort_handle) = abortable(response);
|
let (abortable_response, abort_handle) = abortable(response);
|
||||||
crate::spawn(abortable_response.map(|_| ()))
|
crate::spawn(abortable_response.map(|_| ())).map_err(|e| {
|
||||||
.map_err(|e| {
|
io::Error::new(
|
||||||
io::Error::new(
|
io::ErrorKind::Other,
|
||||||
io::ErrorKind::Other,
|
format!(
|
||||||
format!(
|
"Could not spawn response task. Is shutdown: {}",
|
||||||
"Could not spawn response task. Is shutdown: {}",
|
e.is_shutdown()
|
||||||
e.is_shutdown()
|
),
|
||||||
),
|
)
|
||||||
)
|
})?;
|
||||||
})?;
|
self.as_mut()
|
||||||
self.in_flight_requests().insert(request_id, abort_handle);
|
.in_flight_requests()
|
||||||
|
.insert(request_id, abort_handle);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cancel_request(self: &mut Pin<&mut Self>, trace_context: &trace::Context, request_id: u64) {
|
fn cancel_request(mut self: Pin<&mut Self>, trace_context: &trace::Context, request_id: u64) {
|
||||||
// It's possible the request was already completed, so it's fine
|
// It's possible the request was already completed, so it's fine
|
||||||
// if this is None.
|
// if this is None.
|
||||||
if let Some(cancel_handle) = self.in_flight_requests().remove(&request_id) {
|
if let Some(cancel_handle) = self.as_mut().in_flight_requests().remove(&request_id) {
|
||||||
self.in_flight_requests().compact(0.1);
|
self.as_mut().in_flight_requests().compact(0.1);
|
||||||
|
|
||||||
cancel_handle.abort();
|
cancel_handle.abort();
|
||||||
let remaining = self.in_flight_requests().len();
|
let remaining = self.as_mut().in_flight_requests().len();
|
||||||
trace!(
|
trace!(
|
||||||
"[{}/{}] Request canceled. In-flight requests = {}",
|
"[{}/{}] Request canceled. In-flight requests = {}",
|
||||||
trace_context.trace_id,
|
trace_context.trace_id,
|
||||||
@@ -521,16 +546,19 @@ where
|
|||||||
Req: Send + 'static,
|
Req: Send + 'static,
|
||||||
Resp: Send + 'static,
|
Resp: Send + 'static,
|
||||||
T: Transport<Item = ClientMessage<Req>, SinkItem = Response<Resp>> + Send,
|
T: Transport<Item = ClientMessage<Req>, SinkItem = Response<Resp>> + Send,
|
||||||
F: FnMut(Context, Req) -> Fut + Send + 'static,
|
F: FnOnce(context::Context, Req) -> Fut + Send + 'static + Clone,
|
||||||
Fut: Future<Output = io::Result<Resp>> + Send + 'static,
|
Fut: Future<Output = io::Result<Resp>> + Send + 'static,
|
||||||
{
|
{
|
||||||
type Output = io::Result<()>;
|
type Output = io::Result<()>;
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &LocalWaker) -> Poll<io::Result<()>> {
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
trace!("[{}] ClientHandler::poll", self.channel.client_addr);
|
trace!("[{}] ClientHandler::poll", self.channel.client_addr);
|
||||||
loop {
|
loop {
|
||||||
let read = self.pump_read(cx)?;
|
let read = self.as_mut().pump_read(cx)?;
|
||||||
match (read, self.pump_write(cx, read == Poll::Ready(None))?) {
|
match (
|
||||||
|
read,
|
||||||
|
self.as_mut().pump_write(cx, read == Poll::Ready(None))?,
|
||||||
|
) {
|
||||||
(Poll::Ready(None), Poll::Ready(None)) => {
|
(Poll::Ready(None), Poll::Ready(None)) => {
|
||||||
info!("[{}] Client disconnected.", self.channel.client_addr);
|
info!("[{}] Client disconnected.", self.channel.client_addr);
|
||||||
return Poll::Ready(Ok(()));
|
return Poll::Ready(Ok(()));
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
|
|
||||||
//! Transports backed by in-memory channels.
|
//! Transports backed by in-memory channels.
|
||||||
|
|
||||||
use crate::Transport;
|
use crate::{PollIo, Transport};
|
||||||
use futures::{channel::mpsc, task::{LocalWaker}, Poll, Sink, Stream};
|
use futures::{channel::mpsc, task::Context, Poll, Sink, Stream};
|
||||||
use pin_utils::unsafe_pinned;
|
use pin_utils::unsafe_pinned;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::{
|
use std::{
|
||||||
@@ -45,37 +45,33 @@ impl<Item, SinkItem> UnboundedChannel<Item, SinkItem> {
|
|||||||
impl<Item, SinkItem> Stream for UnboundedChannel<Item, SinkItem> {
|
impl<Item, SinkItem> Stream for UnboundedChannel<Item, SinkItem> {
|
||||||
type Item = Result<Item, io::Error>;
|
type Item = Result<Item, io::Error>;
|
||||||
|
|
||||||
fn poll_next(mut self: Pin<&mut Self>, cx: &LocalWaker) -> Poll<Option<io::Result<Item>>> {
|
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> PollIo<Item> {
|
||||||
self.rx().poll_next(cx).map(|option| option.map(Ok))
|
self.rx().poll_next(cx).map(|option| option.map(Ok))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Item, SinkItem> Sink for UnboundedChannel<Item, SinkItem> {
|
impl<Item, SinkItem> Sink<SinkItem> for UnboundedChannel<Item, SinkItem> {
|
||||||
type SinkItem = SinkItem;
|
|
||||||
type SinkError = io::Error;
|
type SinkError = io::Error;
|
||||||
|
|
||||||
fn poll_ready(mut self: Pin<&mut Self>, cx: &LocalWaker) -> Poll<io::Result<()>> {
|
fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
self.tx()
|
self.tx()
|
||||||
.poll_ready(cx)
|
.poll_ready(cx)
|
||||||
.map_err(|_| io::Error::from(io::ErrorKind::NotConnected))
|
.map_err(|_| io::Error::from(io::ErrorKind::NotConnected))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_send(mut self: Pin<&mut Self>, item: SinkItem) -> io::Result<()> {
|
fn start_send(self: Pin<&mut Self>, item: SinkItem) -> io::Result<()> {
|
||||||
self.tx()
|
self.tx()
|
||||||
.start_send(item)
|
.start_send(item)
|
||||||
.map_err(|_| io::Error::from(io::ErrorKind::NotConnected))
|
.map_err(|_| io::Error::from(io::ErrorKind::NotConnected))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_flush(
|
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::SinkError>> {
|
||||||
mut self: Pin<&mut Self>,
|
|
||||||
cx: &LocalWaker,
|
|
||||||
) -> Poll<Result<(), Self::SinkError>> {
|
|
||||||
self.tx()
|
self.tx()
|
||||||
.poll_flush(cx)
|
.poll_flush(cx)
|
||||||
.map_err(|_| io::Error::from(io::ErrorKind::NotConnected))
|
.map_err(|_| io::Error::from(io::ErrorKind::NotConnected))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_close(mut self: Pin<&mut Self>, cx: &LocalWaker) -> Poll<io::Result<()>> {
|
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
self.tx()
|
self.tx()
|
||||||
.poll_close(cx)
|
.poll_close(cx)
|
||||||
.map_err(|_| io::Error::from(io::ErrorKind::NotConnected))
|
.map_err(|_| io::Error::from(io::ErrorKind::NotConnected))
|
||||||
@@ -83,8 +79,8 @@ impl<Item, SinkItem> Sink for UnboundedChannel<Item, SinkItem> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<Item, SinkItem> Transport for UnboundedChannel<Item, SinkItem> {
|
impl<Item, SinkItem> Transport for UnboundedChannel<Item, SinkItem> {
|
||||||
type Item = Item;
|
|
||||||
type SinkItem = SinkItem;
|
type SinkItem = SinkItem;
|
||||||
|
type Item = Item;
|
||||||
|
|
||||||
fn peer_addr(&self) -> io::Result<SocketAddr> {
|
fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||||
Ok(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0))
|
Ok(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0))
|
||||||
@@ -97,18 +93,23 @@ impl<Item, SinkItem> Transport for UnboundedChannel<Item, SinkItem> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{client::{self, Client}, context, server::{self, Handler, Server}, transport};
|
use crate::{
|
||||||
use futures::{prelude::*, stream, compat::TokioDefaultSpawner};
|
client, context,
|
||||||
|
server::{Handler, Server},
|
||||||
|
transport,
|
||||||
|
};
|
||||||
|
use futures::compat::Executor01CompatExt;
|
||||||
|
use futures::{prelude::*, stream};
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn integration() {
|
fn integration() {
|
||||||
let _ = env_logger::try_init();
|
let _ = env_logger::try_init();
|
||||||
crate::init(TokioDefaultSpawner);
|
crate::init(tokio::executor::DefaultExecutor::current().compat());
|
||||||
|
|
||||||
let (client_channel, server_channel) = transport::channel::unbounded();
|
let (client_channel, server_channel) = transport::channel::unbounded();
|
||||||
let server = Server::<String, u64>::new(server::Config::default())
|
let server = Server::<String, u64>::default()
|
||||||
.incoming(stream::once(future::ready(Ok(server_channel))))
|
.incoming(stream::once(future::ready(Ok(server_channel))))
|
||||||
.respond_with(|_ctx, request| {
|
.respond_with(|_ctx, request| {
|
||||||
future::ready(request.parse::<u64>().map_err(|_| {
|
future::ready(request.parse::<u64>().map_err(|_| {
|
||||||
@@ -120,16 +121,19 @@ mod tests {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let responses = async {
|
let responses = async {
|
||||||
let mut client = await!(Client::new(client::Config::default(), client_channel))?;
|
let mut client = client::new(client::Config::default(), client_channel).await?;
|
||||||
|
|
||||||
let response1 = await!(client.call(context::current(), "123".into()));
|
let response1 = client.call(context::current(), "123".into()).await;
|
||||||
let response2 = await!(client.call(context::current(), "abc".into()));
|
let response2 = client.call(context::current(), "abc".into()).await;
|
||||||
|
|
||||||
Ok::<_, io::Error>((response1, response2))
|
Ok::<_, io::Error>((response1, response2))
|
||||||
};
|
};
|
||||||
|
|
||||||
let (response1, response2) =
|
let (response1, response2) = run_future(future::join(
|
||||||
run_future(server.join(responses.unwrap_or_else(|e| panic!(e)))).1;
|
server,
|
||||||
|
responses.unwrap_or_else(|e| panic!(e)),
|
||||||
|
))
|
||||||
|
.1;
|
||||||
|
|
||||||
trace!("response1: {:?}, response2: {:?}", response1, response2);
|
trace!("response1: {:?}, response2: {:?}", response1, response2);
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,13 @@
|
|||||||
//! can be plugged in, using whatever protocol it wants.
|
//! can be plugged in, using whatever protocol it wants.
|
||||||
|
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use std::{io, net::SocketAddr};
|
use std::{
|
||||||
|
io,
|
||||||
|
marker::PhantomData,
|
||||||
|
net::SocketAddr,
|
||||||
|
pin::Pin,
|
||||||
|
task::{Context, Poll},
|
||||||
|
};
|
||||||
|
|
||||||
pub mod channel;
|
pub mod channel;
|
||||||
|
|
||||||
@@ -18,7 +24,7 @@ pub mod channel;
|
|||||||
pub trait Transport
|
pub trait Transport
|
||||||
where
|
where
|
||||||
Self: Stream<Item = io::Result<<Self as Transport>::Item>>,
|
Self: Stream<Item = io::Result<<Self as Transport>::Item>>,
|
||||||
Self: Sink<SinkItem = <Self as Transport>::SinkItem, SinkError = io::Error>,
|
Self: Sink<<Self as Transport>::SinkItem, SinkError = io::Error>,
|
||||||
{
|
{
|
||||||
/// The type read off the transport.
|
/// The type read off the transport.
|
||||||
type Item;
|
type Item;
|
||||||
@@ -30,3 +36,88 @@ where
|
|||||||
/// The address of the local half of this transport.
|
/// The address of the local half of this transport.
|
||||||
fn local_addr(&self) -> io::Result<SocketAddr>;
|
fn local_addr(&self) -> io::Result<SocketAddr>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a new Transport backed by the given Stream + Sink and connecting addresses.
|
||||||
|
pub fn new<S, SinkItem, Item>(
|
||||||
|
inner: S,
|
||||||
|
peer_addr: SocketAddr,
|
||||||
|
local_addr: SocketAddr,
|
||||||
|
) -> impl Transport<Item = Item, SinkItem = SinkItem>
|
||||||
|
where
|
||||||
|
S: Stream<Item = io::Result<Item>>,
|
||||||
|
S: Sink<SinkItem, SinkError = io::Error>,
|
||||||
|
{
|
||||||
|
TransportShim {
|
||||||
|
inner,
|
||||||
|
peer_addr,
|
||||||
|
local_addr,
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A transport created by adding peers to a Stream + Sink.
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct TransportShim<S, SinkItem> {
|
||||||
|
peer_addr: SocketAddr,
|
||||||
|
local_addr: SocketAddr,
|
||||||
|
inner: S,
|
||||||
|
_marker: PhantomData<SinkItem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, SinkItem> TransportShim<S, SinkItem> {
|
||||||
|
pin_utils::unsafe_pinned!(inner: S);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, SinkItem> Stream for TransportShim<S, SinkItem>
|
||||||
|
where
|
||||||
|
S: Stream,
|
||||||
|
{
|
||||||
|
type Item = S::Item;
|
||||||
|
|
||||||
|
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<S::Item>> {
|
||||||
|
self.inner().poll_next(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, Item> Sink<Item> for TransportShim<S, Item>
|
||||||
|
where
|
||||||
|
S: Sink<Item>,
|
||||||
|
{
|
||||||
|
type SinkError = S::SinkError;
|
||||||
|
|
||||||
|
fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), S::SinkError> {
|
||||||
|
self.inner().start_send(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), S::SinkError>> {
|
||||||
|
self.inner().poll_ready(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), S::SinkError>> {
|
||||||
|
self.inner().poll_flush(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), S::SinkError>> {
|
||||||
|
self.inner().poll_close(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, SinkItem, Item> Transport for TransportShim<S, SinkItem>
|
||||||
|
where
|
||||||
|
S: Stream + Sink<SinkItem>,
|
||||||
|
Self: Stream<Item = io::Result<Item>>,
|
||||||
|
Self: Sink<SinkItem, SinkError = io::Error>,
|
||||||
|
{
|
||||||
|
type Item = Item;
|
||||||
|
type SinkItem = SinkItem;
|
||||||
|
|
||||||
|
/// The address of the remote peer this transport is in communication with.
|
||||||
|
fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||||
|
Ok(self.peer_addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The address of the local half of this transport.
|
||||||
|
fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||||
|
Ok(self.local_addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,9 +5,10 @@
|
|||||||
// https://opensource.org/licenses/MIT.
|
// https://opensource.org/licenses/MIT.
|
||||||
|
|
||||||
use futures::{
|
use futures::{
|
||||||
compat::{Compat01As03, Future01CompatExt},
|
compat::*,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
ready, task::{Poll, LocalWaker},
|
ready,
|
||||||
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
use pin_utils::unsafe_pinned;
|
use pin_utils::unsafe_pinned;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
@@ -49,16 +50,15 @@ where
|
|||||||
{
|
{
|
||||||
type Output = Result<T::Ok, timeout::Error<T::Error>>;
|
type Output = Result<T::Ok, timeout::Error<T::Error>>;
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, waker: &LocalWaker) -> Poll<Self::Output> {
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
|
||||||
// First, try polling the future
|
// First, try polling the future
|
||||||
match self.future().try_poll(waker) {
|
match self.as_mut().future().try_poll(cx) {
|
||||||
Poll::Ready(Ok(v)) => return Poll::Ready(Ok(v)),
|
Poll::Ready(Ok(v)) => return Poll::Ready(Ok(v)),
|
||||||
Poll::Pending => {}
|
Poll::Pending => {}
|
||||||
Poll::Ready(Err(e)) => return Poll::Ready(Err(timeout::Error::inner(e))),
|
Poll::Ready(Err(e)) => return Poll::Ready(Err(timeout::Error::inner(e))),
|
||||||
}
|
}
|
||||||
|
|
||||||
let delay = self.delay().poll_unpin(waker);
|
let delay = self.delay().poll_unpin(cx);
|
||||||
|
|
||||||
// Now check the timer
|
// Now check the timer
|
||||||
match ready!(delay) {
|
match ready!(delay) {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Serializes [`io::ErrorKind`] as a `u32`.
|
/// Serializes [`io::ErrorKind`] as a `u32`.
|
||||||
|
#[allow(clippy::trivially_copy_pass_by_ref)] // Exact fn signature required by serde derive
|
||||||
pub fn serialize_io_error_kind_as_u32<S>(
|
pub fn serialize_io_error_kind_as_u32<S>(
|
||||||
kind: &io::ErrorKind,
|
kind: &io::ErrorKind,
|
||||||
serializer: S,
|
serializer: S,
|
||||||
@@ -59,7 +60,8 @@ where
|
|||||||
Other => 16,
|
Other => 16,
|
||||||
UnexpectedEof => 17,
|
UnexpectedEof => 17,
|
||||||
_ => 16,
|
_ => 16,
|
||||||
}.serialize(serializer)
|
}
|
||||||
|
.serialize(serializer)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserializes [`io::ErrorKind`] from a `u32`.
|
/// Deserializes [`io::ErrorKind`] from a `u32`.
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
cargo-features = ["rename-dependency"]
|
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "tarpc"
|
name = "tarpc"
|
||||||
version = "0.13.0"
|
version = "0.18.0"
|
||||||
authors = ["Adam Wright <adam.austin.wright@gmail.com>", "Tim Kuehn <timothy.j.kuehn@gmail.com>"]
|
authors = ["Adam Wright <adam.austin.wright@gmail.com>", "Tim Kuehn <timothy.j.kuehn@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
@@ -21,18 +19,20 @@ serde1 = ["rpc/serde1", "serde", "serde/derive"]
|
|||||||
travis-ci = { repository = "google/tarpc" }
|
travis-ci = { repository = "google/tarpc" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
futures-preview = { version = "0.3.0-alpha.16", features = ["compat"] }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
serde = { optional = true, version = "1.0" }
|
serde = { optional = true, version = "1.0" }
|
||||||
|
rpc = { package = "tarpc-lib", path = "../rpc", version = "0.6" }
|
||||||
tarpc-plugins = { path = "../plugins", version = "0.5.0" }
|
tarpc-plugins = { path = "../plugins", version = "0.5.0" }
|
||||||
rpc = { package = "tarpc-lib", path = "../rpc", version = "0.1" }
|
|
||||||
|
|
||||||
[target.'cfg(not(test))'.dependencies]
|
|
||||||
futures-preview = "0.3.0-alpha.8"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
bincode = "1"
|
||||||
|
bytes = { version = "0.4", features = ["serde"] }
|
||||||
humantime = "1.0"
|
humantime = "1.0"
|
||||||
futures-preview = { version = "0.3.0-alpha.8", features = ["compat", "tokio-compat"] }
|
bincode-transport = { package = "tarpc-bincode-transport", version = "0.7", path = "../bincode-transport" }
|
||||||
bincode-transport = { package = "tarpc-bincode-transport", version = "0.1", path = "../bincode-transport" }
|
env_logger = "0.6"
|
||||||
env_logger = "0.5"
|
libtest = "0.0.1"
|
||||||
tokio = "0.1"
|
tokio = "0.1"
|
||||||
tokio-executor = "0.1"
|
tokio-executor = "0.1"
|
||||||
|
tokio-tcp = "0.1"
|
||||||
|
pin-utils = "0.1.0-alpha.4"
|
||||||
|
|||||||
1
tarpc/README.md
Symbolic link
1
tarpc/README.md
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../README.md
|
||||||
@@ -6,12 +6,9 @@
|
|||||||
|
|
||||||
#![feature(
|
#![feature(
|
||||||
arbitrary_self_types,
|
arbitrary_self_types,
|
||||||
pin,
|
|
||||||
futures_api,
|
|
||||||
await_macro,
|
|
||||||
async_await,
|
async_await,
|
||||||
existential_type,
|
existential_type,
|
||||||
proc_macro_hygiene,
|
proc_macro_hygiene
|
||||||
)]
|
)]
|
||||||
|
|
||||||
use futures::{
|
use futures::{
|
||||||
@@ -55,7 +52,7 @@ struct Subscriber {
|
|||||||
impl subscriber::Service for Subscriber {
|
impl subscriber::Service for Subscriber {
|
||||||
type ReceiveFut = Ready<()>;
|
type ReceiveFut = Ready<()>;
|
||||||
|
|
||||||
fn receive(&self, _: context::Context, message: String) -> Self::ReceiveFut {
|
fn receive(self, _: context::Context, message: String) -> Self::ReceiveFut {
|
||||||
println!("{} received message: {}", self.id, message);
|
println!("{} received message: {}", self.id, message);
|
||||||
future::ready(())
|
future::ready(())
|
||||||
}
|
}
|
||||||
@@ -66,13 +63,13 @@ impl Subscriber {
|
|||||||
let incoming = bincode_transport::listen(&"0.0.0.0:0".parse().unwrap())?;
|
let incoming = bincode_transport::listen(&"0.0.0.0:0".parse().unwrap())?;
|
||||||
let addr = incoming.local_addr();
|
let addr = incoming.local_addr();
|
||||||
tokio_executor::spawn(
|
tokio_executor::spawn(
|
||||||
Server::new(config)
|
server::new(config)
|
||||||
.incoming(incoming)
|
.incoming(incoming)
|
||||||
.take(1)
|
.take(1)
|
||||||
.respond_with(subscriber::serve(Subscriber { id }))
|
.respond_with(subscriber::serve(Subscriber { id }))
|
||||||
.unit_error()
|
.unit_error()
|
||||||
.boxed()
|
.boxed()
|
||||||
.compat()
|
.compat(),
|
||||||
);
|
);
|
||||||
Ok(addr)
|
Ok(addr)
|
||||||
}
|
}
|
||||||
@@ -94,14 +91,14 @@ impl Publisher {
|
|||||||
impl publisher::Service for Publisher {
|
impl publisher::Service for Publisher {
|
||||||
existential type BroadcastFut: Future<Output = ()>;
|
existential type BroadcastFut: Future<Output = ()>;
|
||||||
|
|
||||||
fn broadcast(&self, _: context::Context, message: String) -> Self::BroadcastFut {
|
fn broadcast(self, _: context::Context, message: String) -> Self::BroadcastFut {
|
||||||
async fn broadcast(clients: Arc<Mutex<HashMap<u32, subscriber::Client>>>, message: String) {
|
async fn broadcast(clients: Arc<Mutex<HashMap<u32, subscriber::Client>>>, message: String) {
|
||||||
let mut clients = clients.lock().unwrap().clone();
|
let mut clients = clients.lock().unwrap().clone();
|
||||||
for client in clients.values_mut() {
|
for client in clients.values_mut() {
|
||||||
// Ignore failing subscribers. In a real pubsub,
|
// Ignore failing subscribers. In a real pubsub,
|
||||||
// you'd want to continually retry until subscribers
|
// you'd want to continually retry until subscribers
|
||||||
// ack.
|
// ack.
|
||||||
let _ = await!(client.receive(context::current(), message.clone()));
|
let _ = client.receive(context::current(), message.clone()).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,14 +107,14 @@ impl publisher::Service for Publisher {
|
|||||||
|
|
||||||
existential type SubscribeFut: Future<Output = Result<(), String>>;
|
existential type SubscribeFut: Future<Output = Result<(), String>>;
|
||||||
|
|
||||||
fn subscribe(&self, _: context::Context, id: u32, addr: SocketAddr) -> Self::SubscribeFut {
|
fn subscribe(self, _: context::Context, id: u32, addr: SocketAddr) -> Self::SubscribeFut {
|
||||||
async fn subscribe(
|
async fn subscribe(
|
||||||
clients: Arc<Mutex<HashMap<u32, subscriber::Client>>>,
|
clients: Arc<Mutex<HashMap<u32, subscriber::Client>>>,
|
||||||
id: u32,
|
id: u32,
|
||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
let conn = await!(bincode_transport::connect(&addr))?;
|
let conn = bincode_transport::connect(&addr).await?;
|
||||||
let subscriber = await!(subscriber::new_stub(client::Config::default(), conn))?;
|
let subscriber = subscriber::new_stub(client::Config::default(), conn).await?;
|
||||||
println!("Subscribing {}.", id);
|
println!("Subscribing {}.", id);
|
||||||
clients.lock().unwrap().insert(id, subscriber);
|
clients.lock().unwrap().insert(id, subscriber);
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -128,7 +125,7 @@ impl publisher::Service for Publisher {
|
|||||||
|
|
||||||
existential type UnsubscribeFut: Future<Output = ()>;
|
existential type UnsubscribeFut: Future<Output = ()>;
|
||||||
|
|
||||||
fn unsubscribe(&self, _: context::Context, id: u32) -> Self::UnsubscribeFut {
|
fn unsubscribe(self, _: context::Context, id: u32) -> Self::UnsubscribeFut {
|
||||||
println!("Unsubscribing {}", id);
|
println!("Unsubscribing {}", id);
|
||||||
let mut clients = self.clients.lock().unwrap();
|
let mut clients = self.clients.lock().unwrap();
|
||||||
if let None = clients.remove(&id) {
|
if let None = clients.remove(&id) {
|
||||||
@@ -146,46 +143,47 @@ async fn run() -> io::Result<()> {
|
|||||||
let transport = bincode_transport::listen(&"0.0.0.0:0".parse().unwrap())?;
|
let transport = bincode_transport::listen(&"0.0.0.0:0".parse().unwrap())?;
|
||||||
let publisher_addr = transport.local_addr();
|
let publisher_addr = transport.local_addr();
|
||||||
tokio_executor::spawn(
|
tokio_executor::spawn(
|
||||||
Server::new(server::Config::default())
|
Server::default()
|
||||||
.incoming(transport)
|
.incoming(transport)
|
||||||
.take(1)
|
.take(1)
|
||||||
.respond_with(publisher::serve(Publisher::new()))
|
.respond_with(publisher::serve(Publisher::new()))
|
||||||
.unit_error()
|
.unit_error()
|
||||||
.boxed()
|
.boxed()
|
||||||
.compat()
|
.compat(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let subscriber1 = await!(Subscriber::listen(0, server::Config::default()))?;
|
let subscriber1 = Subscriber::listen(0, server::Config::default()).await?;
|
||||||
let subscriber2 = await!(Subscriber::listen(1, server::Config::default()))?;
|
let subscriber2 = Subscriber::listen(1, server::Config::default()).await?;
|
||||||
|
|
||||||
let publisher_conn = bincode_transport::connect(&publisher_addr);
|
let publisher_conn = bincode_transport::connect(&publisher_addr);
|
||||||
let publisher_conn = await!(publisher_conn)?;
|
let publisher_conn = publisher_conn.await?;
|
||||||
let mut publisher = await!(publisher::new_stub(
|
let mut publisher = publisher::new_stub(client::Config::default(), publisher_conn).await?;
|
||||||
client::Config::default(),
|
|
||||||
publisher_conn
|
|
||||||
))?;
|
|
||||||
|
|
||||||
if let Err(e) = await!(publisher.subscribe(context::current(), 0, subscriber1))? {
|
if let Err(e) = publisher
|
||||||
|
.subscribe(context::current(), 0, subscriber1)
|
||||||
|
.await?
|
||||||
|
{
|
||||||
eprintln!("Couldn't subscribe subscriber 0: {}", e);
|
eprintln!("Couldn't subscribe subscriber 0: {}", e);
|
||||||
}
|
}
|
||||||
if let Err(e) = await!(publisher.subscribe(context::current(), 1, subscriber2))? {
|
if let Err(e) = publisher
|
||||||
|
.subscribe(context::current(), 1, subscriber2)
|
||||||
|
.await?
|
||||||
|
{
|
||||||
eprintln!("Couldn't subscribe subscriber 1: {}", e);
|
eprintln!("Couldn't subscribe subscriber 1: {}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Broadcasting...");
|
println!("Broadcasting...");
|
||||||
await!(publisher.broadcast(context::current(), "hello to all".to_string()))?;
|
publisher
|
||||||
await!(publisher.unsubscribe(context::current(), 1))?;
|
.broadcast(context::current(), "hello to all".to_string())
|
||||||
await!(publisher.broadcast(context::current(), "hi again".to_string()))?;
|
.await?;
|
||||||
|
publisher.unsubscribe(context::current(), 1).await?;
|
||||||
|
publisher
|
||||||
|
.broadcast(context::current(), "hi again".to_string())
|
||||||
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
tokio::run(
|
tokio::run(run().boxed().map_err(|e| panic!(e)).boxed().compat());
|
||||||
run()
|
|
||||||
.boxed()
|
|
||||||
.map_err(|e| panic!(e))
|
|
||||||
.boxed()
|
|
||||||
.compat(),
|
|
||||||
);
|
|
||||||
thread::sleep(Duration::from_millis(100));
|
thread::sleep(Duration::from_millis(100));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,22 +4,16 @@
|
|||||||
// license that can be found in the LICENSE file or at
|
// license that can be found in the LICENSE file or at
|
||||||
// https://opensource.org/licenses/MIT.
|
// https://opensource.org/licenses/MIT.
|
||||||
|
|
||||||
#![feature(
|
#![feature(arbitrary_self_types, async_await, proc_macro_hygiene)]
|
||||||
futures_api,
|
|
||||||
pin,
|
|
||||||
arbitrary_self_types,
|
|
||||||
await_macro,
|
|
||||||
async_await,
|
|
||||||
proc_macro_hygiene,
|
|
||||||
)]
|
|
||||||
|
|
||||||
use futures::{
|
use futures::{
|
||||||
|
compat::Executor01CompatExt,
|
||||||
future::{self, Ready},
|
future::{self, Ready},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
use rpc::{
|
use rpc::{
|
||||||
client, context,
|
client, context,
|
||||||
server::{self, Handler, Server},
|
server::{Handler, Server},
|
||||||
};
|
};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
@@ -41,7 +35,7 @@ impl Service for HelloServer {
|
|||||||
|
|
||||||
type HelloFut = Ready<String>;
|
type HelloFut = Ready<String>;
|
||||||
|
|
||||||
fn hello(&self, _: context::Context, name: String) -> Self::HelloFut {
|
fn hello(self, _: context::Context, name: String) -> Self::HelloFut {
|
||||||
future::ready(format!("Hello, {}!", name))
|
future::ready(format!("Hello, {}!", name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,7 +47,7 @@ async fn run() -> io::Result<()> {
|
|||||||
let addr = transport.local_addr();
|
let addr = transport.local_addr();
|
||||||
|
|
||||||
// The server is configured with the defaults.
|
// The server is configured with the defaults.
|
||||||
let server = Server::new(server::Config::default())
|
let server = Server::default()
|
||||||
// Server can listen on any type that implements the Transport trait.
|
// Server can listen on any type that implements the Transport trait.
|
||||||
.incoming(transport)
|
.incoming(transport)
|
||||||
// Close the stream after the client connects
|
// Close the stream after the client connects
|
||||||
@@ -64,17 +58,17 @@ async fn run() -> io::Result<()> {
|
|||||||
|
|
||||||
tokio_executor::spawn(server.unit_error().boxed().compat());
|
tokio_executor::spawn(server.unit_error().boxed().compat());
|
||||||
|
|
||||||
let transport = await!(bincode_transport::connect(&addr))?;
|
let transport = bincode_transport::connect(&addr).await?;
|
||||||
|
|
||||||
// new_stub is generated by the tarpc::service! macro. Like Server, it takes a config and any
|
// new_stub is generated by the tarpc::service! macro. Like Server, it takes a config and any
|
||||||
// Transport as input, and returns a Client, also generated by the macro.
|
// Transport as input, and returns a Client, also generated by the macro.
|
||||||
// by the service mcro.
|
// by the service mcro.
|
||||||
let mut client = await!(new_stub(client::Config::default(), transport))?;
|
let mut client = new_stub(client::Config::default(), transport).await?;
|
||||||
|
|
||||||
// The client has an RPC method for each RPC defined in tarpc::service!. It takes the same args
|
// The client has an RPC method for each RPC defined in tarpc::service!. It takes the same args
|
||||||
// as defined, with the addition of a Context, which is always the first arg. The Context
|
// as defined, with the addition of a Context, which is always the first arg. The Context
|
||||||
// specifies a deadline and trace information which can be helpful in debugging requests.
|
// specifies a deadline and trace information which can be helpful in debugging requests.
|
||||||
let hello = await!(client.hello(context::current(), "Stim".to_string()))?;
|
let hello = client.hello(context::current(), "Stim".to_string()).await?;
|
||||||
|
|
||||||
println!("{}", hello);
|
println!("{}", hello);
|
||||||
|
|
||||||
@@ -82,6 +76,8 @@ async fn run() -> io::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
tarpc::init(tokio::executor::DefaultExecutor::current().compat());
|
||||||
|
|
||||||
tokio::run(
|
tokio::run(
|
||||||
run()
|
run()
|
||||||
.map_err(|e| eprintln!("Oh no: {}", e))
|
.map_err(|e| eprintln!("Oh no: {}", e))
|
||||||
|
|||||||
@@ -7,21 +7,19 @@
|
|||||||
#![feature(
|
#![feature(
|
||||||
existential_type,
|
existential_type,
|
||||||
arbitrary_self_types,
|
arbitrary_self_types,
|
||||||
pin,
|
|
||||||
futures_api,
|
|
||||||
await_macro,
|
|
||||||
async_await,
|
async_await,
|
||||||
proc_macro_hygiene,
|
proc_macro_hygiene
|
||||||
)]
|
)]
|
||||||
|
|
||||||
use crate::{add::Service as AddService, double::Service as DoubleService};
|
use crate::{add::Service as AddService, double::Service as DoubleService};
|
||||||
use futures::{
|
use futures::{
|
||||||
|
compat::Executor01CompatExt,
|
||||||
future::{self, Ready},
|
future::{self, Ready},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
use rpc::{
|
use rpc::{
|
||||||
client, context,
|
client, context,
|
||||||
server::{self, Handler, Server},
|
server::{Handler, Server},
|
||||||
};
|
};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
@@ -45,7 +43,7 @@ struct AddServer;
|
|||||||
impl AddService for AddServer {
|
impl AddService for AddServer {
|
||||||
type AddFut = Ready<i32>;
|
type AddFut = Ready<i32>;
|
||||||
|
|
||||||
fn add(&self, _: context::Context, x: i32, y: i32) -> Self::AddFut {
|
fn add(self, _: context::Context, x: i32, y: i32) -> Self::AddFut {
|
||||||
future::ready(x + y)
|
future::ready(x + y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,10 +56,12 @@ struct DoubleServer {
|
|||||||
impl DoubleService for DoubleServer {
|
impl DoubleService for DoubleServer {
|
||||||
existential type DoubleFut: Future<Output = Result<i32, String>> + Send;
|
existential type DoubleFut: Future<Output = Result<i32, String>> + Send;
|
||||||
|
|
||||||
fn double(&self, _: context::Context, x: i32) -> Self::DoubleFut {
|
fn double(self, _: context::Context, x: i32) -> Self::DoubleFut {
|
||||||
async fn double(mut client: add::Client, x: i32) -> Result<i32, String> {
|
async fn double(mut client: add::Client, x: i32) -> Result<i32, String> {
|
||||||
let result = await!(client.add(context::current(), x, x));
|
client
|
||||||
result.map_err(|e| e.to_string())
|
.add(context::current(), x, x)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
double(self.add_client.clone(), x)
|
double(self.add_client.clone(), x)
|
||||||
@@ -71,41 +71,34 @@ impl DoubleService for DoubleServer {
|
|||||||
async fn run() -> io::Result<()> {
|
async fn run() -> io::Result<()> {
|
||||||
let add_listener = bincode_transport::listen(&"0.0.0.0:0".parse().unwrap())?;
|
let add_listener = bincode_transport::listen(&"0.0.0.0:0".parse().unwrap())?;
|
||||||
let addr = add_listener.local_addr();
|
let addr = add_listener.local_addr();
|
||||||
let add_server = Server::new(server::Config::default())
|
let add_server = Server::default()
|
||||||
.incoming(add_listener)
|
.incoming(add_listener)
|
||||||
.take(1)
|
.take(1)
|
||||||
.respond_with(add::serve(AddServer));
|
.respond_with(add::serve(AddServer));
|
||||||
tokio_executor::spawn(add_server.unit_error().boxed().compat());
|
tokio_executor::spawn(add_server.unit_error().boxed().compat());
|
||||||
|
|
||||||
let to_add_server = await!(bincode_transport::connect(&addr))?;
|
let to_add_server = bincode_transport::connect(&addr).await?;
|
||||||
let add_client = await!(add::new_stub(client::Config::default(), to_add_server))?;
|
let add_client = add::new_stub(client::Config::default(), to_add_server).await?;
|
||||||
|
|
||||||
let double_listener = bincode_transport::listen(&"0.0.0.0:0".parse().unwrap())?;
|
let double_listener = bincode_transport::listen(&"0.0.0.0:0".parse().unwrap())?;
|
||||||
let addr = double_listener.local_addr();
|
let addr = double_listener.local_addr();
|
||||||
let double_server = rpc::Server::new(server::Config::default())
|
let double_server = rpc::Server::default()
|
||||||
.incoming(double_listener)
|
.incoming(double_listener)
|
||||||
.take(1)
|
.take(1)
|
||||||
.respond_with(double::serve(DoubleServer { add_client }));
|
.respond_with(double::serve(DoubleServer { add_client }));
|
||||||
tokio_executor::spawn(double_server.unit_error().boxed().compat());
|
tokio_executor::spawn(double_server.unit_error().boxed().compat());
|
||||||
|
|
||||||
let to_double_server = await!(bincode_transport::connect(&addr))?;
|
let to_double_server = bincode_transport::connect(&addr).await?;
|
||||||
let mut double_client = await!(double::new_stub(
|
let mut double_client = double::new_stub(client::Config::default(), to_double_server).await?;
|
||||||
client::Config::default(),
|
|
||||||
to_double_server
|
|
||||||
))?;
|
|
||||||
|
|
||||||
for i in 1..=5 {
|
for i in 1..=5 {
|
||||||
println!("{:?}", await!(double_client.double(context::current(), i))?);
|
println!("{:?}", double_client.double(context::current(), i).await?);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
tokio::run(
|
tarpc::init(tokio::executor::DefaultExecutor::current().compat());
|
||||||
run()
|
tokio::run(run().map_err(|e| panic!(e)).boxed().compat());
|
||||||
.map_err(|e| panic!(e))
|
|
||||||
.boxed()
|
|
||||||
.compat(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
416
tarpc/examples/service_registry.rs
Normal file
416
tarpc/examples/service_registry.rs
Normal file
@@ -0,0 +1,416 @@
|
|||||||
|
#![feature(
|
||||||
|
async_await,
|
||||||
|
arbitrary_self_types,
|
||||||
|
proc_macro_hygiene,
|
||||||
|
impl_trait_in_bindings
|
||||||
|
)]
|
||||||
|
|
||||||
|
mod registry {
|
||||||
|
use bytes::Bytes;
|
||||||
|
use futures::{
|
||||||
|
future::{ready, Ready},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{
|
||||||
|
io,
|
||||||
|
pin::Pin,
|
||||||
|
sync::Arc,
|
||||||
|
task::{Context, Poll},
|
||||||
|
};
|
||||||
|
use tarpc::{
|
||||||
|
client::{self, Client},
|
||||||
|
context,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A request to a named service.
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct ServiceRequest {
|
||||||
|
service_name: String,
|
||||||
|
request: Bytes,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A response from a named service.
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct ServiceResponse {
|
||||||
|
response: Bytes,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A list of registered services.
|
||||||
|
pub struct Registry<Services> {
|
||||||
|
registrations: Services,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Registry<Nil> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Registry { registrations: Nil }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Services: MaybeServe + Sync> Registry<Services> {
|
||||||
|
/// Returns a function that serves requests for the registered services.
|
||||||
|
pub fn serve(
|
||||||
|
self,
|
||||||
|
) -> impl FnOnce(
|
||||||
|
context::Context,
|
||||||
|
ServiceRequest,
|
||||||
|
) -> Either<Services::Future, Ready<io::Result<ServiceResponse>>>
|
||||||
|
+ Clone {
|
||||||
|
let registrations = Arc::new(self.registrations);
|
||||||
|
move |cx, req: ServiceRequest| match registrations.serve(cx, &req) {
|
||||||
|
Some(serve) => Either::Left(serve),
|
||||||
|
None => Either::Right(ready(Err(io::Error::new(
|
||||||
|
io::ErrorKind::NotFound,
|
||||||
|
format!("Service '{}' not registered", req.service_name),
|
||||||
|
)))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Registers `serve` with the given `name` using the given serialization scheme.
|
||||||
|
pub fn register<S, Req, Resp, RespFut, Ser, De>(
|
||||||
|
self,
|
||||||
|
name: String,
|
||||||
|
serve: S,
|
||||||
|
deserialize: De,
|
||||||
|
serialize: Ser,
|
||||||
|
) -> Registry<Registration<impl Serve + Send + 'static, Services>>
|
||||||
|
where
|
||||||
|
Req: Send,
|
||||||
|
S: FnOnce(context::Context, Req) -> RespFut + Send + 'static + Clone,
|
||||||
|
RespFut: Future<Output = io::Result<Resp>> + Send + 'static,
|
||||||
|
De: FnOnce(Bytes) -> io::Result<Req> + Send + 'static + Clone,
|
||||||
|
Ser: FnOnce(Resp) -> io::Result<Bytes> + Send + 'static + Clone,
|
||||||
|
{
|
||||||
|
let registrations = Registration {
|
||||||
|
name: name,
|
||||||
|
serve: move |cx, req: Bytes| {
|
||||||
|
async move {
|
||||||
|
let req = deserialize.clone()(req)?;
|
||||||
|
let response = serve.clone()(cx, req).await?;
|
||||||
|
let response = serialize.clone()(response)?;
|
||||||
|
Ok(ServiceResponse { response })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rest: self.registrations,
|
||||||
|
};
|
||||||
|
Registry { registrations }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a client that sends requests to a service
|
||||||
|
/// named `service_name`, over the given channel, using
|
||||||
|
/// the specified serialization scheme.
|
||||||
|
pub fn new_client<Req, Resp, Ser, De>(
|
||||||
|
service_name: String,
|
||||||
|
channel: &client::Channel<ServiceRequest, ServiceResponse>,
|
||||||
|
mut serialize: Ser,
|
||||||
|
mut deserialize: De,
|
||||||
|
) -> client::MapResponse<
|
||||||
|
client::WithRequest<
|
||||||
|
client::Channel<ServiceRequest, ServiceResponse>,
|
||||||
|
impl FnMut(Req) -> ServiceRequest,
|
||||||
|
>,
|
||||||
|
impl FnMut(ServiceResponse) -> Resp,
|
||||||
|
>
|
||||||
|
where
|
||||||
|
Req: Send + 'static,
|
||||||
|
Resp: Send + 'static,
|
||||||
|
De: FnMut(Bytes) -> io::Result<Resp> + Clone + Send + 'static,
|
||||||
|
Ser: FnMut(Req) -> io::Result<Bytes> + Clone + Send + 'static,
|
||||||
|
{
|
||||||
|
channel
|
||||||
|
.clone()
|
||||||
|
.with_request(move |req| {
|
||||||
|
ServiceRequest {
|
||||||
|
service_name: service_name.clone(),
|
||||||
|
// TODO: shouldn't need to unwrap here. Maybe with_request should allow for
|
||||||
|
// returning Result.
|
||||||
|
request: serialize(req).unwrap(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// TODO: same thing. Maybe this should be more like and_then rather than map.
|
||||||
|
.map_response(move |resp| deserialize(resp.response).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serves a request.
|
||||||
|
///
|
||||||
|
/// This trait is mostly an implementation detail that isn't used outside of the registry
|
||||||
|
/// internals.
|
||||||
|
pub trait Serve: Clone + Send + 'static {
|
||||||
|
type Response: Future<Output = io::Result<ServiceResponse>> + Send + 'static;
|
||||||
|
fn serve(self, cx: context::Context, request: Bytes) -> Self::Response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serves a request if the request is for a registered service.
|
||||||
|
///
|
||||||
|
/// This trait is mostly an implementation detail that isn't used outside of the registry
|
||||||
|
/// internals.
|
||||||
|
pub trait MaybeServe: Send + 'static {
|
||||||
|
type Future: Future<Output = io::Result<ServiceResponse>> + Send + 'static;
|
||||||
|
|
||||||
|
fn serve(&self, cx: context::Context, request: &ServiceRequest) -> Option<Self::Future>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A registry starting with service S, followed by Rest.
|
||||||
|
///
|
||||||
|
/// This type is mostly an implementation detail that is not used directly
|
||||||
|
/// outside of the registry internals.
|
||||||
|
pub struct Registration<S, Rest> {
|
||||||
|
/// The registered service's name. Must be unique across all registered services.
|
||||||
|
name: String,
|
||||||
|
/// The registered service.
|
||||||
|
serve: S,
|
||||||
|
/// Any remaining registered services.
|
||||||
|
rest: Rest,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An empty registry.
|
||||||
|
///
|
||||||
|
/// This type is mostly an implementation detail that is not used directly
|
||||||
|
/// outside of the registry internals.
|
||||||
|
pub struct Nil;
|
||||||
|
|
||||||
|
impl MaybeServe for Nil {
|
||||||
|
type Future = futures::future::Ready<io::Result<ServiceResponse>>;
|
||||||
|
|
||||||
|
fn serve(&self, _: context::Context, _: &ServiceRequest) -> Option<Self::Future> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, Rest> MaybeServe for Registration<S, Rest>
|
||||||
|
where
|
||||||
|
S: Serve,
|
||||||
|
Rest: MaybeServe,
|
||||||
|
{
|
||||||
|
type Future = Either<S::Response, Rest::Future>;
|
||||||
|
|
||||||
|
fn serve(&self, cx: context::Context, request: &ServiceRequest) -> Option<Self::Future> {
|
||||||
|
if self.name == request.service_name {
|
||||||
|
Some(Either::Left(
|
||||||
|
self.serve.clone().serve(cx, request.request.clone()),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
self.rest.serve(cx, request).map(Either::Right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wraps either of two future types that both resolve to the same output type.
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[must_use = "futures do nothing unless polled"]
|
||||||
|
pub enum Either<Left, Right> {
|
||||||
|
Left(Left),
|
||||||
|
Right(Right),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Output, Left, Right> Future for Either<Left, Right>
|
||||||
|
where
|
||||||
|
Left: Future<Output = Output>,
|
||||||
|
Right: Future<Output = Output>,
|
||||||
|
{
|
||||||
|
type Output = Output;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Output> {
|
||||||
|
unsafe {
|
||||||
|
match Pin::get_unchecked_mut(self) {
|
||||||
|
Either::Left(car) => Pin::new_unchecked(car).poll(cx),
|
||||||
|
Either::Right(cdr) => Pin::new_unchecked(cdr).poll(cx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Resp, F> Serve for F
|
||||||
|
where
|
||||||
|
F: FnOnce(context::Context, Bytes) -> Resp + Clone + Send + 'static,
|
||||||
|
Resp: Future<Output = io::Result<ServiceResponse>> + Send + 'static,
|
||||||
|
{
|
||||||
|
type Response = Resp;
|
||||||
|
|
||||||
|
fn serve(self, cx: context::Context, request: Bytes) -> Resp {
|
||||||
|
self(cx, request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example
|
||||||
|
use bytes::Bytes;
|
||||||
|
use futures::{
|
||||||
|
compat::Executor01CompatExt,
|
||||||
|
future::{ready, Ready},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
io,
|
||||||
|
sync::{Arc, RwLock},
|
||||||
|
};
|
||||||
|
use tarpc::{client, context, server::Handler};
|
||||||
|
|
||||||
|
fn deserialize<Req>(req: Bytes) -> io::Result<Req>
|
||||||
|
where
|
||||||
|
Req: for<'a> Deserialize<'a> + Send,
|
||||||
|
{
|
||||||
|
bincode::deserialize(req.as_ref()).map_err(|e| io::Error::new(io::ErrorKind::Other, e))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize<Resp>(resp: Resp) -> io::Result<Bytes>
|
||||||
|
where
|
||||||
|
Resp: Serialize,
|
||||||
|
{
|
||||||
|
Ok(bincode::serialize(&resp)
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
mod write_service {
|
||||||
|
tarpc::service! {
|
||||||
|
rpc write(key: String, value: String);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod read_service {
|
||||||
|
tarpc::service! {
|
||||||
|
rpc read(key: String) -> Option<String>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
struct Server {
|
||||||
|
data: Arc<RwLock<HashMap<String, String>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl write_service::Service for Server {
|
||||||
|
type WriteFut = Ready<()>;
|
||||||
|
|
||||||
|
fn write(self, _: context::Context, key: String, value: String) -> Self::WriteFut {
|
||||||
|
self.data.write().unwrap().insert(key, value);
|
||||||
|
ready(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl read_service::Service for Server {
|
||||||
|
type ReadFut = Ready<Option<String>>;
|
||||||
|
|
||||||
|
fn read(self, _: context::Context, key: String) -> Self::ReadFut {
|
||||||
|
ready(self.data.read().unwrap().get(&key).cloned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait DefaultSpawn {
|
||||||
|
fn spawn(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F> DefaultSpawn for F
|
||||||
|
where
|
||||||
|
F: Future<Output = ()> + Send + 'static,
|
||||||
|
{
|
||||||
|
fn spawn(self) {
|
||||||
|
tokio_executor::spawn(self.unit_error().boxed().compat())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BincodeRegistry<Services> {
|
||||||
|
registry: registry::Registry<Services>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for BincodeRegistry<registry::Nil> {
|
||||||
|
fn default() -> Self {
|
||||||
|
BincodeRegistry {
|
||||||
|
registry: registry::Registry::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Services: registry::MaybeServe + Sync> BincodeRegistry<Services> {
|
||||||
|
fn serve(
|
||||||
|
self,
|
||||||
|
) -> impl FnOnce(
|
||||||
|
context::Context,
|
||||||
|
registry::ServiceRequest,
|
||||||
|
) -> registry::Either<
|
||||||
|
Services::Future,
|
||||||
|
Ready<io::Result<registry::ServiceResponse>>,
|
||||||
|
> + Clone {
|
||||||
|
self.registry.serve()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register<S, Req, Resp, RespFut>(
|
||||||
|
self,
|
||||||
|
name: String,
|
||||||
|
serve: S,
|
||||||
|
) -> BincodeRegistry<registry::Registration<impl registry::Serve + Send + 'static, Services>>
|
||||||
|
where
|
||||||
|
Req: for<'a> Deserialize<'a> + Send + 'static,
|
||||||
|
Resp: Serialize + 'static,
|
||||||
|
S: FnOnce(context::Context, Req) -> RespFut + Send + 'static + Clone,
|
||||||
|
RespFut: Future<Output = io::Result<Resp>> + Send + 'static,
|
||||||
|
{
|
||||||
|
let registry = self.registry.register(name, serve, deserialize, serialize);
|
||||||
|
BincodeRegistry { registry }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_client<Req, Resp>(
|
||||||
|
service_name: String,
|
||||||
|
channel: &client::Channel<registry::ServiceRequest, registry::ServiceResponse>,
|
||||||
|
) -> client::MapResponse<
|
||||||
|
client::WithRequest<
|
||||||
|
client::Channel<registry::ServiceRequest, registry::ServiceResponse>,
|
||||||
|
impl FnMut(Req) -> registry::ServiceRequest,
|
||||||
|
>,
|
||||||
|
impl FnMut(registry::ServiceResponse) -> Resp,
|
||||||
|
>
|
||||||
|
where
|
||||||
|
Req: Serialize + Send + 'static,
|
||||||
|
Resp: for<'a> Deserialize<'a> + Send + 'static,
|
||||||
|
{
|
||||||
|
registry::new_client(service_name, channel, serialize, deserialize)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run() -> io::Result<()> {
|
||||||
|
let server = Server::default();
|
||||||
|
let registry = BincodeRegistry::default()
|
||||||
|
.register(
|
||||||
|
"WriteService".to_string(),
|
||||||
|
write_service::serve(server.clone()),
|
||||||
|
)
|
||||||
|
.register(
|
||||||
|
"ReadService".to_string(),
|
||||||
|
read_service::serve(server.clone()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let listener = bincode_transport::listen(&"0.0.0.0:0".parse().unwrap())?;
|
||||||
|
let server_addr = listener.local_addr();
|
||||||
|
let server = tarpc::Server::default()
|
||||||
|
.incoming(listener)
|
||||||
|
.take(1)
|
||||||
|
.respond_with(registry.serve());
|
||||||
|
tokio_executor::spawn(server.unit_error().boxed().compat());
|
||||||
|
|
||||||
|
let transport = bincode_transport::connect(&server_addr).await?;
|
||||||
|
let channel = client::new(client::Config::default(), transport).await?;
|
||||||
|
|
||||||
|
let write_client = new_client("WriteService".to_string(), &channel);
|
||||||
|
let mut write_client = write_service::Client::from(write_client);
|
||||||
|
|
||||||
|
let read_client = new_client("ReadService".to_string(), &channel);
|
||||||
|
let mut read_client = read_service::Client::from(read_client);
|
||||||
|
|
||||||
|
write_client
|
||||||
|
.write(context::current(), "key".to_string(), "val".to_string())
|
||||||
|
.await?;
|
||||||
|
let val = read_client
|
||||||
|
.read(context::current(), "key".to_string())
|
||||||
|
.await?;
|
||||||
|
println!("{:?}", val);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
tarpc::init(tokio::executor::DefaultExecutor::current().compat());
|
||||||
|
tokio::run(run().boxed().map_err(|e| panic!(e)).boxed().compat());
|
||||||
|
}
|
||||||
@@ -1 +1 @@
|
|||||||
edition = "Edition2018"
|
edition = "2018"
|
||||||
|
|||||||
117
tarpc/src/lib.rs
117
tarpc/src/lib.rs
@@ -4,122 +4,9 @@
|
|||||||
// license that can be found in the LICENSE file or at
|
// license that can be found in the LICENSE file or at
|
||||||
// https://opensource.org/licenses/MIT.
|
// https://opensource.org/licenses/MIT.
|
||||||
|
|
||||||
//! tarpc is an RPC framework for rust with a focus on ease of use. Defining a
|
#![doc(include = "../README.md")]
|
||||||
//! service can be done in just a few lines of code, and most of the boilerplate of
|
|
||||||
//! writing a server is taken care of for you.
|
|
||||||
//!
|
|
||||||
//! ## What is an RPC framework?
|
|
||||||
//! "RPC" stands for "Remote Procedure Call," a function call where the work of
|
|
||||||
//! producing the return value is being done somewhere else. When an rpc function is
|
|
||||||
//! invoked, behind the scenes the function contacts some other process somewhere
|
|
||||||
//! and asks them to evaluate the function instead. The original function then
|
|
||||||
//! returns the value produced by the other process.
|
|
||||||
//!
|
|
||||||
//! RPC frameworks are a fundamental building block of most microservices-oriented
|
|
||||||
//! architectures. Two well-known ones are [gRPC](http://www.grpc.io) and
|
|
||||||
//! [Cap'n Proto](https://capnproto.org/).
|
|
||||||
//!
|
|
||||||
//! tarpc differentiates itself from other RPC frameworks by defining the schema in code,
|
|
||||||
//! rather than in a separate language such as .proto. This means there's no separate compilation
|
|
||||||
//! process, and no cognitive context switching between different languages. Additionally, it
|
|
||||||
//! works with the community-backed library serde: any serde-serializable type can be used as
|
|
||||||
//! arguments to tarpc fns.
|
|
||||||
//!
|
|
||||||
//! ## Example
|
|
||||||
//!
|
|
||||||
//! Here's a small service.
|
|
||||||
//!
|
|
||||||
//! ```rust
|
|
||||||
//! #![feature(futures_api, pin, arbitrary_self_types, await_macro, async_await, proc_macro_hygiene)]
|
|
||||||
//!
|
|
||||||
//!
|
|
||||||
//! use futures::{
|
|
||||||
//! compat::TokioDefaultSpawner,
|
|
||||||
//! future::{self, Ready},
|
|
||||||
//! prelude::*,
|
|
||||||
//! };
|
|
||||||
//! use tarpc::{
|
|
||||||
//! client, context,
|
|
||||||
//! server::{self, Handler, Server},
|
|
||||||
//! };
|
|
||||||
//! use std::io;
|
|
||||||
//!
|
|
||||||
//! // This is the service definition. It looks a lot like a trait definition.
|
|
||||||
//! // It defines one RPC, hello, which takes one arg, name, and returns a String.
|
|
||||||
//! tarpc::service! {
|
|
||||||
//! /// Returns a greeting for name.
|
|
||||||
//! rpc hello(name: String) -> String;
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! // This is the type that implements the generated Service trait. It is the business logic
|
|
||||||
//! // and is used to start the server.
|
|
||||||
//! #[derive(Clone)]
|
|
||||||
//! struct HelloServer;
|
|
||||||
//!
|
|
||||||
//! impl Service for HelloServer {
|
|
||||||
//! // Each defined rpc generates two items in the trait, a fn that serves the RPC, and
|
|
||||||
//! // an associated type representing the future output by the fn.
|
|
||||||
//!
|
|
||||||
//! type HelloFut = Ready<String>;
|
|
||||||
//!
|
|
||||||
//! fn hello(&self, _: context::Context, name: String) -> Self::HelloFut {
|
|
||||||
//! future::ready(format!("Hello, {}!", name))
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! async fn run() -> io::Result<()> {
|
|
||||||
//! // bincode_transport is provided by the associated crate bincode-transport. It makes it easy
|
|
||||||
//! // to start up a serde-powered bincode serialization strategy over TCP.
|
|
||||||
//! let transport = bincode_transport::listen(&"0.0.0.0:0".parse().unwrap())?;
|
|
||||||
//! let addr = transport.local_addr();
|
|
||||||
//!
|
|
||||||
//! // The server is configured with the defaults.
|
|
||||||
//! let server = Server::new(server::Config::default())
|
|
||||||
//! // Server can listen on any type that implements the Transport trait.
|
|
||||||
//! .incoming(transport)
|
|
||||||
//! // Close the stream after the client connects
|
|
||||||
//! .take(1)
|
|
||||||
//! // serve is generated by the service! macro. It takes as input any type implementing
|
|
||||||
//! // the generated Service trait.
|
|
||||||
//! .respond_with(serve(HelloServer));
|
|
||||||
//!
|
|
||||||
//! tokio_executor::spawn(server.unit_error().boxed().compat());
|
|
||||||
//!
|
|
||||||
//! let transport = await!(bincode_transport::connect(&addr))?;
|
|
||||||
//!
|
|
||||||
//! // new_stub is generated by the service! macro. Like Server, it takes a config and any
|
|
||||||
//! // Transport as input, and returns a Client, also generated by the macro.
|
|
||||||
//! // by the service mcro.
|
|
||||||
//! let mut client = await!(new_stub(client::Config::default(), transport))?;
|
|
||||||
//!
|
|
||||||
//! // The client has an RPC method for each RPC defined in service!. It takes the same args
|
|
||||||
//! // as defined, with the addition of a Context, which is always the first arg. The Context
|
|
||||||
//! // specifies a deadline and trace information which can be helpful in debugging requests.
|
|
||||||
//! let hello = await!(client.hello(context::current(), "Stim".to_string()))?;
|
|
||||||
//!
|
|
||||||
//! println!("{}", hello);
|
|
||||||
//!
|
|
||||||
//! Ok(())
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn main() {
|
|
||||||
//! tarpc::init(TokioDefaultSpawner);
|
|
||||||
//! tokio::run(run()
|
|
||||||
//! .map_err(|e| eprintln!("Oh no: {}", e))
|
|
||||||
//! .boxed()
|
|
||||||
//! .compat(),
|
|
||||||
//! );
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
|
|
||||||
#![deny(missing_docs, missing_debug_implementations)]
|
#![deny(missing_docs, missing_debug_implementations)]
|
||||||
#![feature(
|
#![feature(async_await, external_doc)]
|
||||||
futures_api,
|
|
||||||
pin,
|
|
||||||
await_macro,
|
|
||||||
async_await,
|
|
||||||
decl_macro,
|
|
||||||
)]
|
|
||||||
#![cfg_attr(test, feature(proc_macro_hygiene, arbitrary_self_types))]
|
#![cfg_attr(test, feature(proc_macro_hygiene, arbitrary_self_types))]
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ macro_rules! add_serde_if_enabled {
|
|||||||
/// Rpc methods are specified, mirroring trait syntax:
|
/// Rpc methods are specified, mirroring trait syntax:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # #![feature(await_macro, pin, arbitrary_self_types, async_await, futures_api, proc_macro_hygiene)]
|
/// # #![feature(arbitrary_self_types, async_await, proc_macro_hygiene)]
|
||||||
/// # fn main() {}
|
/// # fn main() {}
|
||||||
/// # tarpc::service! {
|
/// # tarpc::service! {
|
||||||
/// /// Say hello
|
/// /// Say hello
|
||||||
@@ -51,6 +51,9 @@ macro_rules! add_serde_if_enabled {
|
|||||||
///
|
///
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! service {
|
macro_rules! service {
|
||||||
|
() => {
|
||||||
|
compile_error!("Must define at least one RPC method.");
|
||||||
|
};
|
||||||
// Entry point
|
// Entry point
|
||||||
(
|
(
|
||||||
$(
|
$(
|
||||||
@@ -112,24 +115,26 @@ macro_rules! service {
|
|||||||
)*
|
)*
|
||||||
) => {
|
) => {
|
||||||
$crate::add_serde_if_enabled! {
|
$crate::add_serde_if_enabled! {
|
||||||
|
/// The request sent over the wire from the client to the server.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[doc(hidden)]
|
|
||||||
#[allow(non_camel_case_types, unused)]
|
#[allow(non_camel_case_types, unused)]
|
||||||
--
|
--
|
||||||
pub enum Request__ {
|
pub enum Request {
|
||||||
$(
|
$(
|
||||||
|
$(#[$attr])*
|
||||||
$fn_name{ $($arg: $in_,)* }
|
$fn_name{ $($arg: $in_,)* }
|
||||||
),*
|
),*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$crate::add_serde_if_enabled! {
|
$crate::add_serde_if_enabled! {
|
||||||
|
/// The response sent over the wire from the server to the client.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[doc(hidden)]
|
|
||||||
#[allow(non_camel_case_types, unused)]
|
#[allow(non_camel_case_types, unused)]
|
||||||
--
|
--
|
||||||
pub enum Response__ {
|
pub enum Response {
|
||||||
$(
|
$(
|
||||||
|
$(#[$attr])*
|
||||||
$fn_name($out)
|
$fn_name($out)
|
||||||
),*
|
),*
|
||||||
}
|
}
|
||||||
@@ -149,37 +154,39 @@ macro_rules! service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$(#[$attr])*
|
$(#[$attr])*
|
||||||
fn $fn_name(&self, ctx: $crate::context::Context, $($arg:$in_),*) -> $crate::ty_snake_to_camel!(Self::$fn_name);
|
fn $fn_name(self, ctx: $crate::context::Context, $($arg:$in_),*) -> $crate::ty_snake_to_camel!(Self::$fn_name);
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use an existential type instead of this when existential types work.
|
// TODO: use an existential type instead of this when existential types work.
|
||||||
|
/// A future resolving to a server [`Response`].
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
pub enum Response<S: Service> {
|
pub enum ResponseFut<S: Service> {
|
||||||
$(
|
$(
|
||||||
|
$(#[$attr])*
|
||||||
$fn_name($crate::ty_snake_to_camel!(<S as Service>::$fn_name)),
|
$fn_name($crate::ty_snake_to_camel!(<S as Service>::$fn_name)),
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: Service> ::std::fmt::Debug for Response<S> {
|
impl<S: Service> ::std::fmt::Debug for ResponseFut<S> {
|
||||||
fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
fmt.debug_struct("Response").finish()
|
fmt.debug_struct("Response").finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: Service> ::std::future::Future for Response<S> {
|
impl<S: Service> ::std::future::Future for ResponseFut<S> {
|
||||||
type Output = ::std::io::Result<Response__>;
|
type Output = ::std::io::Result<Response>;
|
||||||
|
|
||||||
fn poll(self: ::std::pin::Pin<&mut Self>, waker: &::std::task::LocalWaker)
|
fn poll(self: ::std::pin::Pin<&mut Self>, cx: &mut ::std::task::Context<'_>)
|
||||||
-> ::std::task::Poll<::std::io::Result<Response__>>
|
-> ::std::task::Poll<::std::io::Result<Response>>
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
match ::std::pin::Pin::get_mut_unchecked(self) {
|
match ::std::pin::Pin::get_unchecked_mut(self) {
|
||||||
$(
|
$(
|
||||||
Response::$fn_name(resp) =>
|
ResponseFut::$fn_name(resp) =>
|
||||||
::std::pin::Pin::new_unchecked(resp)
|
::std::pin::Pin::new_unchecked(resp)
|
||||||
.poll(waker)
|
.poll(cx)
|
||||||
.map(Response__::$fn_name)
|
.map(Response::$fn_name)
|
||||||
.map(Ok),
|
.map(Ok),
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
@@ -189,13 +196,13 @@ macro_rules! service {
|
|||||||
|
|
||||||
/// Returns a serving function to use with rpc::server::Server.
|
/// Returns a serving function to use with rpc::server::Server.
|
||||||
pub fn serve<S: Service>(service: S)
|
pub fn serve<S: Service>(service: S)
|
||||||
-> impl FnMut($crate::context::Context, Request__) -> Response<S> + Send + 'static + Clone {
|
-> impl FnOnce($crate::context::Context, Request) -> ResponseFut<S> + Send + 'static + Clone {
|
||||||
move |ctx, req| {
|
move |ctx, req| {
|
||||||
match req {
|
match req {
|
||||||
$(
|
$(
|
||||||
Request__::$fn_name{ $($arg,)* } => {
|
Request::$fn_name{ $($arg,)* } => {
|
||||||
let resp = Service::$fn_name(&mut service.clone(), ctx, $($arg),*);
|
let resp = Service::$fn_name(service.clone(), ctx, $($arg),*);
|
||||||
Response::$fn_name(resp)
|
ResponseFut::$fn_name(resp)
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
@@ -205,30 +212,40 @@ macro_rules! service {
|
|||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
/// The client stub that makes RPC calls to the server. Exposes a Future interface.
|
/// The client stub that makes RPC calls to the server. Exposes a Future interface.
|
||||||
pub struct Client($crate::client::Client<Request__, Response__>);
|
pub struct Client<C = $crate::client::Channel<Request, Response>>(C);
|
||||||
|
|
||||||
/// Returns a new client stub that sends requests over the given transport.
|
/// Returns a new client stub that sends requests over the given transport.
|
||||||
pub async fn new_stub<T>(config: $crate::client::Config, transport: T)
|
pub async fn new_stub<T>(config: $crate::client::Config, transport: T)
|
||||||
-> ::std::io::Result<Client>
|
-> ::std::io::Result<Client>
|
||||||
where
|
where
|
||||||
T: $crate::Transport<
|
T: $crate::Transport<
|
||||||
Item = $crate::Response<Response__>,
|
Item = $crate::Response<Response>,
|
||||||
SinkItem = $crate::ClientMessage<Request__>> + Send,
|
SinkItem = $crate::ClientMessage<Request>> + Send + 'static,
|
||||||
{
|
{
|
||||||
Ok(Client(await!($crate::client::Client::new(config, transport))?))
|
Ok(Client($crate::client::new(config, transport).await?))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl<C> From<C> for Client<C>
|
||||||
|
where for <'a> C: $crate::Client<'a, Request, Response = Response>
|
||||||
|
{
|
||||||
|
fn from(client: C) -> Self {
|
||||||
|
Client(client)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C> Client<C>
|
||||||
|
where for<'a> C: $crate::Client<'a, Request, Response = Response>
|
||||||
|
{
|
||||||
$(
|
$(
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
$(#[$attr])*
|
$(#[$attr])*
|
||||||
pub fn $fn_name(&mut self, ctx: $crate::context::Context, $($arg: $in_),*)
|
pub fn $fn_name(&mut self, ctx: $crate::context::Context, $($arg: $in_),*)
|
||||||
-> impl ::std::future::Future<Output = ::std::io::Result<$out>> + '_ {
|
-> impl ::std::future::Future<Output = ::std::io::Result<$out>> + '_ {
|
||||||
let request__ = Request__::$fn_name { $($arg,)* };
|
let request__ = Request::$fn_name { $($arg,)* };
|
||||||
let resp = self.0.call(ctx, request__);
|
let resp = $crate::Client::call(&mut self.0, ctx, request__);
|
||||||
async move {
|
async move {
|
||||||
match await!(resp)? {
|
match resp.await? {
|
||||||
Response__::$fn_name(msg__) => ::std::result::Result::Ok(msg__),
|
Response::$fn_name(msg__) => ::std::result::Result::Ok(msg__),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -265,15 +282,11 @@ mod syntax_test {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod functional_test {
|
mod functional_test {
|
||||||
use futures::{
|
use futures::{
|
||||||
compat::TokioDefaultSpawner,
|
compat::Executor01CompatExt,
|
||||||
future::{ready, Ready},
|
future::{ready, Ready},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
use rpc::{
|
use rpc::{client, context, server::Handler, transport::channel};
|
||||||
client, context,
|
|
||||||
server::{self, Handler},
|
|
||||||
transport::channel,
|
|
||||||
};
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use tokio::runtime::current_thread;
|
use tokio::runtime::current_thread;
|
||||||
|
|
||||||
@@ -288,13 +301,13 @@ mod functional_test {
|
|||||||
impl Service for Server {
|
impl Service for Server {
|
||||||
type AddFut = Ready<i32>;
|
type AddFut = Ready<i32>;
|
||||||
|
|
||||||
fn add(&self, _: context::Context, x: i32, y: i32) -> Self::AddFut {
|
fn add(self, _: context::Context, x: i32, y: i32) -> Self::AddFut {
|
||||||
ready(x + y)
|
ready(x + y)
|
||||||
}
|
}
|
||||||
|
|
||||||
type HeyFut = Ready<String>;
|
type HeyFut = Ready<String>;
|
||||||
|
|
||||||
fn hey(&self, _: context::Context, name: String) -> Self::HeyFut {
|
fn hey(self, _: context::Context, name: String) -> Self::HeyFut {
|
||||||
ready(format!("Hey, {}.", name))
|
ready(format!("Hey, {}.", name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -302,24 +315,24 @@ mod functional_test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn sequential() {
|
fn sequential() {
|
||||||
let _ = env_logger::try_init();
|
let _ = env_logger::try_init();
|
||||||
rpc::init(TokioDefaultSpawner);
|
rpc::init(tokio::executor::DefaultExecutor::current().compat());
|
||||||
|
|
||||||
let test = async {
|
let test = async {
|
||||||
let (tx, rx) = channel::unbounded();
|
let (tx, rx) = channel::unbounded();
|
||||||
tokio_executor::spawn(
|
tokio_executor::spawn(
|
||||||
rpc::Server::new(server::Config::default())
|
crate::Server::default()
|
||||||
.incoming(stream::once(ready(Ok(rx))))
|
.incoming(stream::once(ready(Ok(rx))))
|
||||||
.respond_with(serve(Server))
|
.respond_with(serve(Server))
|
||||||
.unit_error()
|
.unit_error()
|
||||||
.boxed()
|
.boxed()
|
||||||
.compat()
|
.compat(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut client = await!(new_stub(client::Config::default(), tx))?;
|
let mut client = new_stub(client::Config::default(), tx).await?;
|
||||||
assert_eq!(3, await!(client.add(context::current(), 1, 2))?);
|
assert_eq!(3, client.add(context::current(), 1, 2).await?);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"Hey, Tim.",
|
"Hey, Tim.",
|
||||||
await!(client.hey(context::current(), "Tim".to_string()))?
|
client.hey(context::current(), "Tim".to_string()).await?
|
||||||
);
|
);
|
||||||
Ok::<_, io::Error>(())
|
Ok::<_, io::Error>(())
|
||||||
}
|
}
|
||||||
@@ -331,20 +344,20 @@ mod functional_test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn concurrent() {
|
fn concurrent() {
|
||||||
let _ = env_logger::try_init();
|
let _ = env_logger::try_init();
|
||||||
rpc::init(TokioDefaultSpawner);
|
rpc::init(tokio::executor::DefaultExecutor::current().compat());
|
||||||
|
|
||||||
let test = async {
|
let test = async {
|
||||||
let (tx, rx) = channel::unbounded();
|
let (tx, rx) = channel::unbounded();
|
||||||
tokio_executor::spawn(
|
tokio_executor::spawn(
|
||||||
rpc::Server::new(server::Config::default())
|
rpc::Server::default()
|
||||||
.incoming(stream::once(ready(Ok(rx))))
|
.incoming(stream::once(ready(Ok(rx))))
|
||||||
.respond_with(serve(Server))
|
.respond_with(serve(Server))
|
||||||
.unit_error()
|
.unit_error()
|
||||||
.boxed()
|
.boxed()
|
||||||
.compat()
|
.compat(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let client = await!(new_stub(client::Config::default(), tx))?;
|
let client = new_stub(client::Config::default(), tx).await?;
|
||||||
let mut c = client.clone();
|
let mut c = client.clone();
|
||||||
let req1 = c.add(context::current(), 1, 2);
|
let req1 = c.add(context::current(), 1, 2);
|
||||||
let mut c = client.clone();
|
let mut c = client.clone();
|
||||||
@@ -352,9 +365,9 @@ mod functional_test {
|
|||||||
let mut c = client.clone();
|
let mut c = client.clone();
|
||||||
let req3 = c.hey(context::current(), "Tim".to_string());
|
let req3 = c.hey(context::current(), "Tim".to_string());
|
||||||
|
|
||||||
assert_eq!(3, await!(req1)?);
|
assert_eq!(3, req1.await?);
|
||||||
assert_eq!(7, await!(req2)?);
|
assert_eq!(7, req2.await?);
|
||||||
assert_eq!("Hey, Tim.", await!(req3)?);
|
assert_eq!("Hey, Tim.", req3.await?);
|
||||||
Ok::<_, io::Error>(())
|
Ok::<_, io::Error>(())
|
||||||
}
|
}
|
||||||
.map_err(|e| panic!("test failed: {}", e));
|
.map_err(|e| panic!("test failed: {}", e));
|
||||||
|
|||||||
@@ -7,22 +7,16 @@
|
|||||||
#![feature(
|
#![feature(
|
||||||
test,
|
test,
|
||||||
arbitrary_self_types,
|
arbitrary_self_types,
|
||||||
pin,
|
|
||||||
integer_atomics,
|
integer_atomics,
|
||||||
futures_api,
|
|
||||||
generators,
|
|
||||||
await_macro,
|
|
||||||
async_await,
|
async_await,
|
||||||
proc_macro_hygiene,
|
proc_macro_hygiene
|
||||||
)]
|
)]
|
||||||
|
|
||||||
extern crate test;
|
use futures::{compat::Executor01CompatExt, future, prelude::*};
|
||||||
|
use libtest::stats::Stats;
|
||||||
use self::test::stats::Stats;
|
|
||||||
use futures::{compat::TokioDefaultSpawner, future, prelude::*};
|
|
||||||
use rpc::{
|
use rpc::{
|
||||||
client, context,
|
client, context,
|
||||||
server::{self, Handler, Server},
|
server::{Handler, Server},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
io,
|
io,
|
||||||
@@ -41,7 +35,7 @@ struct Serve;
|
|||||||
impl ack::Service for Serve {
|
impl ack::Service for Serve {
|
||||||
type AckFut = future::Ready<()>;
|
type AckFut = future::Ready<()>;
|
||||||
|
|
||||||
fn ack(&self, _: context::Context) -> Self::AckFut {
|
fn ack(self, _: context::Context) -> Self::AckFut {
|
||||||
future::ready(())
|
future::ready(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -51,17 +45,17 @@ async fn bench() -> io::Result<()> {
|
|||||||
let addr = listener.local_addr();
|
let addr = listener.local_addr();
|
||||||
|
|
||||||
tokio_executor::spawn(
|
tokio_executor::spawn(
|
||||||
Server::new(server::Config::default())
|
Server::default()
|
||||||
.incoming(listener)
|
.incoming(listener)
|
||||||
.take(1)
|
.take(1)
|
||||||
.respond_with(ack::serve(Serve))
|
.respond_with(ack::serve(Serve))
|
||||||
.unit_error()
|
.unit_error()
|
||||||
.boxed()
|
.boxed()
|
||||||
.compat()
|
.compat(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let conn = await!(bincode_transport::connect(&addr))?;
|
let conn = bincode_transport::connect(&addr).await?;
|
||||||
let mut client = await!(ack::new_stub(client::Config::default(), conn))?;
|
let mut client = ack::new_stub(client::Config::default(), conn).await?;
|
||||||
|
|
||||||
let total = 10_000usize;
|
let total = 10_000usize;
|
||||||
let mut successful = 0u32;
|
let mut successful = 0u32;
|
||||||
@@ -69,7 +63,7 @@ async fn bench() -> io::Result<()> {
|
|||||||
let mut durations = vec![];
|
let mut durations = vec![];
|
||||||
for _ in 1..=total {
|
for _ in 1..=total {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let response = await!(client.ack(context::current()));
|
let response = client.ack(context::current()).await;
|
||||||
let elapsed = now.elapsed();
|
let elapsed = now.elapsed();
|
||||||
|
|
||||||
match response {
|
match response {
|
||||||
@@ -120,12 +114,7 @@ async fn bench() -> io::Result<()> {
|
|||||||
#[test]
|
#[test]
|
||||||
fn bench_small_packet() {
|
fn bench_small_packet() {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
tarpc::init(TokioDefaultSpawner);
|
tarpc::init(tokio::executor::DefaultExecutor::current().compat());
|
||||||
|
|
||||||
tokio::run(
|
tokio::run(bench().map_err(|e| panic!(e.to_string())).boxed().compat())
|
||||||
bench()
|
|
||||||
.map_err(|e| panic!(e.to_string()))
|
|
||||||
.boxed()
|
|
||||||
.compat(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tarpc-trace"
|
name = "tarpc-trace"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
authors = ["tikue <tikue@google.com>"]
|
authors = ["tikue <tikue@google.com>"]
|
||||||
edition = '2018'
|
edition = '2018'
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
@@ -13,7 +13,7 @@ readme = "../README.md"
|
|||||||
description = "foundations for tracing in tarpc"
|
description = "foundations for tracing in tarpc"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rand = "0.5"
|
rand = "0.6"
|
||||||
|
|
||||||
[dependencies.serde]
|
[dependencies.serde]
|
||||||
version = "1.0"
|
version = "1.0"
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
edition = "Edition2018"
|
edition = "2018"
|
||||||
|
|||||||
@@ -27,10 +27,7 @@ use std::{
|
|||||||
/// Consists of a span identifying an event, an optional parent span identifying a causal event
|
/// Consists of a span identifying an event, an optional parent span identifying a causal event
|
||||||
/// that triggered the current span, and a trace with which all related spans are associated.
|
/// that triggered the current span, and a trace with which all related spans are associated.
|
||||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||||
#[cfg_attr(
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
feature = "serde",
|
|
||||||
derive(serde::Serialize, serde::Deserialize)
|
|
||||||
)]
|
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
/// An identifier of the trace associated with the current context. A trace ID is typically
|
/// An identifier of the trace associated with the current context. A trace ID is typically
|
||||||
/// created at a root span and passed along through all causal events.
|
/// created at a root span and passed along through all causal events.
|
||||||
@@ -50,18 +47,12 @@ pub struct Context {
|
|||||||
/// A 128-bit UUID identifying a trace. All spans caused by the same originating span share the
|
/// A 128-bit UUID identifying a trace. All spans caused by the same originating span share the
|
||||||
/// same trace ID.
|
/// same trace ID.
|
||||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||||
#[cfg_attr(
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
feature = "serde",
|
|
||||||
derive(serde::Serialize, serde::Deserialize)
|
|
||||||
)]
|
|
||||||
pub struct TraceId(u128);
|
pub struct TraceId(u128);
|
||||||
|
|
||||||
/// A 64-bit identifier of a span within a trace. The identifier is unique within the span's trace.
|
/// A 64-bit identifier of a span within a trace. The identifier is unique within the span's trace.
|
||||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||||
#[cfg_attr(
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
feature = "serde",
|
|
||||||
derive(serde::Serialize, serde::Deserialize)
|
|
||||||
)]
|
|
||||||
pub struct SpanId(u64);
|
pub struct SpanId(u64);
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
@@ -80,7 +71,7 @@ impl TraceId {
|
|||||||
/// Returns a random trace ID that can be assumed to be globally unique if `rng` generates
|
/// Returns a random trace ID that can be assumed to be globally unique if `rng` generates
|
||||||
/// actually-random numbers.
|
/// actually-random numbers.
|
||||||
pub fn random<R: Rng>(rng: &mut R) -> Self {
|
pub fn random<R: Rng>(rng: &mut R) -> Self {
|
||||||
TraceId((rng.next_u64() as u128) << mem::size_of::<u64>() | rng.next_u64() as u128)
|
TraceId(u128::from(rng.next_u64()) << mem::size_of::<u64>() | u128::from(rng.next_u64()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user