Skip to content

Commit a33e914

Browse files
committed
fix(cli): Racing on playground webserver port binding
1 parent fb5fbdd commit a33e914

File tree

2 files changed

+21
-20
lines changed

2 files changed

+21
-20
lines changed

cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ rustc-hash = "1"
3535
semver = "1.0"
3636
serde = { version = "1.0.130", features = ["derive"] }
3737
smallbitvec = "2.5.1"
38-
tiny_http = "0.8"
38+
tiny_http = "0.12.0"
3939
walkdir = "2.3"
4040
webbrowser = "0.5.1"
4141
which = "4.1.0"

cli/src/playground.rs

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -45,20 +45,7 @@ fn get_main_html(tree_sitter_dir: &Option<PathBuf>) -> Cow<'static, [u8]> {
4545
}
4646

4747
pub fn serve(grammar_path: &Path, open_in_browser: bool) {
48-
let port = env::var("TREE_SITTER_PLAYGROUND_PORT")
49-
.map(|v| v.parse::<u16>().expect("Invalid port specification"))
50-
.unwrap_or_else(
51-
|_| get_available_port().expect(
52-
"Couldn't find an available port, try providing a port number via the TREE_SITTER_PLAYGROUND_PORT \
53-
environment variable"
54-
)
55-
);
56-
let addr = format!(
57-
"{}:{}",
58-
env::var("TREE_SITTER_PLAYGROUND_ADDR").unwrap_or("127.0.0.1".to_owned()),
59-
port
60-
);
61-
let server = Server::http(&addr).expect("Failed to start web server");
48+
let server = get_server();
6249
let grammar_name = wasm::get_grammar_name(&grammar_path.join("src"))
6350
.with_context(|| "Failed to get wasm filename")
6451
.unwrap();
@@ -71,7 +58,7 @@ pub fn serve(grammar_path: &Path, open_in_browser: bool) {
7158
)
7259
})
7360
.unwrap();
74-
let url = format!("http://{}", addr);
61+
let url = format!("http://{}", server.server_addr());
7562
println!("Started playground on: {}", url);
7663
if open_in_browser {
7764
if let Err(_) = webbrowser::open(&url) {
@@ -135,10 +122,24 @@ fn response<'a>(data: &'a [u8], header: &Header) -> Response<&'a [u8]> {
135122
.with_header(header.clone())
136123
}
137124

138-
fn get_available_port() -> Option<u16> {
139-
(8000..12000).find(port_is_available)
125+
fn get_server() -> Server {
126+
let addr = env::var("TREE_SITTER_PLAYGROUND_ADDR").unwrap_or("127.0.0.1".to_owned());
127+
let port = env::var("TREE_SITTER_PLAYGROUND_PORT")
128+
.map(|v| v.parse::<u16>().expect("Invalid port specification"))
129+
.ok();
130+
let listener = match port {
131+
Some(port) => bind_to(&*addr, port).expect("Can't bind to the specified port"),
132+
None => {
133+
get_listener_on_available_port(&*addr).expect("Can't find a free port to bind to it")
134+
}
135+
};
136+
Server::from_listener(listener, None).expect("Failed to start web server")
137+
}
138+
139+
fn get_listener_on_available_port(addr: &str) -> Option<TcpListener> {
140+
(8000..12000).find_map(|port| bind_to(addr, port))
140141
}
141142

142-
fn port_is_available(port: &u16) -> bool {
143-
TcpListener::bind(("127.0.0.1", *port)).is_ok()
143+
fn bind_to(addr: &str, port: u16) -> Option<TcpListener> {
144+
TcpListener::bind(format!("{addr}:{port}")).ok()
144145
}

0 commit comments

Comments
 (0)