This repository has been archived by the owner on Jun 14, 2021. It is now read-only.
generated from josephjkahn/quantum_god2021
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tail.rs
132 lines (111 loc) · 4.18 KB
/
tail.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use std::net::{SocketAddr, TcpListener};
use anyhow::Result;
use crate::settings::global_user::GlobalUser;
use crate::settings::toml::Target;
use crate::tail::Tail;
const DEFAULT_TUNNEL_PORT: u16 = 8080;
const DEFAULT_METRICS_PORT: u16 = 8081;
pub fn start(
target: &Target,
user: &GlobalUser,
format: String,
tunnel_port: Option<u16>,
metrics_port: Option<u16>,
verbose: bool,
) -> Result<()> {
let tunnel_port = find_open_port(tunnel_port, DEFAULT_TUNNEL_PORT)?;
let metrics_port = find_open_port(metrics_port, DEFAULT_METRICS_PORT)?;
Tail::run(
target.clone(),
user.clone(),
format,
tunnel_port,
metrics_port,
verbose,
)
}
/// Find open port takes two arguments: an Optional requested port, and a default port.
fn find_open_port(requested: Option<u16>, default: u16) -> Result<u16> {
if let Some(port) = requested {
let addr = format!("127.0.0.1:{}", port);
match TcpListener::bind(addr) {
Ok(socket) => Ok(socket.local_addr()?.port()),
Err(_) => anyhow::bail!("the specified port {} is unavailable", port),
}
} else {
// try to use the default port; else get an ephemeral port from the OS
let addrs = [
SocketAddr::from(([127, 0, 0, 1], default)),
// Binding to port 0 effectively asks the OS to provide the next available
// ephemeral port: https://www.grc.com/port_0.htm
SocketAddr::from(([127, 0, 0, 1], 0)),
];
let socket = TcpListener::bind(&addrs[..])?;
Ok(socket.local_addr()?.port())
}
}
#[cfg(test)]
mod tests {
// These tests are extremely stateful; since what we are testing is how this function behaves
// when requested ports are unavailable, and since our tests run concurrently, each test uses
// unique ports to avoid collisions. There are two possible solutions to this problem:
// 1. require that these tests be run serially, and find a way to clean up bound ports
// 2. use dependency injection and write a substitute for the TcpListener::bind fn.
use super::*;
#[test]
fn test_find_open_port_no_requested_default_available() {
let requested = None;
let default = 3000;
let port = find_open_port(requested, default).unwrap();
// returns default
assert_eq!(port, default);
}
#[test]
fn test_find_open_port_no_requested_default_unavailable() {
let requested = None;
let default = 3001;
let listener = find_open_port(requested, default);
// returns random
assert!(listener.is_ok());
}
#[test]
fn test_find_open_port_requested_available_default_available() {
let requested = 3002;
let default = 3003;
let port = find_open_port(Some(requested), default).unwrap();
// returns requested
assert_eq!(port, requested);
}
#[test]
fn test_find_open_port_requested_available_default_unavailable() {
let requested = 3004;
let default = 3005;
let _default_listener =
TcpListener::bind(SocketAddr::from(([127, 0, 0, 1], default))).unwrap();
let port = find_open_port(Some(requested), default).unwrap();
// returns requested
assert_eq!(port, requested);
}
#[test]
fn test_find_open_port_requested_unavailable_default_available() {
let requested = 3006;
let default = 3007;
let _requested_listener =
TcpListener::bind(SocketAddr::from(([127, 0, 0, 1], requested))).unwrap();
let listener = find_open_port(Some(requested), default);
// throws error
assert!(listener.is_err());
}
#[test]
fn test_find_open_port_requested_unavailable_default_unavailable() {
let requested = 3008;
let default = 3009;
let _requested_listener =
TcpListener::bind(SocketAddr::from(([127, 0, 0, 1], requested))).unwrap();
let _default_listener =
TcpListener::bind(SocketAddr::from(([127, 0, 0, 1], default))).unwrap();
let listener = find_open_port(Some(requested), default);
// throws error
assert!(listener.is_err());
}
}