1 use std::io::prelude::*;
2 use std::os::unix::prelude::*;
3 
4 use libc::off_t;
5 use nix::sys::sendfile::*;
6 use tempfile::tempfile;
7 
8 cfg_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 = "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]
test_sendfile_linux()19 fn 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 = "freebsd")]
40 #[test]
test_sendfile_freebsd()41 fn test_sendfile_freebsd() {
42     // Declare the content
43     let header_strings = vec!["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"];
44     let body = "Xabcdef123456";
45     let body_offset = 1;
46     let trailer_strings = vec!["\n", "Served by Make Believe\n"];
47 
48     // Write the body to a file
49     let mut tmp = tempfile().unwrap();
50     tmp.write_all(body.as_bytes()).unwrap();
51 
52     // Prepare headers and trailers for sendfile
53     let headers: Vec<&[u8]> = header_strings.iter().map(|s| s.as_bytes()).collect();
54     let trailers: Vec<&[u8]> = trailer_strings.iter().map(|s| s.as_bytes()).collect();
55 
56     // Prepare socket pair
57     let (mut rd, wr) = UnixStream::pair().unwrap();
58 
59     // Call the test method
60     let (res, bytes_written) = sendfile(
61         tmp.as_raw_fd(),
62         wr.as_raw_fd(),
63         body_offset as off_t,
64         None,
65         Some(headers.as_slice()),
66         Some(trailers.as_slice()),
67         SfFlags::empty(),
68         0,
69     );
70     assert!(res.is_ok());
71     wr.shutdown(Shutdown::Both).unwrap();
72 
73     // Prepare the expected result
74     let expected_string =
75         header_strings.concat() + &body[body_offset..] + &trailer_strings.concat();
76 
77     // Verify the message that was sent
78     assert_eq!(bytes_written as usize, expected_string.as_bytes().len());
79 
80     let mut read_string = String::new();
81     let bytes_read = rd.read_to_string(&mut read_string).unwrap();
82     assert_eq!(bytes_written as usize, bytes_read);
83     assert_eq!(expected_string, read_string);
84 }
85 
86 #[cfg(any(target_os = "ios", target_os = "macos"))]
87 #[test]
test_sendfile_darwin()88 fn test_sendfile_darwin() {
89     // Declare the content
90     let header_strings = vec!["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"];
91     let body = "Xabcdef123456";
92     let body_offset = 1;
93     let trailer_strings = vec!["\n", "Served by Make Believe\n"];
94 
95     // Write the body to a file
96     let mut tmp = tempfile().unwrap();
97     tmp.write_all(body.as_bytes()).unwrap();
98 
99     // Prepare headers and trailers for sendfile
100     let headers: Vec<&[u8]> = header_strings.iter().map(|s| s.as_bytes()).collect();
101     let trailers: Vec<&[u8]> = trailer_strings.iter().map(|s| s.as_bytes()).collect();
102 
103     // Prepare socket pair
104     let (mut rd, wr) = UnixStream::pair().unwrap();
105 
106     // Call the test method
107     let (res, bytes_written) = sendfile(
108         tmp.as_raw_fd(),
109         wr.as_raw_fd(),
110         body_offset as off_t,
111         None,
112         Some(headers.as_slice()),
113         Some(trailers.as_slice()),
114     );
115     assert!(res.is_ok());
116     wr.shutdown(Shutdown::Both).unwrap();
117 
118     // Prepare the expected result
119     let expected_string =
120         header_strings.concat() + &body[body_offset..] + &trailer_strings.concat();
121 
122     // Verify the message that was sent
123     assert_eq!(bytes_written as usize, expected_string.as_bytes().len());
124 
125     let mut read_string = String::new();
126     let bytes_read = rd.read_to_string(&mut read_string).unwrap();
127     assert_eq!(bytes_written as usize, bytes_read);
128     assert_eq!(expected_string, read_string);
129 }
130