Skip to main content

session_probe/
session_probe.rs

1//! A probe app for the process-per-session isolation tests
2//! (`tests/isolation_hardening.rs`, `tests/isolation_windows.rs`): not a
3//! docs example.
4//!
5//!     session_probe ADDRESS [--token T] [--startup-sleep MS] [--stdin-stop]
6//!     session_probe --nested ADDRESS
7//!
8//! Keys in the page: `f` starts `sleep 1000` (in the session's process
9//! group; on Windows `ping -n 1000 127.0.0.1`, in the session's job), `n`
10//! runs this program as an unrelated nested server (`--nested
11//! 127.0.0.1:0`) and shows how it ended.
12
13use std::io::BufRead;
14use std::process::{Command, Stdio};
15use std::time::{Duration, Instant};
16
17use rich_web::intuituive::prelude::*;
18
19fn app() -> App {
20    App::new(|| {
21        let pid = std::process::id();
22        let state = signal(String::from("waiting"));
23        column([
24            text!("pid {pid} · {state}").fixed(1),
25            label("f forks · n nested").fixed(1),
26        ])
27        .on_key("f", move |_| {
28            // Left running on purpose: the tests check what ends it.
29            #[allow(clippy::zombie_processes)]
30            let child = long_wait().spawn().unwrap();
31            state.set(format!("FORKED {} END", child.id()));
32        })
33        .on_key("n", move |_| {
34            // A program the app runs, which happens to serve as well.
35            let out = Command::new(std::env::current_exe().unwrap())
36                .args(["--nested", "127.0.0.1:0"])
37                .stdin(Stdio::null())
38                .output()
39                .unwrap();
40            let code = out.status.code().unwrap_or(-1);
41            let bound = String::from_utf8_lossy(&out.stdout).contains("BOUND");
42            state.set(format!("NESTED code={code} bound={bound} END"));
43        })
44    })
45}
46
47/// A program that waits about a thousand seconds.
48#[cfg(not(windows))]
49fn long_wait() -> Command {
50    let mut command = Command::new("sleep");
51    command.arg("1000");
52    command
53}
54
55/// A program that waits about a thousand seconds (Windows has no `sleep`).
56#[cfg(windows)]
57fn long_wait() -> Command {
58    let mut command = Command::new("ping");
59    command
60        .args(["-n", "1000", "127.0.0.1"])
61        .stdout(Stdio::null());
62    command
63}
64
65fn main() -> std::io::Result<()> {
66    let mut args = std::env::args().skip(1);
67    let mut addr = "127.0.0.1:8080".to_string();
68    let mut token = None;
69    let mut startup = None;
70    let mut stdin_stop = false;
71    let mut nested = false;
72    while let Some(arg) = args.next() {
73        let mut value = || args.next().expect("a value after the option");
74        match arg.as_str() {
75            "--token" => token = Some(value()),
76            "--startup-sleep" => startup = Some(value().parse::<u64>().expect("ms")),
77            "--stdin-stop" => stdin_stop = true,
78            "--nested" => nested = true,
79            _ => addr = arg,
80        }
81    }
82    if nested {
83        // An unrelated program that serves an app: it should get the
84        // address it asked for.
85        let server = rich_web::Server::bind(addr.as_str(), app)?;
86        println!("BOUND {}", server.local_addr());
87        return Ok(());
88    }
89    if let (Some(ms), true) = (startup, std::env::var_os(rich_web::SESSION_ENV).is_some()) {
90        std::thread::sleep(Duration::from_millis(ms));
91    }
92    let mut server = rich_web::Server::bind(addr.as_str(), app)?.title("session probe");
93    if let Some(token) = token {
94        server = server.token(token);
95    }
96    if stdin_stop {
97        let handle = server.spawn()?;
98        println!("Serving session probe at {}", handle.url());
99        let mut line = String::new();
100        std::io::stdin().lock().read_line(&mut line)?;
101        let started = Instant::now();
102        handle.stop();
103        println!("STOPPED {}", started.elapsed().as_millis());
104        return Ok(());
105    }
106    server.run()
107}