@@ -768,4 +768,118 @@ public void testResultSetsFromDatabaseMetadataClosedOnConnectionClose() throws E
768768 assertTrue (resultSets [i ].isClosed ());
769769 }
770770 }
771+
772+ /**
773+ * Test that JDBC driver respects JVM proxy settings.
774+ *
775+ * <p>This test verifies that when JVM proxy properties are set, the Flight client attempts to
776+ * connect through the proxy. This will FAIL with the current implementation and PASS when the fix
777+ * is applied.
778+ *
779+ * @throws Exception on error.
780+ */
781+ @ Test
782+ public void testJdbcDriverRespectsProxySettings () throws Exception {
783+ final int targetPort = FLIGHT_SERVER_TEST_EXTENSION .getPort ();
784+
785+ String originalHttpsProxyHost = System .getProperty ("https.proxyHost" );
786+ String originalHttpsProxyPort = System .getProperty ("https.proxyPort" );
787+ String originalNonProxyHosts = System .getProperty ("http.nonProxyHosts" );
788+
789+ try (SimpleProxyDetector proxy = new SimpleProxyDetector ()) {
790+ proxy .start ();
791+
792+ System .setProperty ("https.proxyHost" , "localhost" );
793+ System .setProperty ("https.proxyPort" , String .valueOf (proxy .getPort ()));
794+ // Ensure localhost is not in the non-proxy hosts list
795+ System .setProperty ("http.nonProxyHosts" , "" );
796+
797+ final Properties properties = new Properties ();
798+ properties .put (ArrowFlightConnectionProperty .USER .camelName (), userTest );
799+ properties .put (ArrowFlightConnectionProperty .PASSWORD .camelName (), passTest );
800+ properties .put ("useEncryption" , false );
801+
802+ // Use "example.com" as the target host to trigger proxy usage
803+ // The proxy will receive the connection attempt, proving proxy settings are respected
804+ try (Connection connection =
805+ DriverManager .getConnection (
806+ "jdbc:arrow-flight-sql://example.com:" + targetPort , properties )) {
807+ // Connection will fail, but that's OK - we just want to see if proxy was contacted
808+ } catch (Exception e ) {
809+ // Expected - proxy doesn't forward
810+ }
811+
812+ assertTrue (
813+ proxy .wasContacted (),
814+ "JDBC driver should respect JVM proxy settings. "
815+ + "The proxy did not receive a connection, indicating proxy settings are ignored." );
816+
817+ } finally {
818+ if (originalHttpsProxyHost == null ) {
819+ System .clearProperty ("https.proxyHost" );
820+ } else {
821+ System .setProperty ("https.proxyHost" , originalHttpsProxyHost );
822+ }
823+ if (originalHttpsProxyPort == null ) {
824+ System .clearProperty ("https.proxyPort" );
825+ } else {
826+ System .setProperty ("https.proxyPort" , originalHttpsProxyPort );
827+ }
828+ if (originalNonProxyHosts == null ) {
829+ System .clearProperty ("http.nonProxyHosts" );
830+ } else {
831+ System .setProperty ("http.nonProxyHosts" , originalNonProxyHosts );
832+ }
833+ }
834+ }
835+
836+ /** Simple proxy detector for testing proxy support. */
837+ private static class SimpleProxyDetector implements AutoCloseable {
838+ private final int port ;
839+ private final java .util .concurrent .atomic .AtomicBoolean contacted =
840+ new java .util .concurrent .atomic .AtomicBoolean (false );
841+ private final java .util .concurrent .CountDownLatch started =
842+ new java .util .concurrent .CountDownLatch (1 );
843+ private Thread thread ;
844+
845+ SimpleProxyDetector () throws java .io .IOException {
846+ try (java .net .ServerSocket socket = new java .net .ServerSocket (0 )) {
847+ socket .setReuseAddress (true );
848+ this .port = socket .getLocalPort ();
849+ }
850+ }
851+
852+ void start () throws InterruptedException {
853+ thread =
854+ new Thread (
855+ () -> {
856+ try (java .net .ServerSocket socket = new java .net .ServerSocket (port )) {
857+ started .countDown ();
858+ socket .setSoTimeout (5000 );
859+ socket .accept ();
860+ contacted .set (true );
861+ } catch (Exception e ) {
862+ // Timeout or error
863+ }
864+ });
865+ thread .setDaemon (true );
866+ thread .start ();
867+ started .await (5 , java .util .concurrent .TimeUnit .SECONDS );
868+ }
869+
870+ int getPort () {
871+ return port ;
872+ }
873+
874+ boolean wasContacted () {
875+ return contacted .get ();
876+ }
877+
878+ @ Override
879+ public void close () {
880+ if (thread != null ) {
881+ thread .interrupt ();
882+ }
883+ }
884+ }
771885}
0 commit comments