mirror of
https://github.com/OMGeeky/tarpc.git
synced 2025-12-29 15:49:52 +01:00
1. Renames
Some of the items in this module were renamed to be less generic:
- Handler => Incoming
- ClientHandler => Requests
- ResponseHandler => InFlightRequest
- Channel::{respond_with => requests}
In the case of Handler: handler of *what*? Now it's a bit clearer that
this is a stream of Channels (aka *incoming* connections).
Similarly, ClientHandler was a stream of requests over a single
connection. Hopefully Requests better reflects that.
ResponseHandler was renamed InFlightRequest because it no longer
contains the serving function. Instead, it is just the request, plus
the response channel and an abort hook. As a result of this,
Channel::respond_with underwent a big change: it used to take the
serving function and return a ClientHandler; now it has been renamed
Channel::requests and does not take any args.
2. Execute methods
All methods thats actually result in responses being generated
have been consolidated into methods named `execute`:
- InFlightRequest::execute returns a future that completes when a
response has been generated and sent to the server Channel.
- Requests::execute automatically spawns response handlers for all
requests over a single channel.
- Channel::execute is a convenience for `channel.requests().execute()`.
- Incoming::execute automatically spawns response handlers for all
requests over all channels.
3. Removal of Server.
server::Server was removed, as it provided no value over the Incoming/Channel
abstractions. Additionally, server::new was removed, since it just
returned a Server.
130 lines
3.7 KiB
Rust
130 lines
3.7 KiB
Rust
use flate2::{read::DeflateDecoder, write::DeflateEncoder, Compression};
|
|
use futures::{Sink, SinkExt, Stream, StreamExt, TryStreamExt};
|
|
use serde::{Deserialize, Serialize};
|
|
use serde_bytes::ByteBuf;
|
|
use std::{io, io::Read, io::Write};
|
|
use tarpc::{
|
|
client, context,
|
|
serde_transport::tcp,
|
|
server::{BaseChannel, Channel},
|
|
};
|
|
use tokio_serde::formats::Bincode;
|
|
|
|
/// Type of compression that should be enabled on the request. The transport is free to ignore this.
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Deserialize, Serialize)]
|
|
pub enum CompressionAlgorithm {
|
|
Deflate,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
|
pub enum CompressedMessage<T> {
|
|
Uncompressed(T),
|
|
Compressed {
|
|
algorithm: CompressionAlgorithm,
|
|
payload: ByteBuf,
|
|
},
|
|
}
|
|
|
|
#[derive(Deserialize, Serialize)]
|
|
enum CompressionType {
|
|
Uncompressed,
|
|
Compressed,
|
|
}
|
|
|
|
async fn compress<T>(message: T) -> io::Result<CompressedMessage<T>>
|
|
where
|
|
T: Serialize,
|
|
{
|
|
let message = serialize(message)?;
|
|
let mut encoder = DeflateEncoder::new(Vec::new(), Compression::default());
|
|
encoder.write_all(&message).unwrap();
|
|
let compressed = encoder.finish()?;
|
|
Ok(CompressedMessage::Compressed {
|
|
algorithm: CompressionAlgorithm::Deflate,
|
|
payload: ByteBuf::from(compressed),
|
|
})
|
|
}
|
|
|
|
async fn decompress<T>(message: CompressedMessage<T>) -> io::Result<T>
|
|
where
|
|
for<'a> T: Deserialize<'a>,
|
|
{
|
|
match message {
|
|
CompressedMessage::Compressed { algorithm, payload } => {
|
|
if algorithm != CompressionAlgorithm::Deflate {
|
|
return Err(io::Error::new(
|
|
io::ErrorKind::InvalidData,
|
|
format!("Compression algorithm {:?} not supported", algorithm),
|
|
));
|
|
}
|
|
let mut deflater = DeflateDecoder::new(payload.as_slice());
|
|
let mut payload = ByteBuf::new();
|
|
deflater.read_to_end(&mut payload)?;
|
|
let message = deserialize(payload)?;
|
|
Ok(message)
|
|
}
|
|
CompressedMessage::Uncompressed(message) => Ok(message),
|
|
}
|
|
}
|
|
|
|
fn serialize<T: Serialize>(t: T) -> io::Result<ByteBuf> {
|
|
bincode::serialize(&t)
|
|
.map(ByteBuf::from)
|
|
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
|
|
}
|
|
|
|
fn deserialize<D>(message: ByteBuf) -> io::Result<D>
|
|
where
|
|
for<'a> D: Deserialize<'a>,
|
|
{
|
|
bincode::deserialize(message.as_ref()).map_err(|e| io::Error::new(io::ErrorKind::Other, e))
|
|
}
|
|
|
|
fn add_compression<In, Out>(
|
|
transport: impl Stream<Item = io::Result<CompressedMessage<In>>>
|
|
+ Sink<CompressedMessage<Out>, Error = io::Error>,
|
|
) -> impl Stream<Item = io::Result<In>> + Sink<Out, Error = io::Error>
|
|
where
|
|
Out: Serialize,
|
|
for<'a> In: Deserialize<'a>,
|
|
{
|
|
transport.with(compress).and_then(decompress)
|
|
}
|
|
|
|
#[tarpc::service]
|
|
pub trait World {
|
|
async fn hello(name: String) -> String;
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
struct HelloServer;
|
|
|
|
#[tarpc::server]
|
|
impl World for HelloServer {
|
|
async fn hello(self, _: context::Context, name: String) -> String {
|
|
format!("Hey, {}!", name)
|
|
}
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() -> anyhow::Result<()> {
|
|
let mut incoming = tcp::listen("localhost:0", Bincode::default).await?;
|
|
let addr = incoming.local_addr();
|
|
tokio::spawn(async move {
|
|
let transport = incoming.next().await.unwrap().unwrap();
|
|
BaseChannel::with_defaults(add_compression(transport))
|
|
.execute(HelloServer.serve())
|
|
.await;
|
|
});
|
|
|
|
let transport = tcp::connect(addr, Bincode::default).await?;
|
|
let mut client =
|
|
WorldClient::new(client::Config::default(), add_compression(transport)).spawn()?;
|
|
|
|
println!(
|
|
"{}",
|
|
client.hello(context::current(), "friend".into()).await?
|
|
);
|
|
Ok(())
|
|
}
|