From a33e9141a8e0e3eeb08aeae228bb8b0214ea7d0b Mon Sep 17 00:00:00 2001 From: Andrew Hlynskyi Date: Wed, 9 Nov 2022 04:55:52 +0200 Subject: [PATCH] fix(cli): Racing on playground webserver port binding --- cli/Cargo.toml | 2 +- cli/src/playground.rs | 39 ++++++++++++++++++++------------------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 3c2bd171..5403075d 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -35,7 +35,7 @@ rustc-hash = "1" semver = "1.0" serde = { version = "1.0.130", features = ["derive"] } smallbitvec = "2.5.1" -tiny_http = "0.8" +tiny_http = "0.12.0" walkdir = "2.3" webbrowser = "0.5.1" which = "4.1.0" diff --git a/cli/src/playground.rs b/cli/src/playground.rs index be804b50..a2dbef96 100644 --- a/cli/src/playground.rs +++ b/cli/src/playground.rs @@ -45,20 +45,7 @@ fn get_main_html(tree_sitter_dir: &Option) -> Cow<'static, [u8]> { } pub fn serve(grammar_path: &Path, open_in_browser: bool) { - let port = env::var("TREE_SITTER_PLAYGROUND_PORT") - .map(|v| v.parse::().expect("Invalid port specification")) - .unwrap_or_else( - |_| get_available_port().expect( - "Couldn't find an available port, try providing a port number via the TREE_SITTER_PLAYGROUND_PORT \ - environment variable" - ) - ); - let addr = format!( - "{}:{}", - env::var("TREE_SITTER_PLAYGROUND_ADDR").unwrap_or("127.0.0.1".to_owned()), - port - ); - let server = Server::http(&addr).expect("Failed to start web server"); + let server = get_server(); let grammar_name = wasm::get_grammar_name(&grammar_path.join("src")) .with_context(|| "Failed to get wasm filename") .unwrap(); @@ -71,7 +58,7 @@ pub fn serve(grammar_path: &Path, open_in_browser: bool) { ) }) .unwrap(); - let url = format!("http://{}", addr); + let url = format!("http://{}", server.server_addr()); println!("Started playground on: {}", url); if open_in_browser { if let Err(_) = webbrowser::open(&url) { @@ -135,10 +122,24 @@ fn response<'a>(data: &'a [u8], header: &Header) -> Response<&'a [u8]> { .with_header(header.clone()) } -fn get_available_port() -> Option { - (8000..12000).find(port_is_available) +fn get_server() -> Server { + let addr = env::var("TREE_SITTER_PLAYGROUND_ADDR").unwrap_or("127.0.0.1".to_owned()); + let port = env::var("TREE_SITTER_PLAYGROUND_PORT") + .map(|v| v.parse::().expect("Invalid port specification")) + .ok(); + let listener = match port { + Some(port) => bind_to(&*addr, port).expect("Can't bind to the specified port"), + None => { + get_listener_on_available_port(&*addr).expect("Can't find a free port to bind to it") + } + }; + Server::from_listener(listener, None).expect("Failed to start web server") } -fn port_is_available(port: &u16) -> bool { - TcpListener::bind(("127.0.0.1", *port)).is_ok() +fn get_listener_on_available_port(addr: &str) -> Option { + (8000..12000).find_map(|port| bind_to(addr, port)) +} + +fn bind_to(addr: &str, port: u16) -> Option { + TcpListener::bind(format!("{addr}:{port}")).ok() }