use std::io;
use serde::{Deserialize, Serialize};
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
use tokio_util::compat::{Compat, FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt};
pub type PeerStream = Compat<yamux::Stream>;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AvailabilityItem {
pub store_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub root: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub retrieval_key: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AvailabilityRequest {
pub items: Vec<AvailabilityItem>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AvailabilityAnswer {
pub available: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub roots: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total_length: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub chunk_count: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub complete: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AvailabilityResponse {
pub items: Vec<AvailabilityAnswer>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RangeRequest {
pub store_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub retrieval_key: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub root: Option<String>,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub capsule: bool,
#[serde(default)]
pub offset: u64,
pub length: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RangeFrame {
pub offset: u64,
pub length: u64,
#[serde(with = "serde_bytes")]
pub bytes: Vec<u8>,
pub complete: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total_length: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub chunk_lens: Option<Vec<u64>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub chunk_index: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub inclusion_proof: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub root: Option<String>,
}
impl AvailabilityRequest {
pub fn encode(&self) -> Vec<u8> {
encode_framed(self)
}
pub async fn decode<R: AsyncRead + Unpin>(r: &mut R) -> io::Result<Self> {
decode_framed(r).await
}
}
impl AvailabilityResponse {
pub fn encode(&self) -> Vec<u8> {
encode_framed(self)
}
pub async fn decode<R: AsyncRead + Unpin>(r: &mut R) -> io::Result<Self> {
decode_framed(r).await
}
}
impl RangeRequest {
pub fn resource(
store_id: impl Into<String>,
retrieval_key: impl Into<String>,
offset: u64,
length: u64,
) -> Self {
RangeRequest {
store_id: store_id.into(),
retrieval_key: Some(retrieval_key.into()),
root: None,
capsule: false,
offset,
length,
}
}
pub fn encode(&self) -> Vec<u8> {
encode_framed(self)
}
pub async fn decode<R: AsyncRead + Unpin>(r: &mut R) -> io::Result<Self> {
decode_framed(r).await
}
}
impl RangeFrame {
pub fn encode(&self) -> Vec<u8> {
encode_framed(self)
}
pub async fn decode<R: AsyncRead + Unpin>(r: &mut R) -> io::Result<Option<Self>> {
decode_framed_opt(r).await
}
}
const MAX_FRAMED_BODY: usize = 64 * 1024;
fn encode_framed<T: Serialize>(value: &T) -> Vec<u8> {
let body = serde_json::to_vec(value).expect("control message serializes");
let mut out = Vec::with_capacity(4 + body.len());
out.extend_from_slice(&(body.len() as u32).to_be_bytes());
out.extend_from_slice(&body);
out
}
async fn decode_framed<T: for<'de> Deserialize<'de>, R: AsyncRead + Unpin>(
r: &mut R,
) -> io::Result<T> {
let mut len_buf = [0u8; 4];
r.read_exact(&mut len_buf).await?;
let len = u32::from_be_bytes(len_buf) as usize;
if len > MAX_FRAMED_BODY {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"control message too large",
));
}
let mut body = vec![0u8; len];
r.read_exact(&mut body).await?;
serde_json::from_slice(&body).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
}
async fn decode_framed_opt<T: for<'de> Deserialize<'de>, R: AsyncRead + Unpin>(
r: &mut R,
) -> io::Result<Option<T>> {
let mut len_buf = [0u8; 4];
match r.read_exact(&mut len_buf).await {
Ok(_) => {}
Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => return Ok(None),
Err(e) => return Err(e),
}
let len = u32::from_be_bytes(len_buf) as usize;
if len > MAX_FRAMED_BODY {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"control message too large",
));
}
let mut body = vec![0u8; len];
r.read_exact(&mut body).await?;
serde_json::from_slice(&body)
.map(Some)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
}
enum MuxCommand {
OpenOutbound(tokio::sync::oneshot::Sender<Result<yamux::Stream, String>>),
}
pub struct PeerSession {
cmd_tx: tokio::sync::mpsc::Sender<MuxCommand>,
inbound_rx: tokio::sync::mpsc::Receiver<PeerStream>,
}
impl std::fmt::Debug for PeerSession {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PeerSession").finish_non_exhaustive()
}
}
impl PeerSession {
pub fn client<S>(io: S) -> Self
where
S: AsyncRead + AsyncWrite + Send + Unpin + 'static,
{
Self::new(io, yamux::Mode::Client)
}
pub fn server<S>(io: S) -> Self
where
S: AsyncRead + AsyncWrite + Send + Unpin + 'static,
{
Self::new(io, yamux::Mode::Server)
}
fn new<S>(io: S, mode: yamux::Mode) -> Self
where
S: AsyncRead + AsyncWrite + Send + Unpin + 'static,
{
let (cmd_tx, cmd_rx) = tokio::sync::mpsc::channel::<MuxCommand>(64);
let (inbound_tx, inbound_rx) = tokio::sync::mpsc::channel::<PeerStream>(64);
let conn = yamux::Connection::new(io.compat(), yamux::Config::default(), mode);
tokio::spawn(drive_connection(conn, cmd_rx, inbound_tx));
PeerSession { cmd_tx, inbound_rx }
}
pub async fn open_stream(&mut self) -> io::Result<PeerStream> {
let (tx, rx) = tokio::sync::oneshot::channel();
self.cmd_tx
.send(MuxCommand::OpenOutbound(tx))
.await
.map_err(|_| io::Error::other("mux driver closed"))?;
let stream = rx
.await
.map_err(|_| io::Error::other("mux driver dropped request"))?
.map_err(io::Error::other)?;
Ok(stream.compat())
}
pub async fn accept_stream(&mut self) -> Option<PeerStream> {
self.inbound_rx.recv().await
}
pub async fn open_range_stream(&mut self, req: &RangeRequest) -> io::Result<PeerStream> {
let mut stream = self.open_stream().await?;
stream.write_all(&req.encode()).await?;
stream.flush().await?;
Ok(stream)
}
pub async fn query_availability(
&mut self,
items: Vec<AvailabilityItem>,
) -> io::Result<AvailabilityResponse> {
let req = AvailabilityRequest { items };
let mut stream = self.open_stream().await?;
stream.write_all(&req.encode()).await?;
stream.flush().await?;
AvailabilityResponse::decode(&mut stream).await
}
}
async fn drive_connection<T>(
mut conn: yamux::Connection<T>,
mut cmd_rx: tokio::sync::mpsc::Receiver<MuxCommand>,
inbound_tx: tokio::sync::mpsc::Sender<PeerStream>,
) where
T: futures::AsyncRead + futures::AsyncWrite + Send + Unpin + 'static,
{
use std::future::poll_fn;
loop {
tokio::select! {
cmd = cmd_rx.recv() => {
match cmd {
Some(MuxCommand::OpenOutbound(reply)) => {
let res = poll_fn(|cx| conn.poll_new_outbound(cx)).await;
let _ = reply.send(res.map_err(|e| e.to_string()));
}
None => {
let _ = poll_fn(|cx| conn.poll_close(cx)).await;
return;
}
}
}
inbound = poll_fn(|cx| conn.poll_next_inbound(cx)) => {
match inbound {
Some(Ok(stream)) => {
let _ = inbound_tx.try_send(stream.compat());
}
Some(Err(_)) | None => {
return;
}
}
}
}
}
}