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