diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index 5d95473f2..f264860a8 100644 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -23,6 +23,7 @@ - Fixed `?` characters inside SQL comments, string literals, and quoted identifiers being incorrectly counted as parameter placeholders when `supportManyParameters=1`. `SQLInterpolator` now uses `SqlCommentParser` to locate only real placeholders. Fixes #1331. - Fixed `MetadataOperationTimeout` not being applied when metadata operations use SHOW commands. Operations like `getTables`, `getSchemas`, and `getColumns` now respect the `MetadataOperationTimeout` connection property instead of hanging indefinitely with no timeout. - Reclassify transient server errors to standard SQL states (08S01, 40001) across all Thrift error sites. This ensures UC unavailability and concurrent modification errors surface consistently for better retry handling. Note: Dashboards and branching logic keyed on legacy XXUCC or 42000 must be updated. +- Fixed local port availability check to bind to `localhost` instead of the wildcard address. This prevents the OAuth browser authentication from failing when the port is already used. --- *Note: When making changes, please add your change under the appropriate section diff --git a/src/main/java/com/databricks/jdbc/dbclient/impl/common/ClientConfigurator.java b/src/main/java/com/databricks/jdbc/dbclient/impl/common/ClientConfigurator.java index e6c296e54..b64bbe3a4 100644 --- a/src/main/java/com/databricks/jdbc/dbclient/impl/common/ClientConfigurator.java +++ b/src/main/java/com/databricks/jdbc/dbclient/impl/common/ClientConfigurator.java @@ -286,7 +286,7 @@ public int findAvailablePort(List initialPorts) { boolean isPortAvailable(int port) { try (ServerSocket serverSocket = new ServerSocket()) { serverSocket.setReuseAddress(true); - serverSocket.bind(new InetSocketAddress(port)); + serverSocket.bind(new InetSocketAddress("localhost", port)); return true; } catch (IOException e) { return false; diff --git a/src/test/java/com/databricks/jdbc/dbclient/impl/common/ClientConfiguratorTest.java b/src/test/java/com/databricks/jdbc/dbclient/impl/common/ClientConfiguratorTest.java index 2a76d2290..ccf11201b 100644 --- a/src/test/java/com/databricks/jdbc/dbclient/impl/common/ClientConfiguratorTest.java +++ b/src/test/java/com/databricks/jdbc/dbclient/impl/common/ClientConfiguratorTest.java @@ -392,11 +392,19 @@ void testFindAvailablePort() throws Exception { int result = configurator.findAvailablePort(ports); assertEquals(availablePort, result); + // Test with a single available port which is bound to the wildcard address + try (ServerSocket serverSocket = new ServerSocket()) { + serverSocket.setReuseAddress(true); + serverSocket.bind(new InetSocketAddress(availablePort)); + result = configurator.findAvailablePort(ports); + assertEquals(availablePort, result); + } + // Test with multiple ports, first unavailable int secondAvailablePort = findFreePort(); try (ServerSocket serverSocket = new ServerSocket()) { serverSocket.setReuseAddress(true); - serverSocket.bind(new InetSocketAddress(availablePort)); + serverSocket.bind(new InetSocketAddress("localhost", availablePort)); ports = List.of(availablePort, secondAvailablePort); result = configurator.findAvailablePort(ports); assertEquals(secondAvailablePort, result); @@ -405,7 +413,7 @@ void testFindAvailablePort() throws Exception { // Test incremental search - first port unavailable, finds next available in sequence try (ServerSocket serverSocket2 = new ServerSocket()) { serverSocket2.setReuseAddress(true); - serverSocket2.bind(new InetSocketAddress(availablePort)); + serverSocket2.bind(new InetSocketAddress("localhost", availablePort)); ports = List.of(availablePort); result = configurator.findAvailablePort(ports); assertTrue( @@ -414,7 +422,7 @@ void testFindAvailablePort() throws Exception { // 3. The returned port should actually be available try (ServerSocket testSocket = new ServerSocket()) { testSocket.setReuseAddress(true); - testSocket.bind(new InetSocketAddress(result)); + testSocket.bind(new InetSocketAddress("localhost", result)); assertNotNull(testSocket, "Returned port should be available for binding"); } } @@ -441,9 +449,9 @@ void testFindAvailablePortThrowsExceptionWhenNoPortsAvailable() throws Exception try (ServerSocket socket1 = new ServerSocket(); ServerSocket socket2 = new ServerSocket()) { socket1.setReuseAddress(true); - socket1.bind(new InetSocketAddress(port1)); + socket1.bind(new InetSocketAddress("localhost", port1)); socket2.setReuseAddress(true); - socket2.bind(new InetSocketAddress(port2)); + socket2.bind(new InetSocketAddress("localhost", port2)); // First test with multiple specified ports List unavailablePorts = List.of(port1, port2); @@ -473,7 +481,7 @@ protected boolean isPortAvailable(int port) { private int findFreePort() { try (ServerSocket socket = new ServerSocket()) { socket.setReuseAddress(true); - socket.bind(new InetSocketAddress(0)); + socket.bind(new InetSocketAddress("localhost", 0)); return socket.getLocalPort(); } catch (IOException e) { throw new RuntimeException("Failed to find free port", e);