-
I have a As a I'll also need to know how to serialize the response so I can send it back over the duplex. Thanks! |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 2 replies
-
Hey, If I understand correctly you basically want to know how to decode/encode the bytes to/from jsonrpc method call and response. I can think of the following two options to do that:
// I'm not sure whether everything is exported to instantiate it from another crate
let rpc_service = ...;
let stream = ..;
let bytes = stream.read(..).await.unwrap();
let request: jsonrpsee:types::Request = serde_json::from_slice(bytes).unwrap();
let rp = rpc_service.call(request).await.unwrap();
stream.write(rp.into_result().as_bytes()).await.unwrap();
I think 1) doesn't work because the constructor is not exported but we can fix that if you think that it will work. |
Beta Was this translation helpful? Give feedback.
-
I figured this out. Here is a client that works off of use async_trait::async_trait;
use jsonrpsee::{
async_client::{Client, ClientBuilder},
core::client::{ReceivedMessage, TransportReceiverT, TransportSenderT},
proc_macros::rpc,
};
use tokio::io::{
duplex, split, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, DuplexStream, ReadHalf,
WriteHalf,
};
#[tokio::main]
async fn main() {
let (client_stream, server_stream) = duplex(1024);
tokio::spawn(mock_server(server_stream));
let client = build_client(client_stream);
let sum = client.add(3, 5).await.unwrap();
println!("{}", sum);
}
async fn mock_server(mut pipe: DuplexStream) -> Result<(), std::io::Error> {
let mut buffer = vec![0u8; 1024];
let read = pipe.read(&mut buffer).await?;
println!(
"Received JSON request:\n{}",
String::from_utf8_lossy(&buffer[..read])
);
// Send our mock response
let response = r#"
{
"jsonrpc": "2.0",
"id": 0,
"result": 8
}
"#;
pipe.write_all(response.as_bytes()).await?;
pipe.flush().await?;
Ok(())
}
fn duplex_transport<T: AsyncRead + AsyncWrite>(
duplex: T,
) -> (
AsyncReadTransport<ReadHalf<T>>,
AsyncWriteTransport<WriteHalf<T>>,
) {
let (reader, writer) = split(duplex);
let reader = AsyncReadTransport { reader };
let writer = AsyncWriteTransport { writer };
(reader, writer)
}
fn build_client<T: AsyncRead + AsyncWrite + Send + 'static>(duplex: T) -> Client {
let (r, w) = duplex_transport(duplex);
ClientBuilder::default().build_with_tokio(w, r)
}
#[rpc(client)]
trait Calculator {
#[method(name = "add")]
fn add(&self, a: u64, b: u64) -> Result<u64, ErrorObject>;
}
struct AsyncWriteTransport<T: AsyncWrite> {
writer: T,
}
#[async_trait]
impl<T: AsyncWrite + Send + 'static + std::marker::Unpin> TransportSenderT
for AsyncWriteTransport<T>
{
type Error = std::io::Error;
async fn send(&mut self, msg: String) -> Result<(), Self::Error> {
self.writer.write_all(msg.as_bytes()).await?;
Ok(())
}
}
struct AsyncReadTransport<T: AsyncRead> {
reader: T,
}
#[async_trait]
impl<T: AsyncRead + Send + 'static + std::marker::Unpin> TransportReceiverT
for AsyncReadTransport<T>
{
type Error = std::io::Error;
async fn receive(&mut self) -> Result<ReceivedMessage, Self::Error> {
let mut buffer = vec![0u8; 1024];
let read = self.reader.read(&mut buffer[..]).await?;
Ok(ReceivedMessage::Bytes(buffer[..read].to_vec()))
}
} And here is a server that does the same: use jsonrpsee::proc_macros::rpc;
#[tokio::main]
async fn main() {
let request = r#"
{
"jsonrpc": "2.0",
"id": 1,
"method": "add",
"params": { "a": 3, "b": 5 }
}
"#;
let server = CalculatorService {}.into_rpc();
let response = server.raw_json_request(request, 1).await.unwrap();
println!("{}", response.0);
}
#[rpc(server)]
trait Calculator {
#[method(name = "add")]
fn add(&self, a: u64, b: u64) -> u64;
}
struct CalculatorService;
impl CalculatorServer for CalculatorService {
fn add(&self, a: u64, b: u64) -> u64 {
a + b
}
} |
Beta Was this translation helpful? Give feedback.
I figured this out. Here is a client that works off of
DuplexStream
: