pub mod proto;
pub mod receiver;
pub mod sender;
pub mod server;
pub mod transport;
use std::io::{Read, Write};
use std::path::Path;
use crate::filter::Filter;
use crate::net::proto::{Init, Msg, NetOptions, PROTO_VERSION, Role};
use crate::net::transport::{recv_msg, send_msg};
use crate::report::{Reporter, Stats};
use crate::{Error, Result, RunControl};
pub use proto::MAGIC;
pub use receiver::run_receiver;
pub use sender::run_sender;
pub use transport::{IoDuplex, duplex_pair};
fn handshake_initiator<C: Read + Write>(
conn: &mut C,
role: Role,
root: &Path,
options: NetOptions,
) -> Result<()> {
let mut hello = Vec::with_capacity(11);
hello.extend_from_slice(MAGIC);
hello.extend_from_slice(&PROTO_VERSION.to_be_bytes());
conn.write_all(&hello)?;
conn.flush()?;
match recv_msg(conn)? {
Msg::ServerHello { version, ok } => {
if !ok || version != PROTO_VERSION {
return Err(Error::Protocol(format!(
"remote ripsync protocol v{version} is incompatible (need v{PROTO_VERSION})"
)));
}
}
other => {
return Err(Error::Protocol(format!(
"expected ServerHello, got {other:?}"
)));
}
}
send_msg(
conn,
&Msg::Init(Init {
role,
root: root.to_path_buf(),
options,
}),
)?;
match recv_msg(conn)? {
Msg::InitAck => Ok(()),
other => Err(Error::Protocol(format!("expected InitAck, got {other:?}"))),
}
}
pub(crate) fn handshake_responder<C: Read + Write>(conn: &mut C) -> Result<Init> {
let mut buf = [0u8; 11];
conn.read_exact(&mut buf)?;
let magic_ok = &buf[..7] == MAGIC.as_slice();
let version = u32::from_be_bytes([buf[7], buf[8], buf[9], buf[10]]);
let ok = magic_ok && version == PROTO_VERSION;
send_msg(
conn,
&Msg::ServerHello {
version: PROTO_VERSION,
ok,
},
)?;
if !ok {
return Err(Error::Protocol(format!(
"bad handshake (magic_ok={magic_ok}, version={version})"
)));
}
match recv_msg(conn)? {
Msg::Init(init) => {
send_msg(conn, &Msg::InitAck)?;
Ok(init)
}
other => Err(Error::Protocol(format!("expected Init, got {other:?}"))),
}
}
pub fn run_responder<C: Read + Write, R: Reporter>(
conn: &mut C,
filter: &Filter,
threads: usize,
control: &RunControl,
reporter: &R,
) -> Result<Stats> {
let init = handshake_responder(conn)?;
match init.role {
Role::Push => run_receiver(
conn,
&init.root,
filter,
init.options,
threads,
control,
reporter,
),
Role::Pull => run_sender(conn, &init.root, filter, init.options, threads, control),
}
}
#[allow(clippy::too_many_arguments)]
pub fn run_initiator<C: Read + Write, R: Reporter>(
conn: &mut C,
role: Role,
local_root: &Path,
remote_root: &Path,
options: NetOptions,
filter: &Filter,
threads: usize,
control: &RunControl,
reporter: &R,
) -> Result<Stats> {
handshake_initiator(conn, role, remote_root, options)?;
match role {
Role::Push => run_sender(conn, local_root, filter, options, threads, control),
Role::Pull => run_receiver(
conn, local_root, filter, options, threads, control, reporter,
),
}
}