Add docstrings
This commit is contained in:
parent
43d2e85a00
commit
bfb959bca8
7 changed files with 268 additions and 23 deletions
|
|
@ -3,13 +3,33 @@ use std::net::Ipv4Addr;
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::{env, process};
|
||||
|
||||
/// Configuration `struct` used for parsing TFTP options from user
|
||||
/// input.
|
||||
///
|
||||
/// This `struct` is meant to be created by [`Config::new()`]. See its
|
||||
/// documentation for more.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// // Create TFTP configuration from user arguments.
|
||||
/// use std::env;
|
||||
/// use tftpd::Config;
|
||||
///
|
||||
/// let config = Config::new(env::args()).unwrap();
|
||||
/// ```
|
||||
pub struct Config {
|
||||
/// Local IP address of the TFTP Server. (default: 127.0.0.1)
|
||||
pub ip_address: Ipv4Addr,
|
||||
/// Local Port number of the TFTP Server. (default: 69)
|
||||
pub port: u16,
|
||||
/// Default directory of the TFTP Server. (default: current working directory)
|
||||
pub directory: PathBuf,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Creates a new configuration by parsing the supplied arguments. It is
|
||||
/// intended for use with [`env::args()`].
|
||||
pub fn new<T>(mut args: T) -> Result<Config, Box<dyn Error>>
|
||||
where
|
||||
T: Iterator<Item = String>,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,22 @@
|
|||
use std::error::Error;
|
||||
|
||||
/// Allows conversions between byte arrays and other types.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use tftpd::Convert;
|
||||
///
|
||||
/// assert_eq!(Convert::to_u16(&[0x01, 0x02]).unwrap(), 0x0102);
|
||||
///
|
||||
/// let (result, index) = Convert::to_string(b"hello world\0", 0).unwrap();
|
||||
/// assert_eq!(result, "hello world");
|
||||
/// assert_eq!(index, 11);
|
||||
/// ```
|
||||
pub struct Convert;
|
||||
|
||||
impl Convert {
|
||||
/// Converts a [`u8`] slice to a [`u16`].
|
||||
pub fn to_u16(buf: &[u8]) -> Result<u16, &'static str> {
|
||||
if buf.len() < 2 {
|
||||
Err("Error when converting to u16")
|
||||
|
|
@ -11,6 +25,8 @@ impl Convert {
|
|||
}
|
||||
}
|
||||
|
||||
/// Converts a zero terminated [`u8`] slice to a [`String`], and returns the
|
||||
/// size of the [`String`]. Useful for TFTP packet conversions.
|
||||
pub fn to_string(buf: &[u8], start: usize) -> Result<(String, usize), Box<dyn Error>> {
|
||||
match buf[start..].iter().position(|&b| b == 0x00) {
|
||||
Some(index) => Ok((
|
||||
|
|
|
|||
21
src/lib.rs
21
src/lib.rs
|
|
@ -1,3 +1,19 @@
|
|||
#![warn(missing_docs)]
|
||||
|
||||
//! Multithreaded TFTP daemon implemented in pure Rust without dependencies.
|
||||
//!
|
||||
//! This server implements [RFC 1350](https://www.rfc-editor.org/rfc/rfc1350), The TFTP Protocol (Revision 2).
|
||||
//! It also supports the following [RFC 2347](https://www.rfc-editor.org/rfc/rfc2347) TFTP Option Extensions:
|
||||
//!
|
||||
//! - [RFC 2348](https://www.rfc-editor.org/rfc/rfc2348) Blocksize Option
|
||||
//! - [RFC 2349](https://www.rfc-editor.org/rfc/rfc2349) Timeout Interval Option
|
||||
//! - [RFC 2349](https://www.rfc-editor.org/rfc/rfc2349) Transfer Size Option
|
||||
//!
|
||||
//! # Security
|
||||
//!
|
||||
//! Since TFTP servers do not offer any type of login or access control mechanisms, this server only allows
|
||||
//! transfer and receiving inside a chosen folder, and disallows external file access.
|
||||
|
||||
mod config;
|
||||
mod convert;
|
||||
mod message;
|
||||
|
|
@ -8,5 +24,10 @@ mod worker;
|
|||
pub use config::Config;
|
||||
pub use convert::Convert;
|
||||
pub use message::Message;
|
||||
pub use packet::ErrorCode;
|
||||
pub use packet::Opcode;
|
||||
pub use packet::OptionType;
|
||||
pub use packet::Packet;
|
||||
pub use packet::TransferOption;
|
||||
pub use server::Server;
|
||||
pub use worker::Worker;
|
||||
|
|
|
|||
|
|
@ -3,13 +3,33 @@ use std::{
|
|||
net::{SocketAddr, UdpSocket},
|
||||
};
|
||||
|
||||
use crate::packet::{ErrorCode, Packet, TransferOption};
|
||||
use crate::{ErrorCode, Packet, TransferOption};
|
||||
|
||||
/// Message `struct` is used for easy message transmission of common TFTP
|
||||
/// message types.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::{net::{SocketAddr, UdpSocket}, str::FromStr};
|
||||
/// use tftpd::{Message, ErrorCode};
|
||||
///
|
||||
/// // Send a FileNotFound error.
|
||||
/// Message::send_error_to(
|
||||
/// &UdpSocket::bind(SocketAddr::from_str("127.0.0.1:69").unwrap()).unwrap(),
|
||||
/// &SocketAddr::from_str("127.0.0.1:1234").unwrap(),
|
||||
/// ErrorCode::FileNotFound,
|
||||
/// "file does not exist".to_string(),
|
||||
/// );
|
||||
/// ```
|
||||
pub struct Message;
|
||||
|
||||
const MAX_REQUEST_PACKET_SIZE: usize = 512;
|
||||
|
||||
impl Message {
|
||||
/// Sends a data packet to the socket's connected remote. See
|
||||
/// [`UdpSocket`] for more information about connected
|
||||
/// sockets.
|
||||
pub fn send_data(
|
||||
socket: &UdpSocket,
|
||||
block_num: u16,
|
||||
|
|
@ -20,12 +40,18 @@ impl Message {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Sends an acknowledgement packet to the socket's connected remote. See
|
||||
/// [`UdpSocket`] for more information about connected
|
||||
/// sockets.
|
||||
pub fn send_ack(socket: &UdpSocket, block_number: u16) -> Result<(), Box<dyn Error>> {
|
||||
socket.send(&Packet::Ack(block_number).serialize()?)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sends an error packet to the socket's connected remote. See
|
||||
/// [`UdpSocket`] for more information about connected
|
||||
/// sockets.
|
||||
pub fn send_error(
|
||||
socket: &UdpSocket,
|
||||
code: ErrorCode,
|
||||
|
|
@ -36,6 +62,7 @@ impl Message {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Sends an error packet to the supplied [`SocketAddr`].
|
||||
pub fn send_error_to<'a>(
|
||||
socket: &UdpSocket,
|
||||
to: &SocketAddr,
|
||||
|
|
@ -58,15 +85,20 @@ impl Message {
|
|||
Err(msg.into())
|
||||
}
|
||||
|
||||
/// Sends an option acknowledgement packet to the socket's connected remote.
|
||||
/// See [`UdpSocket`] for more information about connected sockets.
|
||||
pub fn send_oack(
|
||||
socket: &UdpSocket,
|
||||
options: Vec<TransferOption>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
socket.send(&Packet::Oack { options }.serialize()?)?;
|
||||
socket.send(&Packet::Oack(options).serialize()?)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Receives a packet from the socket's connected remote, and returns the
|
||||
/// parsed [`Packet`]. This function cannot handle large data packets due to
|
||||
/// the limited buffer size. For handling data packets, see [`Message::recv_data()`].
|
||||
pub fn recv(socket: &UdpSocket) -> Result<Packet, Box<dyn Error>> {
|
||||
let mut buf = [0; MAX_REQUEST_PACKET_SIZE];
|
||||
let number_of_bytes = socket.recv(&mut buf)?;
|
||||
|
|
@ -75,14 +107,10 @@ impl Message {
|
|||
Ok(packet)
|
||||
}
|
||||
|
||||
pub fn recv_data(socket: &UdpSocket, size: usize) -> Result<Packet, Box<dyn Error>> {
|
||||
let mut buf = vec![0; size + 4];
|
||||
let number_of_bytes = socket.recv(&mut buf)?;
|
||||
let packet = Packet::deserialize(&buf[..number_of_bytes])?;
|
||||
|
||||
Ok(packet)
|
||||
}
|
||||
|
||||
/// Receives a packet from any incoming remote request, and returns the
|
||||
/// parsed [`Packet`] and the requesting [`SocketAddr`]. This function cannot handle
|
||||
/// large data packets due to the limited buffer size, so it is intended for
|
||||
/// only accepting incoming requests.
|
||||
pub fn recv_from(socket: &UdpSocket) -> Result<(Packet, SocketAddr), Box<dyn Error>> {
|
||||
let mut buf = [0; MAX_REQUEST_PACKET_SIZE];
|
||||
let (number_of_bytes, from) = socket.recv_from(&mut buf)?;
|
||||
|
|
@ -90,4 +118,15 @@ impl Message {
|
|||
|
||||
Ok((packet, from))
|
||||
}
|
||||
|
||||
/// Receives a data packet from the socket's connected remote, and returns the
|
||||
/// parsed [`Packet`]. The received packet can actually be of any type, however,
|
||||
/// this function also allows supplying the buffer size for an incoming request.
|
||||
pub fn recv_data(socket: &UdpSocket, size: usize) -> Result<Packet, Box<dyn Error>> {
|
||||
let mut buf = vec![0; size + 4];
|
||||
let number_of_bytes = socket.recv(&mut buf)?;
|
||||
let packet = Packet::deserialize(&buf[..number_of_bytes])?;
|
||||
|
||||
Ok(packet)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
125
src/packet.rs
125
src/packet.rs
|
|
@ -1,32 +1,62 @@
|
|||
use crate::Convert;
|
||||
use std::{error::Error, fmt};
|
||||
|
||||
/// Packet `enum` represents the valid TFTP packet types.
|
||||
///
|
||||
/// This `enum` has function implementaions for serializing [`Packet`]s into
|
||||
/// [`Vec<u8>`]s and deserializing [`u8`] slices to [`Packet`]s.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// use tftpd::Packet;
|
||||
///
|
||||
/// let packet = Packet::Data { block_num: 15, data: vec![0x01, 0x02, 0x03] };
|
||||
///
|
||||
/// assert_eq!(packet.serialize().unwrap(), vec![0x00, 0x03, 0x00, 0x0F, 0x01, 0x02, 0x03]);
|
||||
/// assert_eq!(Packet::deserialize(&[0x00, 0x03, 0x00, 0x0F, 0x01, 0x02, 0x03]).unwrap(), packet);
|
||||
/// ```
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Packet {
|
||||
/// Read Request `struct`
|
||||
Rrq {
|
||||
/// Name of the requested file
|
||||
filename: String,
|
||||
/// Transfer mode
|
||||
mode: String,
|
||||
/// Transfer options
|
||||
options: Vec<TransferOption>,
|
||||
},
|
||||
/// Write Request `struct`
|
||||
Wrq {
|
||||
/// Name of the requested file
|
||||
filename: String,
|
||||
/// Transfer mode
|
||||
mode: String,
|
||||
/// Transfer options
|
||||
options: Vec<TransferOption>,
|
||||
},
|
||||
/// Data `struct`
|
||||
Data {
|
||||
/// Block number
|
||||
block_num: u16,
|
||||
/// Data
|
||||
data: Vec<u8>,
|
||||
},
|
||||
/// Acknowledgement `tuple` with block number
|
||||
Ack(u16),
|
||||
/// Error `struct`
|
||||
Error {
|
||||
/// Error code
|
||||
code: ErrorCode,
|
||||
/// Error message
|
||||
msg: String,
|
||||
},
|
||||
Oack {
|
||||
options: Vec<TransferOption>,
|
||||
},
|
||||
/// Option acknowledgement `tuple` with transfer options
|
||||
Oack(Vec<TransferOption>),
|
||||
}
|
||||
|
||||
impl Packet {
|
||||
/// Deserializes a [`u8`] slice into a [`Packet`].
|
||||
pub fn deserialize(buf: &[u8]) -> Result<Packet, Box<dyn Error>> {
|
||||
let opcode = Opcode::from_u16(Convert::to_u16(&buf[0..=1])?)?;
|
||||
|
||||
|
|
@ -39,29 +69,50 @@ impl Packet {
|
|||
}
|
||||
}
|
||||
|
||||
/// Serializes a [`Packet`] into a [`Vec<u8>`].
|
||||
pub fn serialize(&self) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
match self {
|
||||
Packet::Data { block_num, data } => Ok(serialize_data(block_num, data)),
|
||||
Packet::Ack(block_num) => Ok(serialize_ack(block_num)),
|
||||
Packet::Error { code, msg } => Ok(serialize_error(code, msg)),
|
||||
Packet::Oack { options } => Ok(serialize_oack(options)),
|
||||
Packet::Oack(options) => Ok(serialize_oack(options)),
|
||||
_ => Err("Invalid packet".into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Opcode `enum` represents the opcodes used in the TFTP definition.
|
||||
///
|
||||
/// This `enum` has function implementations for converting [`u16`]s to
|
||||
/// [`Opcode`]s and [`Opcode`]s to [`u8`] arrays.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use tftpd::Opcode;
|
||||
///
|
||||
/// assert_eq!(Opcode::from_u16(3).unwrap(), Opcode::Data);
|
||||
/// assert_eq!(Opcode::Ack.as_bytes(), [0x00, 0x04]);
|
||||
/// ```
|
||||
#[repr(u16)]
|
||||
#[derive(PartialEq)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Opcode {
|
||||
/// Read request opcode
|
||||
Rrq = 0x0001,
|
||||
/// Write request opcode
|
||||
Wrq = 0x0002,
|
||||
/// Data opcode
|
||||
Data = 0x0003,
|
||||
/// Acknowledgement opcode
|
||||
Ack = 0x0004,
|
||||
/// Error opcode
|
||||
Error = 0x0005,
|
||||
/// Option acknowledgement opcode
|
||||
Oack = 0x0006,
|
||||
}
|
||||
|
||||
impl Opcode {
|
||||
/// Converts a [`u16`] to an [`Opcode`].
|
||||
pub fn from_u16(val: u16) -> Result<Opcode, &'static str> {
|
||||
match val {
|
||||
0x0001 => Ok(Opcode::Rrq),
|
||||
|
|
@ -74,18 +125,37 @@ impl Opcode {
|
|||
}
|
||||
}
|
||||
|
||||
/// Converts a [`u16`] to a [`u8`] array with 2 elements.
|
||||
pub fn as_bytes(self) -> [u8; 2] {
|
||||
return (self as u16).to_be_bytes();
|
||||
}
|
||||
}
|
||||
|
||||
/// TransferOption `struct` represents the TFTP transfer options.
|
||||
///
|
||||
/// This `struct` has a function implementation for converting [`TransferOption`]s
|
||||
/// to [`Vec<u8>`]s.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use tftpd::{TransferOption, OptionType};
|
||||
///
|
||||
/// assert_eq!(TransferOption { option: OptionType::BlockSize, value: 1432 }.as_bytes(), vec![
|
||||
/// 0x62, 0x6C, 0x6B, 0x73, 0x69, 0x7A, 0x65, 0x00, 0x31, 0x34, 0x33, 0x32,
|
||||
/// 0x00,
|
||||
/// ]);
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct TransferOption {
|
||||
/// Type of the option
|
||||
pub option: OptionType,
|
||||
/// Value of the option
|
||||
pub value: usize,
|
||||
}
|
||||
|
||||
impl TransferOption {
|
||||
/// Converts a [`TransferOption`] to a [`Vec<u8>`].
|
||||
pub fn as_bytes(&self) -> Vec<u8> {
|
||||
[
|
||||
self.option.as_str().as_bytes(),
|
||||
|
|
@ -97,15 +167,32 @@ impl TransferOption {
|
|||
}
|
||||
}
|
||||
|
||||
/// OptionType `enum` represents the TFTP option types
|
||||
///
|
||||
/// This `enum` has function implementations for conversion between
|
||||
/// [`OptionType`]s and [`str`]s.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use tftpd::OptionType;
|
||||
///
|
||||
/// assert_eq!(OptionType::BlockSize, OptionType::from_str("blksize").unwrap());
|
||||
/// assert_eq!("tsize", OptionType::TransferSize.as_str());
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum OptionType {
|
||||
/// Block Size option type
|
||||
BlockSize,
|
||||
/// Transfer Size option type
|
||||
TransferSize,
|
||||
/// Timeout option type
|
||||
Timeout,
|
||||
}
|
||||
|
||||
impl OptionType {
|
||||
fn as_str(&self) -> &'static str {
|
||||
/// Converts an [`OptionType`] to a [`str`].
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
OptionType::BlockSize => "blksize",
|
||||
OptionType::TransferSize => "tsize",
|
||||
|
|
@ -113,7 +200,8 @@ impl OptionType {
|
|||
}
|
||||
}
|
||||
|
||||
fn from_str(value: &str) -> Result<Self, &'static str> {
|
||||
/// Converts a [`str`] to an [`OptionType`].
|
||||
pub fn from_str(value: &str) -> Result<Self, &'static str> {
|
||||
match value {
|
||||
"blksize" => Ok(OptionType::BlockSize),
|
||||
"tsize" => Ok(OptionType::TransferSize),
|
||||
|
|
@ -123,20 +211,42 @@ impl OptionType {
|
|||
}
|
||||
}
|
||||
|
||||
/// ErrorCode `enum` represents the error codes used in the TFTP definition.
|
||||
///
|
||||
/// This `enum` has function implementations for converting [`u16`]s to
|
||||
/// [`ErrorCode`]s and [`ErrorCode`]s to [`u8`] arrays.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use tftpd::ErrorCode;
|
||||
///
|
||||
/// assert_eq!(ErrorCode::from_u16(3).unwrap(), ErrorCode::DiskFull);
|
||||
/// assert_eq!(ErrorCode::FileExists.as_bytes(), [0x00, 0x06]);
|
||||
/// ```
|
||||
#[repr(u16)]
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub enum ErrorCode {
|
||||
/// Not Defined error code
|
||||
NotDefined = 0,
|
||||
/// File not found error code
|
||||
FileNotFound = 1,
|
||||
/// Access violation error code
|
||||
AccessViolation = 2,
|
||||
/// Disk full error code
|
||||
DiskFull = 3,
|
||||
/// Illegal operation error code
|
||||
IllegalOperation = 4,
|
||||
/// Unknown ID error code
|
||||
UnknownId = 5,
|
||||
/// File exists error code
|
||||
FileExists = 6,
|
||||
/// No such user error code
|
||||
NoSuchUser = 7,
|
||||
}
|
||||
|
||||
impl ErrorCode {
|
||||
/// Converts a [`u16`] to an [`ErrorCode`].
|
||||
pub fn from_u16(code: u16) -> Result<ErrorCode, &'static str> {
|
||||
match code {
|
||||
0 => Ok(ErrorCode::NotDefined),
|
||||
|
|
@ -151,6 +261,7 @@ impl ErrorCode {
|
|||
}
|
||||
}
|
||||
|
||||
/// Converts an [`ErrorCode`] to a [`u8`] array with 2 elements.
|
||||
pub fn as_bytes(self) -> [u8; 2] {
|
||||
return (self as u16).to_be_bytes();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,31 @@
|
|||
use crate::packet::{ErrorCode, Packet, TransferOption};
|
||||
use crate::{Config, Message, Worker};
|
||||
use crate::{ErrorCode, Packet, TransferOption};
|
||||
use std::error::Error;
|
||||
use std::net::{SocketAddr, UdpSocket};
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Server `struct` is used for handling incoming TFTP requests.
|
||||
///
|
||||
/// This `struct` is meant to be created by [`Server::new()`]. See its
|
||||
/// documentation for more.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// // Create the TFTP server.
|
||||
/// use std::env;
|
||||
/// use tftpd::{Config, Server};
|
||||
///
|
||||
/// let config = Config::new(env::args()).unwrap();
|
||||
/// let server = Server::new(&config).unwrap();
|
||||
/// ```
|
||||
pub struct Server {
|
||||
socket: UdpSocket,
|
||||
directory: PathBuf,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
/// Creates the TFTP Server with the supplied [`Config`].
|
||||
pub fn new(config: &Config) -> Result<Server, Box<dyn Error>> {
|
||||
let socket = UdpSocket::bind(SocketAddr::from((config.ip_address, config.port)))?;
|
||||
|
||||
|
|
@ -21,6 +37,7 @@ impl Server {
|
|||
Ok(server)
|
||||
}
|
||||
|
||||
/// Starts listening for connections. Note that this function does not finish running until termination.
|
||||
pub fn listen(&self) {
|
||||
loop {
|
||||
if let Ok((packet, from)) = Message::recv_from(&self.socket) {
|
||||
|
|
|
|||
|
|
@ -8,15 +8,32 @@ use std::{
|
|||
time::Duration,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
packet::{ErrorCode, OptionType, Packet, TransferOption},
|
||||
Message,
|
||||
};
|
||||
use crate::{ErrorCode, Message, OptionType, Packet, TransferOption};
|
||||
|
||||
/// Worker `struct` is used for multithreaded file sending and receiving.
|
||||
/// It creates a new socket using the Server's IP and a random port
|
||||
/// requested from the OS to communicate with the requesting client.
|
||||
///
|
||||
/// See [`Worker::send()`] and [`Worker::receive()`] for more details.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::{net::SocketAddr, path::PathBuf, str::FromStr};
|
||||
/// use tftpd::Worker;
|
||||
///
|
||||
/// // Send a file, responding to a read request.
|
||||
/// Worker::send(
|
||||
/// SocketAddr::from_str("127.0.0.1:1234").unwrap(),
|
||||
/// SocketAddr::from_str("127.0.0.1:4321").unwrap(),
|
||||
/// PathBuf::from_str("/home/rust/test.txt").unwrap(),
|
||||
/// vec![]
|
||||
/// );
|
||||
/// ```
|
||||
pub struct Worker;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct WorkerOptions {
|
||||
struct WorkerOptions {
|
||||
blk_size: usize,
|
||||
t_size: usize,
|
||||
timeout: u64,
|
||||
|
|
@ -33,6 +50,8 @@ const DEFAULT_TIMEOUT_SECS: u64 = 5;
|
|||
const DEFAULT_BLOCK_SIZE: usize = 512;
|
||||
|
||||
impl Worker {
|
||||
/// Sends a file to the remote [`SocketAddr`] that has sent a read request using
|
||||
/// a random port, asynchronously.
|
||||
pub fn send(
|
||||
addr: SocketAddr,
|
||||
remote: SocketAddr,
|
||||
|
|
@ -56,6 +75,8 @@ impl Worker {
|
|||
});
|
||||
}
|
||||
|
||||
/// Receives a file from the remote [`SocketAddr`] that has sent a write request using
|
||||
/// a random port, asynchronously.
|
||||
pub fn receive(
|
||||
addr: SocketAddr,
|
||||
remote: SocketAddr,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue