|
26 | 26 | import static org.junit.jupiter.api.Assertions.fail; |
27 | 27 |
|
28 | 28 | import com.google.protobuf.Message; |
| 29 | +import java.io.IOException; |
| 30 | +import java.net.Proxy; |
| 31 | +import java.net.ProxySelector; |
| 32 | +import java.net.SocketAddress; |
| 33 | +import java.net.URI; |
29 | 34 | import java.net.URISyntaxException; |
30 | 35 | import java.sql.Connection; |
31 | 36 | import java.sql.Driver; |
32 | 37 | import java.sql.DriverManager; |
33 | 38 | import java.sql.ResultSet; |
34 | 39 | import java.sql.SQLException; |
35 | 40 | import java.sql.Statement; |
| 41 | +import java.util.List; |
36 | 42 | import java.util.Map; |
37 | 43 | import java.util.Properties; |
| 44 | +import java.util.concurrent.atomic.AtomicBoolean; |
38 | 45 | import java.util.function.Consumer; |
39 | 46 | import org.apache.arrow.driver.jdbc.authentication.UserPasswordAuthentication; |
40 | 47 | import org.apache.arrow.driver.jdbc.client.ArrowFlightSqlClientHandler; |
@@ -769,117 +776,38 @@ public void testResultSetsFromDatabaseMetadataClosedOnConnectionClose() throws E |
769 | 776 | } |
770 | 777 | } |
771 | 778 |
|
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 | 779 | @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(); |
| 780 | + public void testJdbcDriverConsultsProxySelectorForTcpConnections() throws Exception { |
| 781 | + AtomicBoolean consulted = new AtomicBoolean(false); |
| 782 | + ProxySelector original = ProxySelector.getDefault(); |
| 783 | + ProxySelector.setDefault( |
| 784 | + new ProxySelector() { |
| 785 | + @Override |
| 786 | + public List<Proxy> select(URI uri) { |
| 787 | + consulted.set(true); |
| 788 | + return original.select(uri); |
| 789 | + } |
791 | 790 |
|
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", ""); |
| 791 | + @Override |
| 792 | + public void connectFailed(URI uri, SocketAddress sa, IOException e) {} |
| 793 | + }); |
796 | 794 |
|
| 795 | + try { |
797 | 796 | final Properties properties = new Properties(); |
798 | 797 | properties.put(ArrowFlightConnectionProperty.USER.camelName(), userTest); |
799 | 798 | properties.put(ArrowFlightConnectionProperty.PASSWORD.camelName(), passTest); |
800 | 799 | properties.put("useEncryption", false); |
801 | 800 |
|
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 | | - } |
| 801 | + DriverManager.getConnection( |
| 802 | + "jdbc:arrow-flight-sql://localhost:" + FLIGHT_SERVER_TEST_EXTENSION.getPort(), |
| 803 | + properties) |
| 804 | + .close(); |
811 | 805 |
|
812 | 806 | 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 | | - |
| 807 | + consulted.get(), |
| 808 | + "JDBC driver must consult ProxySelector so JVM proxy settings are respected"); |
817 | 809 | } 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 | | - } |
| 810 | + ProxySelector.setDefault(original); |
883 | 811 | } |
884 | 812 | } |
885 | 813 | } |
0 commit comments