Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/runtime/server/DirectoryRoute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ impl DirectoryRoute {
file_type: FileType::File,
pollable: false,
offset: body_offset,
positional: false,
length: Some(body_len),
idle_timeout: server.config().idle_timeout,
owner: StreamOwner::DirectoryRoute(guard.into_route()),
Expand Down
39 changes: 38 additions & 1 deletion src/runtime/server/FileResponseStream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ use bun_io::{BufferedReader, FileType, ReadState};
use bun_io::{FilePollFlag, PosixFlags as ReaderFlags};
use bun_jsc::JsCell;
use bun_ptr::RefPtr;
#[cfg(windows)]
use bun_sys::FdExt as _;
use bun_sys::{self as sys, Fd};
use bun_uws::{AnyResponse, WriteResult};

Expand Down Expand Up @@ -106,13 +108,46 @@ pub(crate) struct StartOptions {
pub pollable: bool,
/// Byte offset into the file to begin reading from.
pub offset: u64,
/// Read at `offset` even when it is 0: the position of a caller's `fd` can be anywhere.
pub positional: bool,
/// Maximum bytes to send; `None` reads to EOF. For regular files this
/// should be `stat.size - offset` (after Range/slice clamping).
pub length: Option<u64>,
pub idle_timeout: u8,
pub owner: StreamOwner,
}

#[cfg(windows)]
impl StartOptions {
/// libuv moves the position of a handle to read at an offset, so read through a second one.
fn with_own_handle(mut self) -> Self {
if !self.positional || self.auto_close {
return self;
}
let mut path_buf = bun_paths::path_buffer_pool::get();
let Ok(path) = sys::get_fd_path_z(self.fd, &mut path_buf) else {
return self;
};
let flags = sys::O::RDONLY | sys::O::NONBLOCK | sys::O::CLOEXEC;
let Ok(own) = sys::open(path, flags, 0) else {
return self;
};
// The path can name another file by now.
let same_file = matches!(
(sys::fstat(self.fd), sys::fstat(own)),
(Ok(theirs), Ok(ours)) if theirs.st_dev == ours.st_dev && theirs.st_ino == ours.st_ino
);
if !same_file {
own.close();
return self;
}
self.fd = own;
self.auto_close = true;
self.positional = false;
self
}
}

/// Who hears about the end of the stream. Exactly one of complete / abort /
/// error is delivered, exactly once.
pub(crate) enum StreamOwner {
Expand Down Expand Up @@ -157,6 +192,8 @@ impl StreamOwner {

impl FileResponseStream {
pub(crate) fn start(opts: StartOptions) {
#[cfg(windows)]
let opts = opts.with_own_handle();
let use_sendfile = can_sendfile(opts.resp, opts.file_type, opts.length);

// Heap-allocate; the raw pointer is handed to uWS callbacks and freed
Expand Down Expand Up @@ -242,7 +279,7 @@ impl FileResponseStream {
// SAFETY: `start()`/`start_file_offset()` re-enter this object through
// the parent pointer (`loop_`/`event_loop`), so no cell borrow spans them.
let reader = this_ref.reader_mut();
let start_result = if opts.offset > 0 {
let start_result = if opts.positional || opts.offset > 0 {
Comment thread
robobun marked this conversation as resolved.
Resolved
reader.start_file_offset(opts.fd, opts.pollable, opts.offset as usize)
} else {
reader.start(opts.fd, opts.pollable)
Expand Down
1 change: 1 addition & 0 deletions src/runtime/server/FileRoute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ impl FileRoute {
file_type,
pollable,
offset,
positional: false,
length,
idle_timeout: server.config().idle_timeout,
owner: StreamOwner::FileRoute(route),
Expand Down
2 changes: 2 additions & 0 deletions src/runtime/server/RequestContext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2027,6 +2027,8 @@ where
file_type,
pollable,
offset: sendfile.offset as u64,
// `!auto_close` is `Bun.file(fd)`: the caller opened the descriptor.
positional: is_regular && !auto_close,
length: if is_regular {
Some(sendfile.remain as u64)
} else {
Expand Down
Loading
Loading