summaryrefslogtreecommitdiff
path: root/ext/flash/sendfile.rs
diff options
context:
space:
mode:
Diffstat (limited to 'ext/flash/sendfile.rs')
-rw-r--r--ext/flash/sendfile.rs81
1 files changed, 81 insertions, 0 deletions
diff --git a/ext/flash/sendfile.rs b/ext/flash/sendfile.rs
new file mode 100644
index 000000000..4efa7bc35
--- /dev/null
+++ b/ext/flash/sendfile.rs
@@ -0,0 +1,81 @@
+// Forked from https://github.com/Thomasdezeeuw/sendfile/blob/024f82cd4dede9048392a5bd6d8afcd4d5aa83d5/src/lib.rs
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use std::future::Future;
+use std::io;
+use std::os::unix::io::RawFd;
+use std::pin::Pin;
+use std::task::{self, Poll};
+
+pub struct SendFile {
+ pub io: (RawFd, RawFd),
+ pub written: usize,
+}
+
+impl SendFile {
+ #[inline]
+ pub fn try_send(&mut self) -> Result<usize, std::io::Error> {
+ #[cfg(target_os = "linux")]
+ {
+ // This is the maximum the Linux kernel will write in a single call.
+ let count = 0x7ffff000;
+ let mut offset = self.written as libc::off_t;
+
+ // SAFETY: call to libc::sendfile()
+ let res =
+ unsafe { libc::sendfile(self.io.1, self.io.0, &mut offset, count) };
+ if res == -1 {
+ Err(io::Error::last_os_error())
+ } else {
+ self.written = offset as usize;
+ Ok(res as usize)
+ }
+ }
+ #[cfg(target_os = "macos")]
+ {
+ // Send all bytes.
+ let mut length = 0;
+ // On macOS `length` is value-result parameter. It determines the number
+ // of bytes to write and returns the number of bytes written also in
+ // case of `EAGAIN` errors.
+ // SAFETY: call to libc::sendfile()
+ let res = unsafe {
+ libc::sendfile(
+ self.io.0,
+ self.io.1,
+ 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)
+ }
+ }
+ }
+}
+
+impl Future for SendFile {
+ type Output = Result<(), std::io::Error>;
+
+ fn poll(
+ mut self: Pin<&mut Self>,
+ _: &mut task::Context<'_>,
+ ) -> Poll<Self::Output> {
+ loop {
+ match self.try_send() {
+ 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, // Try again.
+ Err(err) => break Poll::Ready(Err(err)),
+ }
+ }
+ }
+}