1 | use std::io::prelude::*; |
2 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
3 | use std::os::unix::io::{FromRawFd, OwnedFd}; |
4 | |
5 | use libc::off_t; |
6 | use nix::sys::sendfile::*; |
7 | use tempfile::tempfile; |
8 | |
9 | cfg_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] |
20 | fn 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] |
46 | fn 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] |
72 | fn 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] |
123 | fn 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] |
172 | fn 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 | |