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