2727import base64
2828import json
2929import shutil
30+ import socket
3031import time
3132from pathlib import Path
3233from urllib .error import HTTPError , URLError
@@ -110,6 +111,32 @@ def reset_server_root(server_root: Path) -> None:
110111 server_root .parent .mkdir (parents = True , exist_ok = True )
111112
112113
114+ def choose_http_port (host : str , requested_port : int ) -> tuple [int , bool ]:
115+ """Return a usable HTTP port for the example.
116+
117+ Tries the requested port first. If it is unavailable, fall back to an
118+ ephemeral free port chosen by the OS.
119+ """
120+
121+ def _can_bind (port : int ) -> bool :
122+ with socket .socket (socket .AF_INET , socket .SOCK_STREAM ) as sock :
123+ sock .setsockopt (socket .SOL_SOCKET , socket .SO_REUSEADDR , 1 )
124+ try :
125+ sock .bind ((host , port ))
126+ except OSError :
127+ return False
128+ return True
129+
130+ if requested_port > 0 and _can_bind (requested_port ):
131+ return requested_port , False
132+
133+ with socket .socket (socket .AF_INET , socket .SOCK_STREAM ) as sock :
134+ sock .bind ((host , 0 ))
135+ fallback_port = int (sock .getsockname ()[1 ])
136+
137+ return fallback_port , True
138+
139+
113140def basic_auth_header (username : str , password : str ) -> dict [str , str ]:
114141 token = base64 .b64encode (f"{ username } :{ password } " .encode ("utf-8" )).decode ("ascii" )
115142 return {"Authorization" : f"Basic { token } " }
@@ -186,7 +213,9 @@ def main() -> int:
186213 server_root = Path (args .server_root )
187214 reset_server_root (server_root )
188215
189- base_url = f"http://{ args .host } :{ args .http_port } "
216+ http_port , used_fallback_port = choose_http_port (args .host , args .http_port )
217+
218+ base_url = f"http://{ args .host } :{ http_port } "
190219 basic_headers = basic_auth_header ("root" , ROOT_PASSWORD )
191220
192221 print ("=" * 72 )
@@ -197,14 +226,19 @@ def main() -> int:
197226 print (f"Server root: { server_root } " )
198227 print (f"Database name: { args .db_name } " )
199228 print (f"Base URL: { base_url } " )
229+ if used_fallback_port :
230+ print (
231+ f"Requested HTTP port { args .http_port } was unavailable; "
232+ f"using { http_port } instead."
233+ )
200234 print ()
201235
202236 with arcadedb .create_server (
203237 str (server_root ),
204238 root_password = ROOT_PASSWORD ,
205239 config = {
206240 "host" : args .host ,
207- "http_port" : args . http_port ,
241+ "http_port" : http_port ,
208242 "mode" : "development" ,
209243 },
210244 ) as server :
0 commit comments