use std::io;
use hyper_util::rt::TokioIo;
use tokio::io::DuplexStream;
use tokio::sync::mpsc;
use tonic::service::Routes;
use tonic::transport::{Channel, Endpoint, Server};
const PIPE_BYTES: usize = 64 * 1024;
pub fn connect(name: &str, routes: Routes) -> Channel {
let (pipes, incoming) = mpsc::channel::<DuplexStream>(16);
let incoming = futures_util::stream::unfold(incoming, |mut incoming| async move {
incoming
.recv()
.await
.map(|pipe| (Ok::<_, io::Error>(pipe), incoming))
});
let label = name.to_owned();
tokio::spawn(async move {
if let Err(error) = Server::builder()
.add_routes(routes)
.serve_with_incoming(incoming)
.await
{
tracing::warn!(client = %label, %error, "in-memory gRPC double stopped");
}
});
let connector = tower::service_fn(move |_: http::Uri| {
let pipes = pipes.clone();
async move {
let (client, server) = tokio::io::duplex(PIPE_BYTES);
pipes
.send(server)
.await
.map_err(|_| io::Error::other("the in-memory gRPC double stopped"))?;
Ok::<_, io::Error>(TokioIo::new(client))
}
});
Endpoint::from_static("http://in-memory.invalid").connect_with_connector_lazy(connector)
}