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