upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/tests/common/git_server.rs
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2026-01-09 23:00:59 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2026-01-09 23:00:59 +0000
commitbe1f21aa1ec9d8666f96005ee203413441e6d220 (patch)
tree636c85326cec99a9eb93c2798b106ff47f82b3c9 /tests/common/git_server.rs
parent615bfa0e3e892a22f1691a6a1172ea755a7c3149 (diff)
fix: eliminate port binding race condition in SimpleGitServer
SimpleGitServer had a TOCTOU race where find_free_port() would bind a port, immediately release it, then the caller would try to bind it - allowing another process to grab the port in between. This caused intermittent test failures. Changed to bind the port once and keep it bound while converting from std to tokio listener, matching the pattern already used in SmartGitServer. Deleted the now-unused find_free_port() helper function.
Diffstat (limited to 'tests/common/git_server.rs')
-rw-r--r--tests/common/git_server.rs33
1 files changed, 14 insertions, 19 deletions
diff --git a/tests/common/git_server.rs b/tests/common/git_server.rs
index 9fb62df..3190901 100644
--- a/tests/common/git_server.rs
+++ b/tests/common/git_server.rs
@@ -128,18 +128,26 @@ impl SimpleGitServer {
128 ); 128 );
129 } 129 }
130 130
131 // 4. Find a free port 131 // 4. Create and bind listener (eliminates port race condition)
132 let port = find_free_port(); 132 let std_listener =
133 let addr: SocketAddr = ([127, 0, 0, 1], port).into(); 133 std::net::TcpListener::bind("127.0.0.1:0").expect("Failed to bind to random port");
134 let port = std_listener
135 .local_addr()
136 .expect("Failed to get local addr")
137 .port();
138
139 // Convert to tokio listener (keeps port bound)
140 std_listener
141 .set_nonblocking(true)
142 .expect("Failed to set non-blocking");
143 let listener =
144 TcpListener::from_std(std_listener).expect("Failed to convert to tokio listener");
134 145
135 // 5. Create shutdown channel 146 // 5. Create shutdown channel
136 let (shutdown_tx, mut shutdown_rx) = oneshot::channel::<()>(); 147 let (shutdown_tx, mut shutdown_rx) = oneshot::channel::<()>();
137 148
138 // 6. Start the HTTP server 149 // 6. Start the HTTP server
139 let repo_path = Arc::new(bare_repo_path); 150 let repo_path = Arc::new(bare_repo_path);
140 let listener = TcpListener::bind(addr)
141 .await
142 .expect("Failed to bind to address");
143 151
144 let handle = tokio::spawn(async move { 152 let handle = tokio::spawn(async move {
145 println!("[SmartGitServer] Server loop started on port {}", port); 153 println!("[SmartGitServer] Server loop started on port {}", port);
@@ -296,19 +304,6 @@ fn guess_content_type(path: &Path) -> &'static str {
296 } 304 }
297} 305}
298 306
299/// Find a free port to use for the server.
300fn find_free_port() -> u16 {
301 use std::net::TcpListener;
302
303 let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind to random port");
304 let port = listener
305 .local_addr()
306 .expect("Failed to get local addr")
307 .port();
308 drop(listener);
309 port
310}
311
312/// Wait for the server to be ready to accept connections. 307/// Wait for the server to be ready to accept connections.
313async fn wait_for_server_ready(port: u16) { 308async fn wait_for_server_ready(port: u16) {
314 let max_attempts = 50; // 5 seconds total 309 let max_attempts = 50; // 5 seconds total