server_starter_listener/
lib.rs1#[macro_use]
31extern crate failure;
32#[macro_use]
33extern crate lazy_static;
34
35use std::net::TcpListener;
36use std::os::unix::io::{FromRawFd, RawFd};
37use std::os::unix::net::UnixListener;
38
39use regex::Regex;
40
41const SERVER_STARTER_PORT_ENV: &str = "SERVER_STARTER_PORT";
42
43lazy_static! {
44 static ref HOST_PORT_REGEX: Regex = Regex::new("^[^:]+:\\d+$").unwrap();
45 static ref PORT_REGEX: Regex = Regex::new("^\\d+$").unwrap();
46}
47
48#[derive(Debug)]
52pub enum ServerStarterListener {
53 Tcp(TcpListener),
54 Uds(UnixListener),
55}
56
57impl ServerStarterListener {
58 fn tcp(fd: RawFd) -> ServerStarterListener {
59 ServerStarterListener::Tcp(unsafe { TcpListener::from_raw_fd(fd) })
60 }
61
62 fn uds(fd: RawFd) -> ServerStarterListener {
63 ServerStarterListener::Uds(unsafe { UnixListener::from_raw_fd(fd) })
64 }
65}
66
67#[derive(Fail, Debug)]
71pub enum ListenerError {
72 #[fail(display = "server starter port env var not found.")]
73 ServerStarterPortEnvNotFound,
74 #[fail(display = "cannot parse server starter port: {}", _0)]
75 InvalidServerStarterPortSpec(String),
76}
77
78pub fn listeners() -> Result<Vec<ServerStarterListener>, ListenerError> {
88 let specs = match std::env::var(SERVER_STARTER_PORT_ENV) {
89 Ok(specs) => specs,
90 Err(_) => return Err(ListenerError::ServerStarterPortEnvNotFound),
91 };
92
93 let specs: Vec<&str> = specs.split(';').collect();
94 let mut results = vec![];
95 for spec in specs {
96 let pair: Vec<&str> = spec.split('=').collect();
97 if pair.len() != 2 {
98 return Err(ListenerError::InvalidServerStarterPortSpec(spec.into()));
99 }
100
101 let (left, fd) = (pair[0], pair[1]);
102 let fd: i32 = match fd.parse() {
103 Ok(fd) => fd,
104 Err(_) => return Err(ListenerError::InvalidServerStarterPortSpec(spec.into())),
105 };
106
107 let listener = if HOST_PORT_REGEX.find(left).is_some() || PORT_REGEX.find(left).is_some() {
108 ServerStarterListener::tcp(fd)
109 } else {
110 ServerStarterListener::uds(fd)
111 };
112 results.push(listener)
113 }
114 Ok(results)
115}
116
117#[cfg(test)]
118mod tests {
119 use std::os::unix::io::AsRawFd;
120
121 use crate::{listeners, ServerStarterListener};
122
123 #[test]
124 fn listeners_tcp() {
125 let assert_tcp_listener = |var, fd| {
126 std::env::set_var("SERVER_STARTER_PORT", var);
127 let results = listeners();
128 match results {
129 Ok(results) => {
130 assert_eq!(1, results.len());
131 let listener = results.first().unwrap();
132 match listener {
133 ServerStarterListener::Tcp(tcp_listener) => {
134 assert_eq!(fd, tcp_listener.as_raw_fd());
135 }
136 ServerStarterListener::Uds(_) => {
137 assert!(false, "not tcp listener {:?}", listener)
138 }
139 }
140 }
141 Err(_) => assert!(false, "results not ok {:?}", results),
142 }
143 };
144
145 assert_tcp_listener("80=2", 2);
146 assert_tcp_listener("127.0.0.1:8080=3", 3);
147 assert_tcp_listener("localhost:8080=4", 4);
148 }
149
150 #[test]
151 fn listeners_uds() {
152 let assert_uds_listener = |var, fd| {
153 std::env::set_var("SERVER_STARTER_PORT", var);
154 let results = listeners();
155 match results {
156 Ok(results) => {
157 assert_eq!(1, results.len());
158 let listener = results.first().unwrap();
159 match listener {
160 ServerStarterListener::Tcp(_) => {
161 assert!(false, "not uds listener {:?}", listener)
162 }
163 ServerStarterListener::Uds(uds_listener) => {
164 assert_eq!(fd, uds_listener.as_raw_fd());
165 }
166 }
167 }
168 Err(_) => assert!(false, "results not ok {:?}", results),
169 }
170 };
171
172 assert_uds_listener("/tmp/server-starter-listener/server.sock=2", 2);
173 }
174
175 #[test]
176 fn listeners_without_env() {
177 std::env::remove_var("SERVER_STARTER_PORT");
178 assert!(listeners().is_err());
179 }
180
181 #[test]
182 fn listeners_invalid_env() {
183 std::env::set_var("SERVER_STARTER_PORT", "80=a");
184 assert!(listeners().is_err());
185 }
186}