use std::io;
use std::net::TcpStream;
#[cfg(unix)]
use std::os::unix::net::UnixStream;
#[cfg(windows)]
use std::sync::Arc;
use crate::fs::File;
use crate::io::Handle;
#[cfg(unix)]
use crate::io::Interest;
pub(crate) const SEND_FILE_CHUNK: u64 = 1 << 20;
fn range(offset: u64, len: u64) -> io::Result<u64> {
offset.checked_add(len).ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
"the range of the file runs past what an offset can name",
)
})
}
fn read_at(file: &std::fs::File, buf: &mut [u8], offset: u64) -> io::Result<usize> {
#[cfg(unix)]
return std::os::unix::fs::FileExt::read_at(file, buf, offset);
#[cfg(windows)]
return std::os::windows::fs::FileExt::seek_read(file, buf, offset);
}
macro_rules! send_file {
($($ty:ty),* $(,)?) => {$(
impl Handle<$ty> {
pub async fn send_file(&self, file: &File, offset: u64, len: u64) -> io::Result<u64> {
range(offset, len)?;
core::future::poll_fn(|cx| self.source().poll_flush(cx)).await?;
if len == 0 {
return Ok(0);
}
let chunk = crate::rt::runtime::io_knobs::current().send_file_chunk;
#[cfg(unix)]
if crate::io::sys::SENDS_FILES {
return self.send_file_in_kernel(file, offset, len, chunk).await;
}
#[cfg(windows)]
if crate::io::sys::transmit_file_available() {
return self.send_file_transmitted(file, offset, len, chunk).await;
}
self.send_file_by_copy(file, offset, len, chunk).await
}
#[cfg(unix)]
async fn send_file_in_kernel(
&self,
file: &File,
offset: u64,
len: u64,
chunk: u64,
) -> io::Result<u64> {
let sock = self.source().lease()?;
let from = file.source().lease()?;
let mut sent = 0u64;
while sent < len {
let want = (len - sent).min(chunk) as usize;
let at = offset + sent;
let (out, src) = (sock.clone(), from.clone());
let step = crate::rt::blocking::call(move || {
Ok(crate::io::sys::send_file(out.raw(), src.raw(), at, want))
})
.await?;
match step {
Ok(0) => break,
Ok(n) => {
crate::rt::counters::tally::wrote(n);
sent += n as u64;
}
Err(err) if err.kind() == io::ErrorKind::WouldBlock => {
let raw = sock.raw();
self.async_io(Interest::WRITABLE, || crate::io::sys::poll_writable(raw))
.await?;
}
Err(err) => {
crate::rt::counters::io_error(&err);
return Err(err);
}
}
}
Ok(sent)
}
#[cfg(windows)]
async fn send_file_transmitted(
&self,
file: &File,
offset: u64,
len: u64,
chunk: u64,
) -> io::Result<u64> {
use std::os::windows::io::AsRawHandle;
let raw = file.get_ref().as_raw_handle() as usize as crate::io::raw::Raw;
let Ok(handle) = crate::io::sys::reopen_overlapped(raw) else {
return self.send_file_by_copy(file, offset, len, chunk).await;
};
let handle = Arc::new(handle);
let len = len.min(file.get_ref().metadata()?.len().saturating_sub(offset));
let mut sent = 0u64;
while sent < len {
let want = (len - sent).min(chunk) as u32;
let wrote = self
.source()
.send_file_range(Arc::clone(&handle), offset + sent, want)
.await?;
sent += wrote as u64;
}
Ok(sent)
}
async fn send_file_by_copy(
&self,
file: &File,
offset: u64,
len: u64,
chunk: u64,
) -> io::Result<u64> {
let from = file.source().lease()?;
let mut buf = vec![0u8; chunk.min(len) as usize];
let mut sent = 0u64;
while sent < len {
let want = (len - sent).min(buf.len() as u64) as usize;
let at = offset + sent;
let from = from.clone();
let (read, back) = crate::rt::blocking::call(move || {
let read = read_at(&from.file(), &mut buf[..want], at);
Ok((read, buf))
})
.await?;
buf = back;
let n = read?;
crate::rt::counters::tally::read(n);
if n == 0 {
break;
}
buf.truncate(n);
let (wrote, back) = self.write_owned(buf).await;
buf = back;
wrote?;
buf.resize(chunk.min(len) as usize, 0);
sent += n as u64;
}
Ok(sent)
}
}
)*};
}
send_file!(TcpStream);
#[cfg(unix)]
send_file!(UnixStream);