Skip to content

Commit f0a88f2

Browse files
committed
preserving path part from url when addEndpoint(string) used
1 parent bbc1af2 commit f0a88f2

File tree

2 files changed

+83
-6
lines changed

2 files changed

+83
-6
lines changed

client-v2/src/main/java/com/clickhouse/client/api/Client.java

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -288,21 +288,33 @@ public Builder() {
288288
* <ul>
289289
* <li>{@code http://localhost:8123}</li>
290290
* <li>{@code https://localhost:8443}</li>
291+
* <li>{@code http://localhost:8123/clickhouse} (with path for reverse proxy scenarios)</li>
291292
* </ul>
292293
*
293-
* @param endpoint - URL formatted string with protocol, host and port.
294+
* @param endpoint - URL formatted string with protocol, host, port, and optional path.
294295
*/
295296
public Builder addEndpoint(String endpoint) {
296297
try {
297298
URL endpointURL = new URL(endpoint);
298299

299-
if (endpointURL.getProtocol().equalsIgnoreCase("https")) {
300-
addEndpoint(Protocol.HTTP, endpointURL.getHost(), endpointURL.getPort(), true);
301-
} else if (endpointURL.getProtocol().equalsIgnoreCase("http")) {
302-
addEndpoint(Protocol.HTTP, endpointURL.getHost(), endpointURL.getPort(), false);
303-
} else {
300+
if (!endpointURL.getProtocol().equalsIgnoreCase("https") &&
301+
!endpointURL.getProtocol().equalsIgnoreCase("http")) {
304302
throw new IllegalArgumentException("Only HTTP and HTTPS protocols are supported");
305303
}
304+
305+
// Build endpoint URL preserving the path but ignoring query parameters
306+
StringBuilder sb = new StringBuilder();
307+
sb.append(endpointURL.getProtocol().toLowerCase());
308+
sb.append("://");
309+
sb.append(endpointURL.getHost());
310+
if (endpointURL.getPort() > 0) {
311+
sb.append(":").append(endpointURL.getPort());
312+
}
313+
String path = endpointURL.getPath();
314+
if (path != null && !path.isEmpty()) {
315+
sb.append(path);
316+
}
317+
this.endpoints.add(sb.toString());
306318
} catch (MalformedURLException e) {
307319
throw new IllegalArgumentException("Endpoint should be a valid URL string, but was " + endpoint, e);
308320
}

client-v2/src/test/java/com/clickhouse/client/HttpTransportTests.java

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,6 +1120,71 @@ public void testSNIWithCloud() throws Exception {
11201120
}
11211121
}
11221122

1123+
@Test(groups = {"integration"})
1124+
public void testEndpointUrlPathIsPreserved() throws Exception {
1125+
if (isCloud()) {
1126+
return; // mocked server
1127+
}
1128+
1129+
int serverPort = new Random().nextInt(1000) + 10000;
1130+
WireMockServer mockServer = new WireMockServer(WireMockConfiguration
1131+
.options().port(serverPort)
1132+
.notifier(new Slf4jNotifier(true)));
1133+
mockServer.start();
1134+
1135+
try {
1136+
// Setup stubs for two virtual ClickHouse instances behind a reverse proxy
1137+
mockServer.addStubMapping(WireMock.post(WireMock.urlPathEqualTo("/sales/db"))
1138+
.willReturn(WireMock.aResponse()
1139+
.withStatus(HttpStatus.SC_OK)
1140+
.withHeader("X-ClickHouse-Summary",
1141+
"{ \"read_bytes\": \"100\", \"read_rows\": \"10\"}")).build());
1142+
1143+
mockServer.addStubMapping(WireMock.post(WireMock.urlPathEqualTo("/billing/db"))
1144+
.willReturn(WireMock.aResponse()
1145+
.withStatus(HttpStatus.SC_OK)
1146+
.withHeader("X-ClickHouse-Summary",
1147+
"{ \"read_bytes\": \"200\", \"read_rows\": \"20\"}")).build());
1148+
1149+
// Test sales virtual instance
1150+
try (Client salesClient = new Client.Builder()
1151+
.addEndpoint("http://localhost:" + serverPort + "/sales/db")
1152+
.setUsername("default")
1153+
.setPassword(ClickHouseServerForTest.getPassword())
1154+
.compressServerResponse(false)
1155+
.build()) {
1156+
1157+
try (QueryResponse response = salesClient.query("SELECT 1").get(10, TimeUnit.SECONDS)) {
1158+
Assert.assertEquals(response.getReadBytes(), 100);
1159+
}
1160+
}
1161+
1162+
// Test billing virtual instance - also verify query parameters in URL are ignored
1163+
try (Client billingClient = new Client.Builder()
1164+
.addEndpoint("http://localhost:" + serverPort + "/billing/db?ignored_param=value")
1165+
.setUsername("default")
1166+
.setPassword(ClickHouseServerForTest.getPassword())
1167+
.compressServerResponse(false)
1168+
.build()) {
1169+
1170+
try (QueryResponse response = billingClient.query("SELECT 1").get(10, TimeUnit.SECONDS)) {
1171+
Assert.assertEquals(response.getReadBytes(), 200);
1172+
}
1173+
1174+
// Verify that ignored_param is not in the request URL
1175+
mockServer.verify(WireMock.postRequestedFor(WireMock.urlPathEqualTo("/billing/db"))
1176+
.withoutQueryParam("ignored_param"));
1177+
}
1178+
1179+
// Verify requests were made to the correct paths
1180+
mockServer.verify(WireMock.postRequestedFor(WireMock.urlPathEqualTo("/sales/db")));
1181+
mockServer.verify(WireMock.postRequestedFor(WireMock.urlPathEqualTo("/billing/db")));
1182+
1183+
} finally {
1184+
mockServer.stop();
1185+
}
1186+
}
1187+
11231188
protected Client.Builder newClient() {
11241189
ClickHouseNode node = getServer(ClickHouseProtocol.HTTP);
11251190
boolean isSecure = isCloud();

0 commit comments

Comments
 (0)