From e3eba9bbfd06323d4282a2ffb2c304cf8481250a Mon Sep 17 00:00:00 2001 From: bentsileviav Date: Wed, 18 Feb 2026 15:47:13 +0200 Subject: [PATCH 1/2] support more https situations rather than falling back to http --- .../clickhouse/ClickHouseJdbcUrlParser.java | 39 +++++-- .../ClickHouseJdbcUrlParserTest.java | 110 ++++++++++++++++-- 2 files changed, 126 insertions(+), 23 deletions(-) diff --git a/sdks/java/io/clickhouse/src/main/java/org/apache/beam/sdk/io/clickhouse/ClickHouseJdbcUrlParser.java b/sdks/java/io/clickhouse/src/main/java/org/apache/beam/sdk/io/clickhouse/ClickHouseJdbcUrlParser.java index 92cd1eaeacdd..d0bb497b2358 100644 --- a/sdks/java/io/clickhouse/src/main/java/org/apache/beam/sdk/io/clickhouse/ClickHouseJdbcUrlParser.java +++ b/sdks/java/io/clickhouse/src/main/java/org/apache/beam/sdk/io/clickhouse/ClickHouseJdbcUrlParser.java @@ -117,6 +117,8 @@ static ParsedJdbcUrl parse(String jdbcUrl) { /** * Extracts and normalizes the HTTP/HTTPS URL from a JDBC URL. * + *

Automatically detects HTTPS based on port 8443 or ssl=true parameter. + * * @param jdbcUrl the JDBC URL to process * @return normalized HTTP/HTTPS URL * @throws IllegalArgumentException if the URL format is invalid @@ -136,14 +138,20 @@ private static String extractHttpUrl(String jdbcUrl) { actualUrl = urlWithoutJdbc.substring(3); } else { throw new IllegalArgumentException( - "Invalid JDBC URL format. Expected 'jdbc:clickhouse:' or 'jdbc:ch:' prefix. Got: " - + jdbcUrl); + "Invalid JDBC URL format. Expected 'jdbc:clickhouse:' or 'jdbc:ch:' prefix. Got: " + + jdbcUrl); } - // Check if URL already has a scheme and validate it - if (actualUrl.toLowerCase().startsWith("http://") - || actualUrl.toLowerCase().startsWith("https://")) { - return actualUrl; + boolean useHttps = false; + + // Check if port suggests HTTPS (8443 is default HTTPS port for ClickHouse) + if (actualUrl.contains(":8443")) { + useHttps = true; + } + + // Check if ssl=true in query string + if (actualUrl.toLowerCase().contains("ssl=true")) { + useHttps = true; } // Check for invalid schemes before prepending http:// @@ -151,17 +159,24 @@ private static String extractHttpUrl(String jdbcUrl) { // Extract the scheme part int schemeEnd = actualUrl.indexOf("://"); String scheme = actualUrl.substring(0, schemeEnd).toLowerCase(); - if (!scheme.equals("http") && !scheme.equals("https")) { + + if (scheme.equals("http") || scheme.equals("https")) { + // If http:// but ssl=true detected, upgrade to https:// + if (scheme.equals("http") && useHttps) { + actualUrl = "https://" + actualUrl.substring(schemeEnd + 3); + } + return actualUrl; + } else { throw new IllegalArgumentException( - "Invalid scheme in JDBC URL. Expected 'http' or 'https'. Got: " + scheme); + "Invalid scheme in JDBC URL. Expected 'http' or 'https'. Got: " + scheme); } } - // If URL doesn't start with http:// or https://, assume http:// + // If URL doesn't start with http:// or https://, add the appropriate scheme if (actualUrl.startsWith("//")) { - actualUrl = "http:" + actualUrl; + actualUrl = (useHttps ? "https:" : "http:") + actualUrl; } else { - actualUrl = "http://" + actualUrl; + actualUrl = (useHttps ? "https://" : "http://") + actualUrl; } return actualUrl; @@ -176,7 +191,7 @@ private static String extractHttpUrl(String jdbcUrl) { private static void validateScheme(String scheme) { if (scheme == null || (!scheme.equals("http") && !scheme.equals("https"))) { throw new IllegalArgumentException( - "Invalid scheme. Expected 'http' or 'https'. Got: " + scheme); + "Invalid scheme. Expected 'http' or 'https'. Got: " + scheme); } } diff --git a/sdks/java/io/clickhouse/src/test/java/org/apache/beam/sdk/io/clickhouse/ClickHouseJdbcUrlParserTest.java b/sdks/java/io/clickhouse/src/test/java/org/apache/beam/sdk/io/clickhouse/ClickHouseJdbcUrlParserTest.java index 4b994522d9b3..3cfbae288316 100644 --- a/sdks/java/io/clickhouse/src/test/java/org/apache/beam/sdk/io/clickhouse/ClickHouseJdbcUrlParserTest.java +++ b/sdks/java/io/clickhouse/src/test/java/org/apache/beam/sdk/io/clickhouse/ClickHouseJdbcUrlParserTest.java @@ -117,7 +117,7 @@ public void testJdbcUrlWithSingleParameter() { @Test public void testJdbcUrlWithMultipleParameters() { String jdbcUrl = - "jdbc:clickhouse://localhost:8123/mydb?user=admin&password=secret&compress=true"; + "jdbc:clickhouse://localhost:8123/mydb?user=admin&password=secret&compress=true"; ParsedJdbcUrl parsed = ClickHouseJdbcUrlParser.parse(jdbcUrl); assertEquals("http://localhost:8123", parsed.getClickHouseUrl()); @@ -177,11 +177,11 @@ public void testJdbcUrlWithIpAddress() { @Test public void testJdbcUrlWithComplexQueryString() { String jdbcUrl = - "jdbc:clickhouse://localhost:8123/mydb?" - + "user=admin&password=secret&" - + "socket_timeout=30000&" - + "connection_timeout=10000&" - + "compress=true"; + "jdbc:clickhouse://localhost:8123/mydb?" + + "user=admin&password=secret&" + + "socket_timeout=30000&" + + "connection_timeout=10000&" + + "compress=true"; ParsedJdbcUrl parsed = ClickHouseJdbcUrlParser.parse(jdbcUrl); Properties props = parsed.getProperties(); @@ -290,11 +290,11 @@ public void testJdbcUrlWithoutJdbcPrefix() { public void testBackwardCompatibilityScenario() { // Simulating a real-world legacy JDBC URL String legacyJdbcUrl = - "jdbc:clickhouse://prod-clickhouse.internal:8123/analytics?" - + "user=analytics_user&" - + "password=secure123&" - + "compress=true&" - + "socket_timeout=60000"; + "jdbc:clickhouse://prod-clickhouse.internal:8123/analytics?" + + "user=analytics_user&" + + "password=secure123&" + + "compress=true&" + + "socket_timeout=60000"; ParsedJdbcUrl parsed = ClickHouseJdbcUrlParser.parse(legacyJdbcUrl); @@ -338,4 +338,92 @@ public void testJdbcUrlWithEmptyQueryParameter() { assertEquals("", props.getProperty("user")); assertEquals("secret", props.getProperty("password")); } + + @Test + public void testJdbcUrlWithSslParameter() { + String jdbcUrl = "jdbc:clickhouse://localhost:8443/mydb?ssl=true&user=admin"; + ParsedJdbcUrl parsed = ClickHouseJdbcUrlParser.parse(jdbcUrl); + + assertEquals("https://localhost:8443", parsed.getClickHouseUrl()); + assertEquals("mydb", parsed.getDatabase()); + assertEquals("admin", parsed.getProperties().getProperty("user")); + assertEquals("true", parsed.getProperties().getProperty("ssl")); + } + + @Test + public void testJdbcUrlWithPort8443DefaultsToHttps() { + String jdbcUrl = "jdbc:clickhouse://myhost:8443/mydb"; + ParsedJdbcUrl parsed = ClickHouseJdbcUrlParser.parse(jdbcUrl); + + assertEquals("https://myhost:8443", parsed.getClickHouseUrl()); + assertEquals("mydb", parsed.getDatabase()); + } + + @Test + public void testClickHouseCloudUrl() { + String jdbcUrl = + "jdbc:clickhouse://someservice.clickhouse.cloud:8443/default?" + + "user=default&password=secret&ssl=true&sslmode=NONE"; + ParsedJdbcUrl parsed = ClickHouseJdbcUrlParser.parse(jdbcUrl); + + assertEquals("https://someservice.clickhouse.cloud:8443", parsed.getClickHouseUrl()); + assertEquals("default", parsed.getDatabase()); + assertEquals("default", parsed.getProperties().getProperty("user")); + assertEquals("secret", parsed.getProperties().getProperty("password")); + assertEquals("true", parsed.getProperties().getProperty("ssl")); + } + + @Test + public void testJdbcUrlPort8123DefaultsToHttp() { + String jdbcUrl = "jdbc:clickhouse://localhost:8123/mydb"; + ParsedJdbcUrl parsed = ClickHouseJdbcUrlParser.parse(jdbcUrl); + + assertEquals("http://localhost:8123", parsed.getClickHouseUrl()); + } + + @Test + public void testHttpSchemeUpgradedToHttpsWhenSslTrue() { + String jdbcUrl = "jdbc:clickhouse:http://localhost:8443/mydb?ssl=true"; + ParsedJdbcUrl parsed = ClickHouseJdbcUrlParser.parse(jdbcUrl); + + // Should upgrade http to https because ssl=true + assertEquals("https://localhost:8443", parsed.getClickHouseUrl()); + } + + @Test + public void testSslTrueTriggersHttps() { + String jdbcUrl = "jdbc:clickhouse://localhost:8123/mydb?ssl=true"; + ParsedJdbcUrl parsed = ClickHouseJdbcUrlParser.parse(jdbcUrl); + + // Should use https because ssl=true, even though port is 8123 + assertEquals("https://localhost:8123", parsed.getClickHouseUrl()); + } + + @Test + public void testPort8443TriggersHttps() { + String jdbcUrl = "jdbc:clickhouse://localhost:8443/mydb"; + ParsedJdbcUrl parsed = ClickHouseJdbcUrlParser.parse(jdbcUrl); + + // Should use https because port is 8443 + assertEquals("https://localhost:8443", parsed.getClickHouseUrl()); + } + + @Test + public void testClickHouseCloudUrlWithSsl() { + String jdbcUrl = + "jdbc:clickhouse://someservice.clickhouse.cloud:8443/default?" + + "user=default&password=secret&ssl=true&sslmode=NONE"; + ParsedJdbcUrl parsed = ClickHouseJdbcUrlParser.parse(jdbcUrl); + + assertEquals("https://someservice.clickhouse.cloud:8443", parsed.getClickHouseUrl()); + assertEquals("default", parsed.getDatabase()); + } + + @Test + public void testExplicitHttpsPreserved() { + String jdbcUrl = "jdbc:clickhouse:https://localhost:8443/mydb"; + ParsedJdbcUrl parsed = ClickHouseJdbcUrlParser.parse(jdbcUrl); + + assertEquals("https://localhost:8443", parsed.getClickHouseUrl()); + } } From ffe47c2fd5e848956f01e48223a4bd222e567b15 Mon Sep 17 00:00:00 2001 From: bentsileviav Date: Wed, 18 Feb 2026 15:56:44 +0200 Subject: [PATCH 2/2] fix spotless --- .../clickhouse/ClickHouseJdbcUrlParser.java | 8 ++--- .../ClickHouseJdbcUrlParserTest.java | 30 +++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/sdks/java/io/clickhouse/src/main/java/org/apache/beam/sdk/io/clickhouse/ClickHouseJdbcUrlParser.java b/sdks/java/io/clickhouse/src/main/java/org/apache/beam/sdk/io/clickhouse/ClickHouseJdbcUrlParser.java index d0bb497b2358..0e9e46b9f298 100644 --- a/sdks/java/io/clickhouse/src/main/java/org/apache/beam/sdk/io/clickhouse/ClickHouseJdbcUrlParser.java +++ b/sdks/java/io/clickhouse/src/main/java/org/apache/beam/sdk/io/clickhouse/ClickHouseJdbcUrlParser.java @@ -138,8 +138,8 @@ private static String extractHttpUrl(String jdbcUrl) { actualUrl = urlWithoutJdbc.substring(3); } else { throw new IllegalArgumentException( - "Invalid JDBC URL format. Expected 'jdbc:clickhouse:' or 'jdbc:ch:' prefix. Got: " - + jdbcUrl); + "Invalid JDBC URL format. Expected 'jdbc:clickhouse:' or 'jdbc:ch:' prefix. Got: " + + jdbcUrl); } boolean useHttps = false; @@ -168,7 +168,7 @@ private static String extractHttpUrl(String jdbcUrl) { return actualUrl; } else { throw new IllegalArgumentException( - "Invalid scheme in JDBC URL. Expected 'http' or 'https'. Got: " + scheme); + "Invalid scheme in JDBC URL. Expected 'http' or 'https'. Got: " + scheme); } } @@ -191,7 +191,7 @@ private static String extractHttpUrl(String jdbcUrl) { private static void validateScheme(String scheme) { if (scheme == null || (!scheme.equals("http") && !scheme.equals("https"))) { throw new IllegalArgumentException( - "Invalid scheme. Expected 'http' or 'https'. Got: " + scheme); + "Invalid scheme. Expected 'http' or 'https'. Got: " + scheme); } } diff --git a/sdks/java/io/clickhouse/src/test/java/org/apache/beam/sdk/io/clickhouse/ClickHouseJdbcUrlParserTest.java b/sdks/java/io/clickhouse/src/test/java/org/apache/beam/sdk/io/clickhouse/ClickHouseJdbcUrlParserTest.java index 3cfbae288316..d80398e9d869 100644 --- a/sdks/java/io/clickhouse/src/test/java/org/apache/beam/sdk/io/clickhouse/ClickHouseJdbcUrlParserTest.java +++ b/sdks/java/io/clickhouse/src/test/java/org/apache/beam/sdk/io/clickhouse/ClickHouseJdbcUrlParserTest.java @@ -117,7 +117,7 @@ public void testJdbcUrlWithSingleParameter() { @Test public void testJdbcUrlWithMultipleParameters() { String jdbcUrl = - "jdbc:clickhouse://localhost:8123/mydb?user=admin&password=secret&compress=true"; + "jdbc:clickhouse://localhost:8123/mydb?user=admin&password=secret&compress=true"; ParsedJdbcUrl parsed = ClickHouseJdbcUrlParser.parse(jdbcUrl); assertEquals("http://localhost:8123", parsed.getClickHouseUrl()); @@ -177,11 +177,11 @@ public void testJdbcUrlWithIpAddress() { @Test public void testJdbcUrlWithComplexQueryString() { String jdbcUrl = - "jdbc:clickhouse://localhost:8123/mydb?" - + "user=admin&password=secret&" - + "socket_timeout=30000&" - + "connection_timeout=10000&" - + "compress=true"; + "jdbc:clickhouse://localhost:8123/mydb?" + + "user=admin&password=secret&" + + "socket_timeout=30000&" + + "connection_timeout=10000&" + + "compress=true"; ParsedJdbcUrl parsed = ClickHouseJdbcUrlParser.parse(jdbcUrl); Properties props = parsed.getProperties(); @@ -290,11 +290,11 @@ public void testJdbcUrlWithoutJdbcPrefix() { public void testBackwardCompatibilityScenario() { // Simulating a real-world legacy JDBC URL String legacyJdbcUrl = - "jdbc:clickhouse://prod-clickhouse.internal:8123/analytics?" - + "user=analytics_user&" - + "password=secure123&" - + "compress=true&" - + "socket_timeout=60000"; + "jdbc:clickhouse://prod-clickhouse.internal:8123/analytics?" + + "user=analytics_user&" + + "password=secure123&" + + "compress=true&" + + "socket_timeout=60000"; ParsedJdbcUrl parsed = ClickHouseJdbcUrlParser.parse(legacyJdbcUrl); @@ -362,8 +362,8 @@ public void testJdbcUrlWithPort8443DefaultsToHttps() { @Test public void testClickHouseCloudUrl() { String jdbcUrl = - "jdbc:clickhouse://someservice.clickhouse.cloud:8443/default?" - + "user=default&password=secret&ssl=true&sslmode=NONE"; + "jdbc:clickhouse://someservice.clickhouse.cloud:8443/default?" + + "user=default&password=secret&ssl=true&sslmode=NONE"; ParsedJdbcUrl parsed = ClickHouseJdbcUrlParser.parse(jdbcUrl); assertEquals("https://someservice.clickhouse.cloud:8443", parsed.getClickHouseUrl()); @@ -411,8 +411,8 @@ public void testPort8443TriggersHttps() { @Test public void testClickHouseCloudUrlWithSsl() { String jdbcUrl = - "jdbc:clickhouse://someservice.clickhouse.cloud:8443/default?" - + "user=default&password=secret&ssl=true&sslmode=NONE"; + "jdbc:clickhouse://someservice.clickhouse.cloud:8443/default?" + + "user=default&password=secret&ssl=true&sslmode=NONE"; ParsedJdbcUrl parsed = ClickHouseJdbcUrlParser.parse(jdbcUrl); assertEquals("https://someservice.clickhouse.cloud:8443", parsed.getClickHouseUrl());