Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit addd8fc

Browse files
committed
Add test for behavior when limits is hit for fd:s or threads
Right now, when the limit on threads or file-descriptors are hit, the server does not typically recover. Incoming-requests returns EndOfStream, panic poisons a Mutex, and recovery is unclear.
1 parent 212b1c4 commit addd8fc

File tree

2 files changed

+109
-0
lines changed

2 files changed

+109
-0
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ zeroize = { version = "1", optional = true }
3131
rustc-serialize = "0.3"
3232
sha1 = "0.6.0"
3333
fdlimit = "0.1"
34+
libc = "0.2"
3435

3536
[package.metadata.docs.rs]
3637
# Enable just one SSL implementation

tests/limits.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
extern crate tiny_http;
2+
3+
use std::io::{Read, Write};
4+
use std::net::{SocketAddr, TcpListener, TcpStream};
5+
6+
use libc::{__rlimit_resource_t, getrlimit, rlimit, setrlimit, RLIMIT_NOFILE, RLIMIT_NPROC};
7+
8+
fn set_limit(limit: __rlimit_resource_t, value: u64) {
9+
let mut current = rlimit {
10+
rlim_cur: 0,
11+
rlim_max: 0,
12+
};
13+
assert_eq!(0, unsafe { getrlimit(limit, &mut current) });
14+
current.rlim_cur = value;
15+
assert_eq!(0, unsafe { setrlimit(limit, &mut current) });
16+
}
17+
18+
struct ServerProcess {
19+
pid: libc::pid_t,
20+
}
21+
22+
impl ServerProcess {
23+
fn start(setup: impl FnOnce()) -> (std::net::SocketAddr, ServerProcess) {
24+
let listener = TcpListener::bind("0.0.0.0:0").unwrap();
25+
let pid = unsafe { libc::fork() };
26+
if pid == 0 {
27+
setup();
28+
let server = tiny_http::Server::from_listener(listener, None).unwrap();
29+
for req in server.incoming_requests() {
30+
req.respond(tiny_http::Response::empty(204)).unwrap();
31+
}
32+
std::process::exit(0);
33+
} else {
34+
let addr = listener.local_addr().unwrap();
35+
drop(listener);
36+
(addr, Self { pid })
37+
}
38+
}
39+
}
40+
41+
impl Drop for ServerProcess {
42+
fn drop(&mut self) {
43+
unsafe {
44+
libc::kill(self.pid, libc::SIGKILL);
45+
libc::waitpid(self.pid, std::ptr::null_mut(), 0);
46+
}
47+
}
48+
}
49+
50+
fn make_request_with_keep_alive(addr: std::net::SocketAddr) -> std::io::Result<TcpStream> {
51+
TcpStream::connect(addr).and_then(|mut s| {
52+
let mut buf = [0; 1024];
53+
write!(
54+
s,
55+
"GET / HTTP/1.1\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\n"
56+
)?;
57+
s.read(&mut buf)?;
58+
Ok(s)
59+
})
60+
}
61+
62+
#[test]
63+
fn survives_fd_limit_1() {
64+
let (addr, _server) = ServerProcess::start(|| {
65+
// Each connection creates to file-descriptors. Let's test with a limit one off
66+
// from survives_fd_limit_2 to trigger out-of-fd at both places in the code
67+
set_limit(RLIMIT_NOFILE, 8);
68+
});
69+
70+
assert_survives_fd_limit(addr);
71+
}
72+
73+
#[test]
74+
fn survives_fd_limit_2() {
75+
let (addr, _server) = ServerProcess::start(|| {
76+
// Each connection creates to file-descriptors. Let's test with a limit one off
77+
// from survives_fd_limit_1 to trigger out-of-fd at both places in the code
78+
set_limit(RLIMIT_NOFILE, 7);
79+
});
80+
81+
assert_survives_fd_limit(addr);
82+
}
83+
84+
fn assert_survives_fd_limit(addr: SocketAddr) {
85+
let clients: Vec<_> = (0..10)
86+
.map(|_| make_request_with_keep_alive(addr))
87+
.collect();
88+
assert!(clients.iter().any(Result::is_err));
89+
90+
drop(clients);
91+
92+
assert!(TcpStream::connect(addr).is_ok())
93+
}
94+
95+
#[test]
96+
fn survives_proc_limit() {
97+
let (addr, _server) = ServerProcess::start(|| {
98+
set_limit(RLIMIT_NPROC, 7);
99+
});
100+
101+
let clients: Vec<_> = (0..10)
102+
.map(|_| make_request_with_keep_alive(addr))
103+
.collect();
104+
assert!(clients.iter().any(Result::is_err));
105+
drop(clients);
106+
107+
assert!(TcpStream::connect(addr).is_ok())
108+
}

0 commit comments

Comments
 (0)