1use std::io::prelude::*; 2use std::os::unix::prelude::*; 3 4use libc::off_t; 5use nix::sys::sendfile::*; 6use tempfile::tempfile; 7 8cfg_if! { 9 if #[cfg(any(target_os = "android", target_os = "linux"))] { 10 use nix::unistd::{close, pipe, read}; 11 } else if #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos"))] { 12 use std::net::Shutdown; 13 use std::os::unix::net::UnixStream; 14 } 15} 16 17#[cfg(any(target_os = "android", target_os = "linux"))] 18#[test] 19fn test_sendfile_linux() { 20 const CONTENTS: &[u8] = b"abcdef123456"; 21 let mut tmp = tempfile().unwrap(); 22 tmp.write_all(CONTENTS).unwrap(); 23 24 let (rd, wr) = pipe().unwrap(); 25 let mut offset: off_t = 5; 26 let res = sendfile(wr, tmp.as_raw_fd(), Some(&mut offset), 2).unwrap(); 27 28 assert_eq!(2, res); 29 30 let mut buf = [0u8; 1024]; 31 assert_eq!(2, read(rd, &mut buf).unwrap()); 32 assert_eq!(b"f1", &buf[0..2]); 33 assert_eq!(7, offset); 34 35 close(rd).unwrap(); 36 close(wr).unwrap(); 37} 38 39#[cfg(target_os = "linux")] 40#[test] 41fn test_sendfile64_linux() { 42 const CONTENTS: &[u8] = b"abcdef123456"; 43 let mut tmp = tempfile().unwrap(); 44 tmp.write_all(CONTENTS).unwrap(); 45 46 let (rd, wr) = pipe().unwrap(); 47 let mut offset: libc::off64_t = 5; 48 let res = sendfile64(wr, tmp.as_raw_fd(), Some(&mut offset), 2).unwrap(); 49 50 assert_eq!(2, res); 51 52 let mut buf = [0u8; 1024]; 53 assert_eq!(2, read(rd, &mut buf).unwrap()); 54 assert_eq!(b"f1", &buf[0..2]); 55 assert_eq!(7, offset); 56 57 close(rd).unwrap(); 58 close(wr).unwrap(); 59} 60 61#[cfg(target_os = "freebsd")] 62#[test] 63fn test_sendfile_freebsd() { 64 // Declare the content 65 let header_strings = 66 vec!["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"]; 67 let body = "Xabcdef123456"; 68 let body_offset = 1; 69 let trailer_strings = vec!["\n", "Served by Make Believe\n"]; 70 71 // Write the body to a file 72 let mut tmp = tempfile().unwrap(); 73 tmp.write_all(body.as_bytes()).unwrap(); 74 75 // Prepare headers and trailers for sendfile 76 let headers: Vec<&[u8]> = 77 header_strings.iter().map(|s| s.as_bytes()).collect(); 78 let trailers: Vec<&[u8]> = 79 trailer_strings.iter().map(|s| s.as_bytes()).collect(); 80 81 // Prepare socket pair 82 let (mut rd, wr) = UnixStream::pair().unwrap(); 83 84 // Call the test method 85 let (res, bytes_written) = sendfile( 86 tmp.as_raw_fd(), 87 wr.as_raw_fd(), 88 body_offset as off_t, 89 None, 90 Some(headers.as_slice()), 91 Some(trailers.as_slice()), 92 SfFlags::empty(), 93 0, 94 ); 95 assert!(res.is_ok()); 96 wr.shutdown(Shutdown::Both).unwrap(); 97 98 // Prepare the expected result 99 let expected_string = header_strings.concat() 100 + &body[body_offset..] 101 + &trailer_strings.concat(); 102 103 // Verify the message that was sent 104 assert_eq!(bytes_written as usize, expected_string.as_bytes().len()); 105 106 let mut read_string = String::new(); 107 let bytes_read = rd.read_to_string(&mut read_string).unwrap(); 108 assert_eq!(bytes_written as usize, bytes_read); 109 assert_eq!(expected_string, read_string); 110} 111 112#[cfg(target_os = "dragonfly")] 113#[test] 114fn test_sendfile_dragonfly() { 115 // Declare the content 116 let header_strings = 117 vec!["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"]; 118 let body = "Xabcdef123456"; 119 let body_offset = 1; 120 let trailer_strings = vec!["\n", "Served by Make Believe\n"]; 121 122 // Write the body to a file 123 let mut tmp = tempfile().unwrap(); 124 tmp.write_all(body.as_bytes()).unwrap(); 125 126 // Prepare headers and trailers for sendfile 127 let headers: Vec<&[u8]> = 128 header_strings.iter().map(|s| s.as_bytes()).collect(); 129 let trailers: Vec<&[u8]> = 130 trailer_strings.iter().map(|s| s.as_bytes()).collect(); 131 132 // Prepare socket pair 133 let (mut rd, wr) = UnixStream::pair().unwrap(); 134 135 // Call the test method 136 let (res, bytes_written) = sendfile( 137 tmp.as_raw_fd(), 138 wr.as_raw_fd(), 139 body_offset as off_t, 140 None, 141 Some(headers.as_slice()), 142 Some(trailers.as_slice()), 143 ); 144 assert!(res.is_ok()); 145 wr.shutdown(Shutdown::Both).unwrap(); 146 147 // Prepare the expected result 148 let expected_string = header_strings.concat() 149 + &body[body_offset..] 150 + &trailer_strings.concat(); 151 152 // Verify the message that was sent 153 assert_eq!(bytes_written as usize, expected_string.as_bytes().len()); 154 155 let mut read_string = String::new(); 156 let bytes_read = rd.read_to_string(&mut read_string).unwrap(); 157 assert_eq!(bytes_written as usize, bytes_read); 158 assert_eq!(expected_string, read_string); 159} 160 161#[cfg(any(target_os = "ios", target_os = "macos"))] 162#[test] 163fn test_sendfile_darwin() { 164 // Declare the content 165 let header_strings = 166 vec!["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"]; 167 let body = "Xabcdef123456"; 168 let body_offset = 1; 169 let trailer_strings = vec!["\n", "Served by Make Believe\n"]; 170 171 // Write the body to a file 172 let mut tmp = tempfile().unwrap(); 173 tmp.write_all(body.as_bytes()).unwrap(); 174 175 // Prepare headers and trailers for sendfile 176 let headers: Vec<&[u8]> = 177 header_strings.iter().map(|s| s.as_bytes()).collect(); 178 let trailers: Vec<&[u8]> = 179 trailer_strings.iter().map(|s| s.as_bytes()).collect(); 180 181 // Prepare socket pair 182 let (mut rd, wr) = UnixStream::pair().unwrap(); 183 184 // Call the test method 185 let (res, bytes_written) = sendfile( 186 tmp.as_raw_fd(), 187 wr.as_raw_fd(), 188 body_offset as off_t, 189 None, 190 Some(headers.as_slice()), 191 Some(trailers.as_slice()), 192 ); 193 assert!(res.is_ok()); 194 wr.shutdown(Shutdown::Both).unwrap(); 195 196 // Prepare the expected result 197 let expected_string = header_strings.concat() 198 + &body[body_offset..] 199 + &trailer_strings.concat(); 200 201 // Verify the message that was sent 202 assert_eq!(bytes_written as usize, expected_string.as_bytes().len()); 203 204 let mut read_string = String::new(); 205 let bytes_read = rd.read_to_string(&mut read_string).unwrap(); 206 assert_eq!(bytes_written as usize, bytes_read); 207 assert_eq!(expected_string, read_string); 208} 209