#![warn(
anonymous_parameters,
bare_trait_objects,
missing_debug_implementations,
missing_docs,
rust_2018_idioms,
trivial_casts,
trivial_numeric_casts,
unused_extern_crates,
unused_import_braces,
unused_qualifications,
unused_results,
variant_size_differences
)]
#![cfg_attr(test, deny(warnings))]
#![doc(test(attr(deny(warnings))))]
use std::future::Future;
use std::io;
use std::marker::Unpin;
use std::os::unix::io::AsRawFd;
use std::pin::Pin;
use std::task::{self, Poll};
pub unsafe fn send_file<F, S>(file: F, socket: S) -> SendFile<F, S> {
SendFile {
file,
socket,
written: 0,
}
}
#[derive(Debug)]
pub struct SendFile<F, S> {
file: F,
socket: S,
written: usize,
}
impl<F, S> SendFile<F, S> {
pub fn into_inner(self) -> (F, S) {
(self.file, self.socket)
}
pub fn written(&self) -> usize {
self.written
}
}
impl<F, S> SendFile<F, S>
where
F: AsRawFd,
S: AsRawFd,
{
#[cfg(target_os = "macos")]
fn raw_send_file(&mut self) -> io::Result<usize> {
let file = self.file.as_raw_fd();
let socket = self.socket.as_raw_fd();
let mut length = 0; let res = unsafe {
libc::sendfile(
file,
socket,
self.written as libc::off_t,
&mut length,
std::ptr::null_mut(),
0,
)
};
self.written += length as usize;
if res == -1 {
Err(io::Error::last_os_error())
} else {
Ok(length as usize)
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
fn raw_send_file(&mut self) -> io::Result<usize> {
let file = self.file.as_raw_fd();
let socket = self.socket.as_raw_fd();
let count = 0x7ffff000;
let mut offset = self.written as libc::off_t;
let n = unsafe { libc::sendfile(socket, file, &mut offset, count) };
if n == -1 {
Err(io::Error::last_os_error())
} else {
self.written = offset as usize;
Ok(n as usize)
}
}
#[cfg(target_os = "freebsd")]
fn raw_send_file(&mut self) -> io::Result<usize> {
let file = self.file.as_raw_fd();
let socket = self.socket.as_raw_fd();
let mut bytes_sent = 0;
let res = unsafe {
libc::sendfile(
file,
socket,
self.written as libc::off_t,
0,
std::ptr::null_mut(),
&mut bytes_sent,
0,
)
};
self.written += bytes_sent as usize;
if res == -1 {
Err(io::Error::last_os_error())
} else {
Ok(bytes_sent as usize)
}
}
}
#[cfg(any(
target_os = "android",
target_os = "freebsd",
target_os = "linux",
target_os = "macos",
))]
impl<F, S> Future for SendFile<F, S>
where
F: AsRawFd + Unpin,
S: AsRawFd + Unpin,
{
type Output = io::Result<()>;
fn poll(mut self: Pin<&mut Self>, _: &mut task::Context<'_>) -> Poll<Self::Output> {
loop {
match self.raw_send_file() {
Ok(0) => break Poll::Ready(Ok(())),
Ok(_) => continue, Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => break Poll::Pending,
Err(ref err) if err.kind() == io::ErrorKind::Interrupted => continue, Err(err) => break Poll::Ready(Err(err)),
}
}
}
}