sozu_command_lib/
ready.rs1use std::fmt;
2
3#[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord)]
5pub struct Ready(pub u16);
6
7impl Ready {
8 pub const EMPTY: Ready = Ready(0);
9 pub const READABLE: Ready = Ready(0b00001);
10 pub const WRITABLE: Ready = Ready(0b00010);
11 pub const ERROR: Ready = Ready(0b00100);
12 pub const HUP: Ready = Ready(0b01000);
15 pub const WRITE_CLOSED: Ready = Ready(0b10000);
21 pub const ALL: Ready = Ready(0b00011);
22
23 #[inline]
24 pub fn is_empty(&self) -> bool {
25 self.0 == 0
26 }
27
28 #[inline]
29 pub fn is_readable(&self) -> bool {
30 self.contains(Ready::READABLE)
31 }
32
33 #[inline]
34 pub fn is_writable(&self) -> bool {
35 self.contains(Ready::WRITABLE)
36 }
37
38 pub fn is_error(&self) -> bool {
39 self.contains(Ready::ERROR)
40 }
41
42 pub fn is_hup(&self) -> bool {
43 self.contains(Ready::HUP)
44 }
45
46 pub fn is_write_closed(&self) -> bool {
47 self.contains(Ready::WRITE_CLOSED)
48 }
49
50 #[inline]
51 pub fn insert<T: Into<Self>>(&mut self, other: T) {
52 let other = other.into();
53 self.0 |= other.0;
54 }
55
56 #[inline]
57 pub fn remove<T: Into<Self>>(&mut self, other: T) {
58 let other = other.into();
59 self.0 &= !other.0;
60 }
61
62 #[inline]
63 pub fn contains<T: Into<Self>>(&self, other: T) -> bool {
64 let other = other.into();
65 (*self & other) == other
66 }
67}
68
69use std::ops;
72impl<T: Into<Ready>> ops::BitOr<T> for Ready {
73 type Output = Ready;
74
75 #[inline]
76 fn bitor(self, other: T) -> Ready {
77 Ready(self.0 | other.into().0)
78 }
79}
80
81impl<T: Into<Ready>> ops::BitOrAssign<T> for Ready {
82 #[inline]
83 fn bitor_assign(&mut self, other: T) {
84 self.0 |= other.into().0;
85 }
86}
87
88impl<T: Into<Ready>> ops::BitAnd<T> for Ready {
89 type Output = Ready;
90
91 #[inline]
92 fn bitand(self, other: T) -> Ready {
93 Ready(self.0 & other.into().0)
94 }
95}
96
97impl fmt::Debug for Ready {
98 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
99 let mut one = false;
100 let flags = [
101 (Ready::READABLE, "Readable"),
102 (Ready::WRITABLE, "Writable"),
103 (Ready::ERROR, "Error"),
104 (Ready::HUP, "Hup"),
105 (Ready::WRITE_CLOSED, "WriteClosed"),
106 ];
107
108 for &(flag, msg) in &flags {
109 if self.contains(flag) {
110 if one {
111 write!(fmt, " | ")?
112 }
113 write!(fmt, "{msg}")?;
114
115 one = true
116 }
117 }
118
119 if !one {
120 fmt.write_str("(empty)")?;
121 }
122
123 Ok(())
124 }
125}
126
127impl std::convert::From<mio::Interest> for Ready {
128 fn from(i: mio::Interest) -> Self {
129 let mut r = Ready::EMPTY;
130 if i.is_readable() {
131 r.insert(Ready::READABLE);
132 }
133 if i.is_writable() {
134 r.insert(Ready::WRITABLE);
135 }
136
137 r
138 }
139}
140
141impl std::convert::From<&mio::event::Event> for Ready {
142 fn from(e: &mio::event::Event) -> Self {
143 let mut r = Ready::EMPTY;
144 if e.is_readable() {
145 r.insert(Ready::READABLE);
146 }
147 if e.is_writable() {
148 r.insert(Ready::WRITABLE);
149 }
150 if e.is_error() {
151 r.insert(Ready::ERROR);
152 }
153 if e.is_read_closed() || e.is_write_closed() {
154 r.insert(Ready::HUP);
155 }
156 if e.is_write_closed() {
157 r.insert(Ready::WRITE_CLOSED);
158 }
159
160 r
161 }
162}
163
164#[cfg(all(test, target_os = "linux"))]
166mod tests {
167 use std::{
168 io::{ErrorKind, Write},
169 net::{Shutdown, TcpListener, TcpStream},
170 os::fd::AsRawFd,
171 time::Duration,
172 };
173
174 use mio::{Events, Interest, Poll, Token};
175
176 use super::Ready;
177
178 fn registered_pair() -> (TcpStream, mio::net::TcpStream, Poll, Events) {
181 let listener = TcpListener::bind("127.0.0.1:0").expect("a loopback listener binds");
182 let client =
183 TcpStream::connect(listener.local_addr().expect("the listener has an address"))
184 .expect("loopback connect completes");
185 let (server, _) = listener.accept().expect("the connection is accepted");
186 server
187 .set_nonblocking(true)
188 .expect("mio needs a nonblocking stream");
189 let mut server = mio::net::TcpStream::from_std(server);
190 let poll = Poll::new().expect("a poll opens");
191 poll.registry()
192 .register(
193 &mut server,
194 Token(1),
195 Interest::READABLE | Interest::WRITABLE,
196 )
197 .expect("the stream registers");
198 let mut events = Events::with_capacity(8);
199 let mut poll = poll;
200 poll.poll(&mut events, Some(Duration::from_millis(100)))
201 .expect("the first poll succeeds");
202 (client, server, poll, events)
203 }
204
205 fn reset(client: TcpStream) {
207 let linger = libc::linger {
208 l_onoff: 1,
209 l_linger: 0,
210 };
211 let rc = unsafe {
214 libc::setsockopt(
215 client.as_raw_fd(),
216 libc::SOL_SOCKET,
217 libc::SO_LINGER,
218 &linger as *const _ as *const libc::c_void,
219 std::mem::size_of::<libc::linger>() as libc::socklen_t,
220 )
221 };
222 assert_eq!(rc, 0, "SO_LINGER zero must set");
223 drop(client);
224 }
225
226 fn next_ready(poll: &mut Poll, events: &mut Events) -> Ready {
227 poll.poll(events, Some(Duration::from_secs(2)))
228 .expect("poll succeeds");
229 let event = events
230 .iter()
231 .find(|event| event.token() == Token(1))
232 .expect("the server end gets an event");
233 Ready::from(event)
234 }
235
236 #[test]
238 fn a_half_close_is_hup_but_not_write_closed() {
239 let (client, _server, mut poll, mut events) = registered_pair();
240 client
241 .shutdown(Shutdown::Write)
242 .expect("the client half-closes");
243 let ready = next_ready(&mut poll, &mut events);
244 assert!(ready.is_hup(), "a half-close raises HUP: {ready:?}");
245 assert!(
246 !ready.is_write_closed(),
247 "a half-close is not a hang-up: {ready:?}"
248 );
249 assert!(!ready.is_error(), "a half-close is not an error: {ready:?}");
250 }
251
252 #[test]
254 fn a_reset_is_error_and_write_closed() {
255 let (client, _server, mut poll, mut events) = registered_pair();
256 reset(client);
257 let ready = next_ready(&mut poll, &mut events);
258 assert!(ready.is_error(), "an unread reset is an error: {ready:?}");
259 assert!(ready.is_write_closed(), "a reset is a hang-up: {ready:?}");
260 }
261
262 #[test]
269 fn a_reset_consumed_by_a_write_is_write_closed_without_error() {
270 let (client, mut server, mut poll, mut events) = registered_pair();
271 reset(client);
272 let mut consumed = None;
273 for _ in 0..200 {
274 match server.write_all(b"x") {
275 Ok(_) => std::thread::sleep(Duration::from_millis(5)),
276 Err(e) => {
277 consumed = Some(e.kind());
278 break;
279 }
280 }
281 }
282 assert!(
283 matches!(
284 consumed,
285 Some(ErrorKind::ConnectionReset) | Some(ErrorKind::BrokenPipe)
286 ),
287 "premise: a write consumes the reset, got {consumed:?}"
288 );
289 let ready = next_ready(&mut poll, &mut events);
290 assert!(ready.is_hup(), "the hang-up raises HUP: {ready:?}");
291 assert!(
292 ready.is_write_closed(),
293 "the hang-up must be told from a half-close: {ready:?}"
294 );
295 assert!(
296 !ready.is_error(),
297 "premise: the write consumed the error, so EPOLLERR is gone: {ready:?}"
298 );
299 }
300}