From f8c3b40739f35374a23f1418181bb25bb2cd2a4e Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 24 Nov 2023 21:12:59 +0000 Subject: [PATCH] sys::sendfile adding solaris' sendfilev wrapper proposal. --- changelog/2207.added.md | 1 + src/sys/sendfile.rs | 61 +++++++++++++++++++++++++++++++++++++++++ test/test.rs | 1 + test/test_sendfile.rs | 60 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+) create mode 100644 changelog/2207.added.md diff --git a/changelog/2207.added.md b/changelog/2207.added.md new file mode 100644 index 0000000000..ddaa904221 --- /dev/null +++ b/changelog/2207.added.md @@ -0,0 +1 @@ +Added `sendfilev` in sys::sendfile for solarish diff --git a/src/sys/sendfile.rs b/src/sys/sendfile.rs index ced85d48e8..579013b4b8 100644 --- a/src/sys/sendfile.rs +++ b/src/sys/sendfile.rs @@ -118,6 +118,42 @@ cfg_if! { ) } } + } else if #[cfg(solarish)] { + use std::os::unix::io::BorrowedFd; + use std::marker::PhantomData; + + #[derive(Debug, Copy, Clone)] + /// Mapping of the raw C sendfilevec_t struct + pub struct SendfileVec<'fd> { + raw: libc::sendfilevec_t, + phantom: PhantomData> + } + + impl<'fd> SendfileVec<'fd> { + /// initialises SendfileVec to send data directly from the process's address space + /// same in C with sfv_fd set to SFV_FD_SELF. + pub fn newself( + off: off_t, + len: usize + ) -> Self { + Self{raw: libc::sendfilevec_t{sfv_fd: libc::SFV_FD_SELF, sfv_flag: 0, sfv_off: off, sfv_len: len}, phantom: PhantomData} + } + + /// initialises SendfileVec to send data from `fd`. + pub fn new( + fd: BorrowedFd<'fd>, + off: off_t, + len: usize + ) -> SendfileVec<'fd> { + Self{raw: libc::sendfilevec_t{sfv_fd: fd.as_raw_fd(), sfv_flag: 0, sfv_off:off, sfv_len: len}, phantom: PhantomData} + } + } + + impl From> for libc::sendfilevec_t { + fn from<'fd>(vec: SendfileVec) -> libc::sendfilevec_t { + vec.raw + } + } } } @@ -285,5 +321,30 @@ cfg_if! { }; (Errno::result(return_code).and(Ok(())), len) } + } else if #[cfg(solarish)] { + /// Write data from the vec arrays to `out_sock` and returns a `Result` and a + /// count of bytes written. + /// + /// Each `SendfileVec` set needs to be instantiated either with `SendfileVec::new` or + /// `SendfileVec::newself`. + /// + /// The former allows to send data from a file descriptor through `fd`, + /// from an offset `off` and for a given amount of data `len`. + /// + /// The latter allows to send data from the process's address space, from an offset `off` + /// and for a given amount of data `len`. + /// + /// For more information, see + /// [the sendfilev(3) man page.](https://illumos.org/man/3EXT/sendfilev) + pub fn sendfilev( + out_sock: F, + vec: &[SendfileVec] + ) -> (Result<()>, usize) { + let mut len = 0usize; + let return_code = unsafe { + libc::sendfilev(out_sock.as_fd().as_raw_fd(), vec.as_ptr() as *const libc::sendfilevec_t, vec.len() as i32, &mut len) + }; + (Errno::result(return_code).and(Ok(())), len) + } } } diff --git a/test/test.rs b/test/test.rs index 41c3dce8e6..eed532c194 100644 --- a/test/test.rs +++ b/test/test.rs @@ -44,6 +44,7 @@ mod test_sched; target_os = "freebsd", apple_targets, target_os = "linux", + solarish ))] mod test_sendfile; mod test_stat; diff --git a/test/test_sendfile.rs b/test/test_sendfile.rs index e6152c5dee..df3445b5c5 100644 --- a/test/test_sendfile.rs +++ b/test/test_sendfile.rs @@ -12,6 +12,7 @@ cfg_if! { target_os = "dragonfly", target_os = "freebsd", apple_targets, + solarish ))] { use std::net::Shutdown; use std::os::unix::net::UnixStream; @@ -204,3 +205,62 @@ fn test_sendfile_darwin() { assert_eq!(bytes_written as usize, bytes_read); assert_eq!(expected_string, read_string); } + +#[cfg(solarish)] +#[test] +fn test_sendfilev() { + use std::os::fd::AsFd; + // Declare the content + let header_strings = + ["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"]; + let body = "Xabcdef123456"; + let body_offset = 1usize; + let trailer_strings = ["\n", "Served by Make Believe\n"]; + + // Write data to files + let mut header_data = tempfile().unwrap(); + header_data + .write_all(header_strings.concat().as_bytes()) + .unwrap(); + let mut body_data = tempfile().unwrap(); + body_data.write_all(body.as_bytes()).unwrap(); + let mut trailer_data = tempfile().unwrap(); + trailer_data + .write_all(trailer_strings.concat().as_bytes()) + .unwrap(); + let (mut rd, wr) = UnixStream::pair().unwrap(); + let vec: &[SendfileVec] = &[ + SendfileVec::new( + header_data.as_fd(), + 0, + header_strings.iter().map(|s| s.len()).sum(), + ), + SendfileVec::new( + body_data.as_fd(), + body_offset as off_t, + body.len() - body_offset, + ), + SendfileVec::new( + trailer_data.as_fd(), + 0, + trailer_strings.iter().map(|s| s.len()).sum(), + ), + ]; + + let (res, bytes_written) = sendfilev(&wr, vec); + assert!(res.is_ok()); + wr.shutdown(Shutdown::Both).unwrap(); + + // Prepare the expected result + let expected_string = header_strings.concat() + + &body[body_offset..] + + &trailer_strings.concat(); + + // Verify the message that was sent + assert_eq!(bytes_written, expected_string.as_bytes().len()); + + let mut read_string = String::new(); + let bytes_read = rd.read_to_string(&mut read_string).unwrap(); + assert_eq!(bytes_written, bytes_read); + assert_eq!(expected_string, read_string); +}