From c7ead924bd32ed64fef74fc63afbfcf10a36fe64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Szczerbi=C5=84ski?= Date: Tue, 3 Sep 2024 15:58:29 +0200 Subject: [PATCH] SNOW-1631831: JDBC - handle TCP connection closure (traffic dependency request) (#1872) Co-authored-by: Piotr Bulawa --- .../client/jdbc/BaseWiremockTest.java | 244 ++++++++++++ .../snowflake/client/jdbc/ProxyLatestIT.java | 360 ++++-------------- .../client/jdbc/RestRequestTest.java | 24 ++ .../jdbc/RestRequestWiremockLatestIT.java | 75 ++++ 4 files changed, 422 insertions(+), 281 deletions(-) create mode 100644 src/test/java/net/snowflake/client/jdbc/BaseWiremockTest.java create mode 100644 src/test/java/net/snowflake/client/jdbc/RestRequestWiremockLatestIT.java diff --git a/src/test/java/net/snowflake/client/jdbc/BaseWiremockTest.java b/src/test/java/net/snowflake/client/jdbc/BaseWiremockTest.java new file mode 100644 index 000000000..5a2fe8e96 --- /dev/null +++ b/src/test/java/net/snowflake/client/jdbc/BaseWiremockTest.java @@ -0,0 +1,244 @@ +package net.snowflake.client.jdbc; + +import static junit.framework.TestCase.assertEquals; +import static net.snowflake.client.AbstractDriverIT.getConnectionParameters; +import static net.snowflake.client.jdbc.SnowflakeUtil.systemGetProperty; +import static org.awaitility.Awaitility.await; +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import java.io.File; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.ServerSocket; +import java.nio.file.Paths; +import java.time.Duration; +import java.util.Map; +import java.util.Properties; +import net.snowflake.client.RunningNotOnGithubActionsMac; +import net.snowflake.client.RunningNotOnJava21; +import net.snowflake.client.RunningNotOnJava8; +import net.snowflake.client.core.HttpUtil; +import net.snowflake.client.log.SFLogger; +import net.snowflake.client.log.SFLoggerFactory; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.BeforeClass; + +public abstract class BaseWiremockTest { + + protected static final SFLogger logger = SFLoggerFactory.getLogger(BaseWiremockTest.class); + protected static final String WIREMOCK_HOME_DIR = ".wiremock"; + protected static final String WIREMOCK_M2_PATH = + "/.m2/repository/org/wiremock/wiremock-standalone/3.8.0/wiremock-standalone-3.8.0.jar"; + protected static final String WIREMOCK_HOST = "localhost"; + protected static final String TRUST_STORE_PROPERTY = "javax.net.ssl.trustStore"; + protected static int wiremockHttpPort; + protected static int wiremockHttpsPort; + private static String originalTrustStorePath; + protected static Process wiremockStandalone; + + @BeforeClass + public static void setUpClass() { + assumeFalse(RunningNotOnJava8.isRunningOnJava8()); + assumeFalse(RunningNotOnJava21.isRunningOnJava21()); + assumeFalse( + RunningNotOnGithubActionsMac + .isRunningOnGithubActionsMac()); // disabled until issue with access to localhost + // (https://github.com/snowflakedb/snowflake-jdbc/pull/1807#discussion_r1686229430) is fixed on + // github actions mac image. Ticket to enable when fixed: SNOW-1555950 + originalTrustStorePath = systemGetProperty(TRUST_STORE_PROPERTY); + startWiremockStandAlone(); + } + + @After + public void tearDown() { + restoreTrustStorePathProperty(); + resetWiremock(); + HttpUtil.httpClient.clear(); + } + + @AfterClass + public static void tearDownClass() { + stopWiremockStandAlone(); + } + + protected static void startWiremockStandAlone() { + // retrying in case of fail in port bindings + await() + .alias("wait for wiremock responding") + .atMost(Duration.ofSeconds(10)) + .until( + () -> { + try { + wiremockHttpPort = findFreePort(); + wiremockHttpsPort = findFreePort(); + wiremockStandalone = + new ProcessBuilder( + "java", + "-jar", + getWiremockStandAlonePath(), + "--root-dir", + System.getProperty("user.dir") + + File.separator + + WIREMOCK_HOME_DIR + + File.separator, + "--enable-browser-proxying", // work as forward proxy + "--proxy-pass-through", + "false", // pass through only matched requests + "--port", + String.valueOf(wiremockHttpPort), + "--https-port", + String.valueOf(wiremockHttpsPort), + "--https-keystore", + getResourceURL("wiremock" + File.separator + "ca-cert.jks"), + "--ca-keystore", + getResourceURL("wiremock" + File.separator + "ca-cert.jks")) + .inheritIO() + .start(); + waitForWiremock(); + return true; + } catch (Exception e) { + logger.warn("Failed to start wiremock, retrying: ", e); + return false; + } + }); + } + + protected void resetWiremock() { + HttpPost postRequest; + postRequest = new HttpPost("http://" + WIREMOCK_HOST + ":" + getAdminPort() + "/__admin/reset"); + try (CloseableHttpClient client = HttpClients.createDefault(); + CloseableHttpResponse response = client.execute(postRequest)) { + assertEquals(200, response.getStatusLine().getStatusCode()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static String getWiremockStandAlonePath() { + return System.getProperty("user.home") + WIREMOCK_M2_PATH; + } + + private static void waitForWiremock() { + await() + .pollDelay(Duration.ofSeconds(1)) + .atMost(Duration.ofSeconds(3)) + .until(BaseWiremockTest::isWiremockResponding); + } + + private static boolean isWiremockResponding() { + try (CloseableHttpClient httpClient = HttpClients.createDefault()) { + HttpGet request = + new HttpGet( + String.format("http://%s:%d/__admin/mappings", WIREMOCK_HOST, wiremockHttpPort)); + CloseableHttpResponse response = httpClient.execute(request); + return response.getStatusLine().getStatusCode() == 200; + } catch (Exception e) { + logger.warn("Waiting for wiremock to respond: ", e); + } + return false; + } + + protected static void stopWiremockStandAlone() { + if (wiremockStandalone != null) { + wiremockStandalone.destroyForcibly(); + await() + .alias("stop wiremock") + .atMost(Duration.ofSeconds(10)) + .until(() -> !wiremockStandalone.isAlive()); + } + } + + private static int findFreePort() { + try { + ServerSocket socket = new ServerSocket(0); + int port = socket.getLocalPort(); + socket.close(); + return port; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + protected Properties getProperties() { + Map params = getConnectionParameters(); + Properties props = new Properties(); + props.put("host", params.get("host")); + props.put("port", params.get("port")); + props.put("account", params.get("account")); + props.put("user", params.get("user")); + props.put("role", params.get("role")); + props.put("password", params.get("password")); + props.put("warehouse", params.get("warehouse")); + props.put("db", params.get("database")); + props.put("ssl", params.get("ssl")); + props.put("insecureMode", true); // OCSP disabled for wiremock proxy tests + return props; + } + + protected HttpPost createWiremockPostRequest(String body, String path) { + HttpPost postRequest = new HttpPost("http://" + WIREMOCK_HOST + ":" + getAdminPort() + path); + final StringEntity entity; + try { + entity = new StringEntity(body); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + postRequest.setEntity(entity); + postRequest.setHeader("Accept", "application/json"); + postRequest.setHeader("Content-type", "application/json"); + return postRequest; + } + + protected static void restoreTrustStorePathProperty() { + if (originalTrustStorePath != null) { + System.setProperty(TRUST_STORE_PROPERTY, originalTrustStorePath); + } else { + System.clearProperty(TRUST_STORE_PROPERTY); + } + } + + private int getAdminPort() { + return wiremockHttpPort; + } + + private static String getResourceURL(String relativePath) { + return Paths.get(systemGetProperty("user.dir"), "src", "test", "resources", relativePath) + .toAbsolutePath() + .toString(); + } + + protected void setCustomTrustStorePropertyPath() { + System.setProperty( + TRUST_STORE_PROPERTY, getResourceURL("wiremock" + File.separator + "ca-cert.jks")); + } + + protected void importMapping(String mappingImport) { + HttpPost request = createWiremockPostRequest(mappingImport, "/__admin/mappings/import"); + try (CloseableHttpClient httpClient = HttpClients.createDefault(); + CloseableHttpResponse response = httpClient.execute(request)) { + assumeTrue(response.getStatusLine().getStatusCode() == 200); + } catch (Exception e) { + logger.error("Importing mapping failed", e); + assumeNoException(e); + } + } + + protected void addMapping(String mapping) { + HttpPost postRequest = createWiremockPostRequest(mapping, "/__admin/mappings"); + try (CloseableHttpClient client = HttpClients.createDefault(); + CloseableHttpResponse response = client.execute(postRequest)) { + assertEquals(201, response.getStatusLine().getStatusCode()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/test/java/net/snowflake/client/jdbc/ProxyLatestIT.java b/src/test/java/net/snowflake/client/jdbc/ProxyLatestIT.java index d3ee887dd..4fca52d1c 100644 --- a/src/test/java/net/snowflake/client/jdbc/ProxyLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ProxyLatestIT.java @@ -1,269 +1,49 @@ package net.snowflake.client.jdbc; import static junit.framework.TestCase.assertEquals; -import static net.snowflake.client.AbstractDriverIT.getConnectionParameters; -import static net.snowflake.client.jdbc.SnowflakeUtil.systemGetProperty; -import static org.awaitility.Awaitility.await; import static org.junit.Assert.assertTrue; -import static org.junit.Assume.assumeFalse; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.File; import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.ServerSocket; -import java.nio.file.Paths; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import java.time.Duration; -import java.util.Map; import java.util.Objects; import java.util.Properties; -import net.snowflake.client.RunningNotOnGithubActionsMac; -import net.snowflake.client.RunningNotOnJava21; -import net.snowflake.client.RunningNotOnJava8; import net.snowflake.client.category.TestCategoryOthers; -import net.snowflake.client.core.HttpUtil; -import net.snowflake.client.log.SFLogger; -import net.snowflake.client.log.SFLoggerFactory; import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.junit.After; -import org.junit.AfterClass; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.experimental.categories.Category; @Category(TestCategoryOthers.class) -public class ProxyLatestIT { - - private static final SFLogger logger = SFLoggerFactory.getLogger(ProxyLatestIT.class); - private static final String WIREMOCK_HOME_DIR = ".wiremock"; - private static final String WIREMOCK_M2_PATH = - "/.m2/repository/org/wiremock/wiremock-standalone/3.8.0/wiremock-standalone-3.8.0.jar"; - private static final String WIREMOCK_HOST = "localhost"; - private static final String TRUST_STORE_PROPERTY = "javax.net.ssl.trustStore"; - private static int httpProxyPort; - private static int httpsProxyPort; - private static String originalTrustStorePath; - private static Process wiremockStandalone; - - @BeforeClass - public static void setUpClass() { - assumeFalse(RunningNotOnJava8.isRunningOnJava8()); - assumeFalse(RunningNotOnJava21.isRunningOnJava21()); - assumeFalse( - RunningNotOnGithubActionsMac - .isRunningOnGithubActionsMac()); // disabled until issue with access to localhost - // (https://github.com/snowflakedb/snowflake-jdbc/pull/1807#discussion_r1686229430) is fixed on - // github actions mac image. Ticket to enable when fixed: SNOW-1555950 - originalTrustStorePath = systemGetProperty(TRUST_STORE_PROPERTY); - startWiremockStandAlone(); - } +public class ProxyLatestIT extends BaseWiremockTest { @After public void tearDown() { - restoreTrustStorePathProperty(); + super.tearDown(); unsetJvmProperties(); - resetWiremock(); - HttpUtil.httpClient.clear(); - } - - @AfterClass - public static void tearDownClass() { - stopWiremockStandAlone(); - } - - @Test - public void testProxyIsUsedWhenSetInProperties() throws SQLException { - setCustomTrustStorePropertyPath(); - Properties props = getProperties(); - addProxyProperties(props); - proxyAll(props); - - connectAndVerifySimpleQuery(props); - verifyProxyWasUsed(); - } - - @Test - public void testProxyIsUsedWhenSetInJVMParams() throws SQLException { - setCustomTrustStorePropertyPath(); - Properties props = getProperties(); - setJvmProperties(props); - proxyAll(props); - - connectAndVerifySimpleQuery(props); - verifyProxyWasUsed(); - } - - @Test - public void testProxyNotUsedWhenNonProxyHostsMatchingInProperties() throws SQLException { - Properties props = getProperties(); - addProxyProperties(props); - props.put("nonProxyHosts", "*"); - proxyAll(props); - - connectAndVerifySimpleQuery(props); - verifyProxyNotUsed(); - } - - @Test - public void testProxyIsUsedWhenNonProxyHostsNotMatchingInProperties() throws SQLException { - setCustomTrustStorePropertyPath(); - Properties props = getProperties(); - addProxyProperties(props); - props.put("nonProxyHosts", "notMatchingHost"); - proxyAll(props); - - connectAndVerifySimpleQuery(props); - verifyProxyWasUsed(); } - @Test - public void testProxyNotUsedWhenNonProxyHostsMatchingInJVMParams() throws SQLException { - Properties props = getProperties(); - setJvmProperties(props); - System.setProperty("http.nonProxyHosts", "*"); - proxyAll(props); - - connectAndVerifySimpleQuery(props); - verifyProxyNotUsed(); - } - - @Test - public void testProxyUsedWhenNonProxyHostsNotMatchingInJVMParams() throws SQLException { - setCustomTrustStorePropertyPath(); - Properties props = getProperties(); - setJvmProperties(props); - System.setProperty("http.nonProxyHosts", "notMatchingHost"); - proxyAll(props); - - connectAndVerifySimpleQuery(props); - verifyProxyWasUsed(); - } - - private static void startWiremockStandAlone() { - // retrying in case of fail in port bindings - await() - .alias("wait for wiremock responding") - .atMost(Duration.ofSeconds(10)) - .until( - () -> { - try { - httpProxyPort = findFreePort(); - httpsProxyPort = findFreePort(); - wiremockStandalone = - new ProcessBuilder( - "java", - "-jar", - getWiremockStandAlonePath(), - "--root-dir", - System.getProperty("user.dir") - + File.separator - + WIREMOCK_HOME_DIR - + File.separator, - "--enable-browser-proxying", // work as forward proxy - "--proxy-pass-through", - "false", // pass through only matched requests - "--port", - String.valueOf(httpProxyPort), - "--https-port", - String.valueOf(httpsProxyPort), - "--https-keystore", - getResourceURL("wiremock" + File.separator + "ca-cert.jks"), - "--ca-keystore", - getResourceURL("wiremock" + File.separator + "ca-cert.jks")) - .inheritIO() - .start(); - waitForWiremock(); - return true; - } catch (Exception e) { - logger.warn("Failed to start wiremock, retrying: ", e); - return false; - } - }); - } - - private void resetWiremock() { - HttpPost postRequest; - postRequest = new HttpPost("http://" + WIREMOCK_HOST + ":" + getAdminPort() + "/__admin/reset"); - try (CloseableHttpClient client = HttpClients.createDefault(); - CloseableHttpResponse response = client.execute(postRequest)) { - assertEquals(200, response.getStatusLine().getStatusCode()); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private static String getWiremockStandAlonePath() { - return System.getProperty("user.home") + WIREMOCK_M2_PATH; - } - - private static void waitForWiremock() { - await() - .pollDelay(Duration.ofSeconds(1)) - .atMost(Duration.ofSeconds(3)) - .until(ProxyLatestIT::isWiremockResponding); - } - - private static boolean isWiremockResponding() { - try (CloseableHttpClient httpClient = HttpClients.createDefault()) { - HttpGet request = - new HttpGet(String.format("http://%s:%d/__admin/mappings", WIREMOCK_HOST, httpProxyPort)); - CloseableHttpResponse response = httpClient.execute(request); - return response.getStatusLine().getStatusCode() == 200; - } catch (Exception e) { - logger.warn("Waiting for wiremock to respond: ", e); - } - return false; - } - - private static void stopWiremockStandAlone() { - if (wiremockStandalone != null) { - wiremockStandalone.destroyForcibly(); - await() - .alias("stop wiremock") - .atMost(Duration.ofSeconds(10)) - .until(() -> !wiremockStandalone.isAlive()); - } + private String getProxyProtocol(Properties props) { + return props.get("ssl").toString().equals("on") ? "https" : "http"; } - private static int findFreePort() { - try { - ServerSocket socket = new ServerSocket(0); - int port = socket.getLocalPort(); - socket.close(); - return port; - } catch (Exception e) { - throw new RuntimeException(e); + private int getProxyPort(String proxyProtocol) { + if (Objects.equals(proxyProtocol, "http")) { + return wiremockHttpPort; + } else { + return wiremockHttpsPort; } } - private Properties getProperties() { - Map params = getConnectionParameters(); - Properties props = new Properties(); - props.put("host", params.get("host")); - props.put("port", params.get("port")); - props.put("account", params.get("account")); - props.put("user", params.get("user")); - props.put("role", params.get("role")); - props.put("password", params.get("password")); - props.put("warehouse", params.get("warehouse")); - props.put("db", params.get("database")); - props.put("ssl", params.get("ssl")); - props.put("insecureMode", true); // OCSP disabled for wiremock proxy tests - return props; - } - private void addProxyProperties(Properties props) { String proxyProtocol = getProxyProtocol(props); props.put("useProxy", "true"); @@ -294,16 +74,22 @@ private void unsetJvmProperties() { System.clearProperty("https.proxyPort"); } - private String getProxyProtocol(Properties props) { - return props.get("ssl").toString().equals("on") ? "https" : "http"; + private String getSnowflakeUrl(Properties props) { + String protocol = getProxyProtocol(props); + return String.format("%s://%s:%s", protocol, props.get("host"), props.get("port")); } - private int getProxyPort(String proxyProtocol) { - if (Objects.equals(proxyProtocol, "http")) { - return httpProxyPort; - } else { - return httpsProxyPort; - } + private void proxyAll(Properties props) { + String template = + "{\n" + + " \"request\": {\n" + + " \"method\": \"ANY\",\n" + + " \"urlPattern\": \".*\"\n" + + " }\n," + + " \"response\": { \"proxyBaseUrl\": \"%s\" }\n" + + "}"; + String body = String.format(template, getSnowflakeUrl(props)); + addMapping(body); } private void verifyProxyWasUsed() { @@ -315,20 +101,6 @@ private void verifyProxyNotUsed() { verifyRequestToProxy(".*", 0); } - private void proxyAll(Properties props) { - String body = - String.format( - "{\"request\": {\"method\": \"ANY\", \"urlPattern\": \".*\"},\"response\": {\"proxyBaseUrl\": \"%s\"}}", - getSnowflakeUrl(props)); - HttpPost postRequest = createWiremockPostRequest(body, "/__admin/mappings"); - try (CloseableHttpClient client = HttpClients.createDefault(); - CloseableHttpResponse response = client.execute(postRequest)) { - assertEquals(201, response.getStatusLine().getStatusCode()); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - private void connectAndVerifySimpleQuery(Properties props) throws SQLException { try (Connection con = DriverManager.getConnection( @@ -358,45 +130,71 @@ private void verifyRequestToProxy(String pathPattern, int expectedCount) { } } - private HttpPost createWiremockPostRequest(String body, String path) { - HttpPost postRequest = new HttpPost("http://" + WIREMOCK_HOST + ":" + getAdminPort() + path); - final StringEntity entity; - try { - entity = new StringEntity(body); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - postRequest.setEntity(entity); - postRequest.setHeader("Accept", "application/json"); - postRequest.setHeader("Content-type", "application/json"); - return postRequest; + @Test + public void testProxyIsUsedWhenSetInProperties() throws SQLException { + setCustomTrustStorePropertyPath(); + Properties props = getProperties(); + addProxyProperties(props); + proxyAll(props); + + connectAndVerifySimpleQuery(props); + verifyProxyWasUsed(); } - private static void restoreTrustStorePathProperty() { - if (originalTrustStorePath != null) { - System.setProperty(TRUST_STORE_PROPERTY, originalTrustStorePath); - } else { - System.clearProperty(TRUST_STORE_PROPERTY); - } + @Test + public void testProxyIsUsedWhenSetInJVMParams() throws SQLException { + setCustomTrustStorePropertyPath(); + Properties props = getProperties(); + setJvmProperties(props); + proxyAll(props); + + connectAndVerifySimpleQuery(props); + verifyProxyWasUsed(); } - private int getAdminPort() { - return httpProxyPort; + @Test + public void testProxyNotUsedWhenNonProxyHostsMatchingInProperties() throws SQLException { + Properties props = getProperties(); + addProxyProperties(props); + props.put("nonProxyHosts", "*"); + proxyAll(props); + + connectAndVerifySimpleQuery(props); + verifyProxyNotUsed(); } - private String getSnowflakeUrl(Properties props) { - String protocol = getProxyProtocol(props); - return String.format("%s://%s:%s", protocol, props.get("host"), props.get("port")); + @Test + public void testProxyIsUsedWhenNonProxyHostsNotMatchingInProperties() throws SQLException { + setCustomTrustStorePropertyPath(); + Properties props = getProperties(); + addProxyProperties(props); + props.put("nonProxyHosts", "notMatchingHost"); + proxyAll(props); + + connectAndVerifySimpleQuery(props); + verifyProxyWasUsed(); } - private static String getResourceURL(String relativePath) { - return Paths.get(systemGetProperty("user.dir"), "src", "test", "resources", relativePath) - .toAbsolutePath() - .toString(); + @Test + public void testProxyNotUsedWhenNonProxyHostsMatchingInJVMParams() throws SQLException { + Properties props = getProperties(); + setJvmProperties(props); + System.setProperty("http.nonProxyHosts", "*"); + proxyAll(props); + + connectAndVerifySimpleQuery(props); + verifyProxyNotUsed(); } - private void setCustomTrustStorePropertyPath() { - System.setProperty( - TRUST_STORE_PROPERTY, getResourceURL("wiremock" + File.separator + "ca-cert.jks")); + @Test + public void testProxyUsedWhenNonProxyHostsNotMatchingInJVMParams() throws SQLException { + setCustomTrustStorePropertyPath(); + Properties props = getProperties(); + setJvmProperties(props); + System.setProperty("http.nonProxyHosts", "notMatchingHost"); + proxyAll(props); + + connectAndVerifySimpleQuery(props); + verifyProxyWasUsed(); } } diff --git a/src/test/java/net/snowflake/client/jdbc/RestRequestTest.java b/src/test/java/net/snowflake/client/jdbc/RestRequestTest.java index ae56a49f7..6e1a26428 100644 --- a/src/test/java/net/snowflake/client/jdbc/RestRequestTest.java +++ b/src/test/java/net/snowflake/client/jdbc/RestRequestTest.java @@ -16,6 +16,7 @@ import static org.mockito.Mockito.when; import java.io.IOException; +import java.net.SocketException; import java.net.SocketTimeoutException; import java.util.ArrayList; import java.util.List; @@ -492,6 +493,29 @@ public CloseableHttpResponse answer(InvocationOnMock invocation) throws Throwabl } } + @Test + public void testConnectionClosedRetriesSuccessful() throws IOException, SnowflakeSQLException { + CloseableHttpClient client = mock(CloseableHttpClient.class); + when(client.execute(any(HttpUriRequest.class))) + .then( + new Answer() { + int callCount = 0; + + @Override + public CloseableHttpResponse answer(InvocationOnMock invocationOnMock) + throws Throwable { + callCount += 1; + if (callCount >= 1) { + return successResponse(); + } else { + throw new SocketException("Connection reset"); + } + } + }); + + execute(client, "fakeurl.com/?requestId=abcd-1234", 0, 0, 0, true, false, 1); + } + @Test(expected = SnowflakeSQLException.class) public void testLoginMaxRetries() throws IOException, SnowflakeSQLException { boolean telemetryEnabled = TelemetryService.getInstance().isEnabled(); diff --git a/src/test/java/net/snowflake/client/jdbc/RestRequestWiremockLatestIT.java b/src/test/java/net/snowflake/client/jdbc/RestRequestWiremockLatestIT.java new file mode 100644 index 000000000..76856b985 --- /dev/null +++ b/src/test/java/net/snowflake/client/jdbc/RestRequestWiremockLatestIT.java @@ -0,0 +1,75 @@ +package net.snowflake.client.jdbc; + +import java.util.concurrent.atomic.AtomicBoolean; +import net.snowflake.client.category.TestCategoryOthers; +import net.snowflake.client.core.ExecTimeTelemetryData; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +@Category(TestCategoryOthers.class) +public class RestRequestWiremockLatestIT extends BaseWiremockTest { + + String connectionResetByPeerScenario = + "{\n" + + " \"mappings\": [\n" + + " {\n" + + " \"scenarioName\": \"Connection reset by peer\",\n" + + " \"requiredScenarioState\": \"Started\",\n" + + " \"newScenarioState\": \"Connection is stable\",\n" + + " \"request\": {\n" + + " \"method\": \"GET\",\n" + + " \"url\": \"/endpoint\"\n" + + " },\n" + + " \"response\": {\n" + + " \"fault\": \"CONNECTION_RESET_BY_PEER\"\n" + + " }\n" + + " },\n" + + " {\n" + + " \"scenarioName\": \"Connection reset by peer\",\n" + + " \"requiredScenarioState\": \"Connection is stable\",\n" + + " \"request\": {\n" + + " \"method\": \"GET\",\n" + + " \"url\": \"/endpoint\"\n" + + " },\n" + + " \"response\": {\n" + + " \"status\": 200\n" + + " }\n" + + " }\n" + + " ],\n" + + " \"importOptions\": {\n" + + " \"duplicatePolicy\": \"IGNORE\",\n" + + " \"deleteAllNotInImport\": true\n" + + " }" + + "}"; + + @Test + public void testProxyIsUsedWhenSetInProperties() throws Exception { + importMapping(connectionResetByPeerScenario); + HttpClientBuilder httpClientBuilder = HttpClientBuilder.create().disableAutomaticRetries(); + try (CloseableHttpClient httpClient = httpClientBuilder.build()) { + HttpGet request = + new HttpGet(String.format("http://%s:%d/endpoint", WIREMOCK_HOST, wiremockHttpPort)); + RestRequest.execute( + httpClient, + request, + 0, + 0, + 0, + 0, + 0, + new AtomicBoolean(false), + false, + false, + false, + false, + new ExecTimeTelemetryData()); + + CloseableHttpResponse response = httpClient.execute(request); + assert (response.getStatusLine().getStatusCode() == 200); + } + } +}