diff --git a/client-v2/src/main/java/com/clickhouse/client/api/http/ClickHouseHttpProto.java b/client-v2/src/main/java/com/clickhouse/client/api/http/ClickHouseHttpProto.java index 8075ede61..f747b7568 100644 --- a/client-v2/src/main/java/com/clickhouse/client/api/http/ClickHouseHttpProto.java +++ b/client-v2/src/main/java/com/clickhouse/client/api/http/ClickHouseHttpProto.java @@ -1,5 +1,9 @@ package com.clickhouse.client.api.http; +import com.google.common.collect.ImmutableSet; + +import java.util.Set; + public class ClickHouseHttpProto { @@ -52,6 +56,22 @@ public class ClickHouseHttpProto { public static final String HEADER_SSL_CERT_AUTH = "x-clickhouse-ssl-certificate-auth"; + /** Server error tag header */ + public static final String HEADER_ERROR_TAG = "X-ClickHouse-Exception-Tag"; + + public static final Set RESPONSE_HEADER_WHITELIST; + + static { + RESPONSE_HEADER_WHITELIST = ImmutableSet.builder() + .add(ClickHouseHttpProto.HEADER_QUERY_ID) + .add(ClickHouseHttpProto.HEADER_SRV_SUMMARY) + .add(ClickHouseHttpProto.HEADER_SRV_DISPLAY_NAME) + .add(ClickHouseHttpProto.HEADER_DATABASE) + .add(ClickHouseHttpProto.HEADER_DB_USER) + .add(ClickHouseHttpProto.HEADER_ERROR_TAG) + .build(); + } + /** * Query parameter to specify the query ID. */ diff --git a/client-v2/src/main/java/com/clickhouse/client/api/internal/HttpAPIClientHelper.java b/client-v2/src/main/java/com/clickhouse/client/api/internal/HttpAPIClientHelper.java index 76e2dec93..4e37010d8 100644 --- a/client-v2/src/main/java/com/clickhouse/client/api/internal/HttpAPIClientHelper.java +++ b/client-v2/src/main/java/com/clickhouse/client/api/internal/HttpAPIClientHelper.java @@ -754,14 +754,6 @@ public static int getHeaderInt(Header header, int defaultValue) { return getHeaderVal(header, defaultValue, Integer::parseInt); } - private static final Set RESPONSE_HEADER_WHITELIST = new HashSet<>(Arrays.asList( - ClickHouseHttpProto.HEADER_QUERY_ID, - ClickHouseHttpProto.HEADER_SRV_SUMMARY, - ClickHouseHttpProto.HEADER_SRV_DISPLAY_NAME, - ClickHouseHttpProto.HEADER_DATABASE, - ClickHouseHttpProto.HEADER_DB_USER - )); - /** * Collects whitelisted response headers from an HTTP response into a map. * @@ -770,7 +762,7 @@ public static int getHeaderInt(Header header, int defaultValue) { */ public static Map collectResponseHeaders(ClassicHttpResponse response) { Map headers = new HashMap<>(); - for (String name : RESPONSE_HEADER_WHITELIST) { + for (String name : ClickHouseHttpProto.RESPONSE_HEADER_WHITELIST) { Header header = response.getFirstHeader(name); if (header != null) { headers.put(name, header.getValue()); diff --git a/client-v2/src/test/java/com/clickhouse/client/HttpTransportTests.java b/client-v2/src/test/java/com/clickhouse/client/HttpTransportTests.java index fc2d13a86..6e3052db3 100644 --- a/client-v2/src/test/java/com/clickhouse/client/HttpTransportTests.java +++ b/client-v2/src/test/java/com/clickhouse/client/HttpTransportTests.java @@ -789,6 +789,35 @@ public void testErrorWithSendProgressHeaders() throws Exception { } } + @Test(groups = { "integration" }) + public void testInvalidSqlAfterHeadersSent() throws Exception { + if (isCloud()) { + return; // mocked server + } + + ClickHouseNode server = getServer(ClickHouseProtocol.HTTP); + try (Client client = new Client.Builder().addEndpoint(Protocol.HTTP, "localhost", server.getPort(), false) + .setUsername("default") + .setPassword(ClickHouseServerForTest.getPassword()) + .build()) { + + // Force delayed server-side failure after the query starts producing progress/headers. + QuerySettings settings = new QuerySettings() + .serverSetting("send_progress_in_http_headers", "1") + .serverSetting("max_result_rows", "1000000") + .serverSetting("result_overflow_mode", "throw") + .serverSetting("max_block_size", "1"); + + try (QueryResponse ignored = client.query("SELECT number FROM system.numbers", settings).get()) { + Assert.fail("Expected exception"); + } catch (ServerException e) { + Assert.assertEquals(e.getCode(), 396); + Assert.assertTrue(e.getMessage().contains("Limit for rows or bytes exceeded"), + "Unexpected error message: " + e.getMessage()); + } + } + } + @Test(groups = { "integration" }, dataProvider = "testUserAgentHasCompleteProductName_dataProvider", dataProviderClass = HttpTransportTests.class) public void testUserAgentHasCompleteProductName(String clientName, Pattern userAgentPattern) throws Exception {