• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ensc / r-tftpd / 6070696104

03 Sep 2023 05:09PM UTC coverage: 70.111% (-0.2%) from 70.34%
6070696104

push

github

ensc
test: add more tests

9 of 9 new or added lines in 1 file covered. (100.0%)

1710 of 2439 relevant lines covered (70.11%)

382.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

72.46
/src/test/httpd.rs
1
use std::env;
2
use std::fs::File;
3
use std::io::Write;
4
use std::net::Ipv4Addr;
5
use std::os::fd::{AsRawFd, RawFd};
6
use std::os::unix::process::CommandExt;
7
use std::path::Path;
8
use std::process::{Command, Stdio, Child};
9
use std::time::Duration;
10

11
use tempfile::TempDir;
12

13
pub struct Server {
14
    process:        Option<Child>,
15
    _tmpdir:        TempDir,
16
    host:        String,
17
}
18

19
const LIGHTTP_PROG: &str = match option_env!("LIGHTTP_PROG") {
20
    Some(e)        => e,
21
    None        => "lighttpd",
22
};
23

24
// TODO: we have to set LISTEN_PID to the pid after 'fork()'.  But pre_exec()
25
// does not seem to support updating the environment.  Call 'lighttpd' through
26
// a wrapper
27
const LIGHTTP_WRAP: &str = r#"#!/bin/sh
28
export LISTEN_PID=$$
29
exec @LIGHTTP@ "$@"
30
"#;
31

32
const LIGHTTP_CONF: &str = r#"
33
var.conf_dir = "@TMPDIR@"
34
var.state_dir = "@TMPDIR@"
35
var.log_root = "@TMPDIR@"
36
var.cache_dur = "@TMPDIR@"
37
var.server_root = "@TMPDIR@"
38

39
server.document-root = "@DATADIR@"
40
server.use-ipv6 = "disable"
41

42
server.modules += ( "mod_deflate" )
43
deflate.allowed-encodings = ("brotli", "gzip", "deflate")
44

45
server.modules += ( "mod_expire" )
46

47
server.systemd-socket-activation = "enable"
48
"#;
49

50
impl Server {
51
    pub fn create(dir: &Path) -> Option<Self>
2✔
52
    {
53
        match cfg!(feature = "proxy") {
2✔
54
            true        => Some(Self::create_lighttpd(dir)),
2✔
55
            false        => None,
×
56
        }
57
    }
2✔
58

59
    unsafe fn remove_cloexec(fd: RawFd) -> nix::libc::c_int {
×
60
        use nix::libc as libc;
61
        use libc::FD_CLOEXEC;
62

63
        match libc::fcntl(fd, libc::F_GETFD) {
×
64
            e if e < 0                        => e,
×
65
            f if f & FD_CLOEXEC == 0        => 0,
×
66
            f                                => libc::fcntl(fd, libc::F_SETFD, f & !FD_CLOEXEC),
×
67
        }
68
    }
×
69

70
    unsafe fn dup_nocloexec(old_fd: RawFd, new_fd: RawFd) -> std::io::Result<()> {
×
71
        // TODO: this happens only occasionally and causes randomness in our
72
        // code coverage tests :(
73
        let rc = match old_fd == new_fd {
×
74
            false        => nix::libc::dup3(old_fd, new_fd, 0),
×
75
            true        => Self::remove_cloexec(old_fd),
×
76
        };
77

78
        if rc < 0 {
×
79
            return Err(std::io::Error::last_os_error());
×
80
        }
81

82
        Ok(())
×
83
    }
×
84

85
    fn create_lighttpd(dir: &Path) -> Self
2✔
86
    {
87
        let addr = std::net::SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 0);
2✔
88
        let sock = std::net::TcpListener::bind(addr).unwrap();
2✔
89
        let host = format!("localhost:{}", sock.local_addr().unwrap().port());
2✔
90

91
        let tmpdir = TempDir::new().unwrap();
2✔
92

93
        let conf = tmpdir.path().join("httpd.conf");
2✔
94
        let prog = tmpdir.path().join("lighttp-wrap");
2✔
95

96
        File::create(&prog).unwrap()
4✔
97
            .write_all(LIGHTTP_WRAP
2✔
98
                       .replace("@LIGHTTP@", LIGHTTP_PROG)
99
                       .as_bytes())
100
            .unwrap();
2✔
101

102
        File::create(&conf).unwrap()
4✔
103
            .write_all(LIGHTTP_CONF
4✔
104
                       .replace("@TMPDIR@", tmpdir.path().to_str().unwrap())
2✔
105
                       .replace("@DATADIR@", dir.to_str().unwrap())
2✔
106
                       .as_bytes())
107
            .unwrap();
2✔
108

109
        let mut proc = Command::new("/bin/sh");
2✔
110

111
        proc
12✔
112
            .arg(prog)
2✔
113
            .arg("-D")
114
            .arg("-i30")
115
            .arg("-f")
116
            .arg(conf)
2✔
117
            .current_dir("/")
118
            .env("PATH",
119
                 "/sbin:/usr/sbin:/usr/local/sbin:".to_owned() + &env::var("PATH").unwrap_or("/bin".to_string()))
2✔
120
            .env("LISTEN_FDS", "1")
121
            .stdin(Stdio::null())
2✔
122
            .stdout(Stdio::inherit())
2✔
123
            .stderr(Stdio::inherit());
4✔
124

125
        let sock_fd = sock.as_raw_fd();
2✔
126

127
        unsafe {
128
            proc.pre_exec(move || {
2✔
129
                Self::dup_nocloexec(sock_fd, 3)
×
130
            })
×
131
        };
132

133
        let child = proc.spawn().unwrap();
2✔
134

135
        Self {
2✔
136
            process:        Some(child),
2✔
137
            _tmpdir:        tmpdir,
2✔
138
            host:        host,
139
        }
140
    }
2✔
141

142
    pub fn get_host(&self) -> Option<&str> {
42✔
143
        Some(&self.host)
42✔
144
    }
42✔
145

146
    pub fn wait_for_ready(&mut self) {
2✔
147
        std::thread::sleep(Duration::from_millis(500));
2✔
148

149
        match self.process.take() {
2✔
150
            None                => panic!("no process"),
×
151
            Some(mut proc)        => match proc.try_wait() {
2✔
152
                Ok(None)        => self.process = Some(proc),
2✔
153
                res                => panic!("lighttpd exited: {res:?}"),
×
154
            }
2✔
155
        }
156
    }
2✔
157
}
158

159
impl std::ops::Drop for Server {
160
    fn drop(&mut self) {
2✔
161
        if let Some(mut proc) = self.process.take() {
2✔
162
            let _ = proc.kill();
2✔
163
            let _ = proc.wait();
2✔
164
        }
2✔
165
    }
2✔
166
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc