From 7b2d5c1d90da11a5c0f4085dd89029a32449f1c3 Mon Sep 17 00:00:00 2001
From: Sergey Chernov
Date: Wed, 24 Jun 2026 15:40:33 -0700
Subject: [PATCH] separated request creation and execution
---
.../com/clickhouse/client/api/Client.java | 134 ++++++++--------
.../client/api/insert/InsertResponse.java | 10 +-
.../client/api/internal/ClientUtils.java | 15 ++
.../api/internal/HttpAPIClientHelper.java | 146 ++++++++++++++----
.../client/api/query/QueryResponse.java | 44 +++---
.../transport/internal/TransportRequest.java | 36 +++++
.../transport/internal/TransportResponse.java | 60 +++++++
7 files changed, 312 insertions(+), 133 deletions(-)
create mode 100644 client-v2/src/main/java/com/clickhouse/client/api/transport/internal/TransportRequest.java
create mode 100644 client-v2/src/main/java/com/clickhouse/client/api/transport/internal/TransportResponse.java
diff --git a/client-v2/src/main/java/com/clickhouse/client/api/Client.java b/client-v2/src/main/java/com/clickhouse/client/api/Client.java
index da157cb9d..742d757a1 100644
--- a/client-v2/src/main/java/com/clickhouse/client/api/Client.java
+++ b/client-v2/src/main/java/com/clickhouse/client/api/Client.java
@@ -16,6 +16,7 @@
import com.clickhouse.client.api.insert.InsertResponse;
import com.clickhouse.client.api.insert.InsertSettings;
import com.clickhouse.client.api.internal.ClientStatisticsHolder;
+import com.clickhouse.client.api.internal.ClientUtils;
import com.clickhouse.client.api.internal.CredentialsManager;
import com.clickhouse.client.api.internal.HttpAPIClientHelper;
import com.clickhouse.client.api.internal.MapUtils;
@@ -36,6 +37,8 @@
import com.clickhouse.client.api.serde.POJOSerDe;
import com.clickhouse.client.api.transport.Endpoint;
import com.clickhouse.client.api.transport.HttpEndpoint;
+import com.clickhouse.client.api.transport.internal.TransportRequest;
+import com.clickhouse.client.api.transport.internal.TransportResponse;
import com.clickhouse.client.config.ClickHouseClientOption;
import com.clickhouse.data.ClickHouseColumn;
import com.clickhouse.data.ClickHouseDataType;
@@ -43,8 +46,6 @@
import com.google.common.collect.ImmutableList;
import net.jpountz.lz4.LZ4Factory;
import org.apache.hc.core5.concurrent.DefaultThreadFactory;
-import org.apache.hc.core5.http.ClassicHttpResponse;
-import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -1319,43 +1320,37 @@ public CompletableFuture insert(String tableName, List> data,
RuntimeException lastException = null;
for (int i = 0; i <= maxRetries; i++) {
// Execute request
- try (ClassicHttpResponse httpResponse =
- httpClientHelper.executeRequest(selectedEndpoint, requestSettings.getAllSettings(),
- out -> {
- out.write("INSERT INTO ".getBytes());
- out.write(tableName.getBytes());
- out.write(" \n FORMAT ".getBytes());
- out.write(format.name().getBytes());
- out.write(" \n".getBytes());
- for (Object obj : data) {
-
- for (POJOFieldSerializer serializer : serializersForTable) {
- try {
- serializer.serialize(obj, out);
- } catch (InvocationTargetException | IllegalAccessException | IOException e) {
- throw new DataSerializationException(obj, serializer, e);
- }
- }
+ TransportRequest transportRequest = httpClientHelper.createRequest(selectedEndpoint, requestSettings.getAllSettings(),
+ out -> {
+ out.write("INSERT INTO ".getBytes());
+ out.write(tableName.getBytes());
+ out.write(" \n FORMAT ".getBytes());
+ out.write(format.name().getBytes());
+ out.write(" \n".getBytes());
+ for (Object obj : data) {
+
+ for (POJOFieldSerializer serializer : serializersForTable) {
+ try {
+ serializer.serialize(obj, out);
+ } catch (InvocationTargetException | IllegalAccessException | IOException e) {
+ throw new DataSerializationException(obj, serializer, e);
}
- out.close();
- })) {
-
-
+ }
+ }
+ out.close();
+ });
+ try (TransportResponse httpResponse = httpClientHelper.executeRequest(transportRequest)) {
// Check response
- if (httpResponse.getCode() == HttpStatus.SC_SERVICE_UNAVAILABLE) {
- LOG.warn("Failed to get response. Server returned {}. Retrying. (Duration: {})", httpResponse.getCode(), durationSince(startTime));
+ if (httpResponse.getStatusCode() == HttpStatus.SC_SERVICE_UNAVAILABLE) {
+ LOG.warn("Failed to get response. Server returned {}. Retrying. (Duration: {})", httpResponse.getStatusCode(), durationSince(startTime));
selectedEndpoint = getNextAliveNode();
continue;
}
ClientStatisticsHolder clientStats = globalClientStats.remove(operationId);
- OperationMetrics metrics = new OperationMetrics(clientStats);
- String summary = HttpAPIClientHelper.getHeaderVal(httpResponse.getFirstHeader(ClickHouseHttpProto.HEADER_SRV_SUMMARY), "{}");
- ProcessParser.parseSummary(summary, metrics);
- String queryId = HttpAPIClientHelper.getHeaderVal(httpResponse.getFirstHeader(ClickHouseHttpProto.HEADER_QUERY_ID), requestSettings.getQueryId(), String::valueOf);
- metrics.operationComplete();
- metrics.setQueryId(queryId);
- return new InsertResponse(metrics, HttpAPIClientHelper.collectResponseHeaders(httpResponse));
+ OperationMetrics metrics = completeOperation(httpResponse, clientStats, requestSettings.getQueryId());
+
+ return new InsertResponse(httpResponse, metrics);
} catch (Exception e) {
String msg = requestExMsg("Insert", (i + 1), durationSince(startTime).toMillis(), requestSettings.getQueryId());
lastException = httpClientHelper.wrapException(msg, e, requestSettings.getQueryId());
@@ -1373,7 +1368,6 @@ public CompletableFuture insert(String tableName, List> data,
throw (lastException == null ? new ClientException(errMsg) : lastException); };
return runAsyncOperation(supplier, requestSettings.getAllSettings());
-
}
/**
@@ -1509,7 +1503,7 @@ public CompletableFuture insert(String tableName,
clientStats.start(ClientMetrics.OP_DURATION);
final ClientStatisticsHolder finalClientStats = clientStats;
- Supplier responseSupplier;
+
final int writeBufferSize = requestSettings.getInputStreamCopyBufferSize() <= 0 ?
(int) configuration.get(ClientConfigProperties.CLIENT_NETWORK_BUFFER_SIZE.getKey()) :
@@ -1533,7 +1527,8 @@ public CompletableFuture insert(String tableName,
if (requestSettings.getQueryId() == null && queryIdGenerator != null) {
requestSettings.setQueryId(queryIdGenerator.get());
}
- responseSupplier = () -> {
+
+ Supplier responseSupplier = () -> {
long startTime = System.nanoTime();
// Selecting some node
Endpoint selectedEndpoint = getNextAliveNode();
@@ -1541,28 +1536,23 @@ public CompletableFuture insert(String tableName,
RuntimeException lastException = null;
for (int i = 0; i <= retries; i++) {
// Execute request
- try (ClassicHttpResponse httpResponse =
- httpClientHelper.executeRequest(selectedEndpoint, requestSettings.getAllSettings(),
- out -> {
- writer.onOutput(out);
- out.close();
- })) {
+ TransportRequest transportRequest = httpClientHelper.createRequest(selectedEndpoint, requestSettings.getAllSettings(),
+ out -> {
+ writer.onOutput(out);
+ out.close();
+ });
+ try (TransportResponse httpResponse = httpClientHelper.executeRequest(transportRequest)) {
// Check response
- if (httpResponse.getCode() == HttpStatus.SC_SERVICE_UNAVAILABLE) {
- LOG.warn("Failed to get response. Server returned {}. Retrying. (Duration: {})", httpResponse.getCode(), durationSince(startTime));
+ if (httpResponse.getStatusCode() == HttpStatus.SC_SERVICE_UNAVAILABLE) {
+ LOG.warn("Failed to get response. Server returned {}. Retrying. (Duration: {})", httpResponse.getStatusCode(), durationSince(startTime));
selectedEndpoint = getNextAliveNode();
continue;
}
- OperationMetrics metrics = new OperationMetrics(finalClientStats);
- String summary = HttpAPIClientHelper.getHeaderVal(httpResponse.getFirstHeader(ClickHouseHttpProto.HEADER_SRV_SUMMARY), "{}");
- ProcessParser.parseSummary(summary, metrics);
- String queryId = HttpAPIClientHelper.getHeaderVal(httpResponse.getFirstHeader(ClickHouseHttpProto.HEADER_QUERY_ID), requestSettings.getQueryId(), String::valueOf);
- metrics.operationComplete();
- metrics.setQueryId(queryId);
- return new InsertResponse(metrics, HttpAPIClientHelper.collectResponseHeaders(httpResponse));
+ OperationMetrics metrics = completeOperation(httpResponse, finalClientStats, requestSettings.getQueryId());
+ return new InsertResponse(httpResponse, metrics);
} catch (Exception e) {
String msg = requestExMsg("Insert", (i + 1), durationSince(startTime).toMillis(), requestSettings.getQueryId());
lastException = httpClientHelper.wrapException(msg, e, requestSettings.getQueryId());
@@ -1668,38 +1658,28 @@ public CompletableFuture query(String sqlQuery, Map query(String sqlQuery, MapQueries data in one of descriptive format and creates a reader out of the response stream.
* Format is selected internally so is ignored when passed in settings. If query contains format
diff --git a/client-v2/src/main/java/com/clickhouse/client/api/insert/InsertResponse.java b/client-v2/src/main/java/com/clickhouse/client/api/insert/InsertResponse.java
index 2fc09f312..c83161725 100644
--- a/client-v2/src/main/java/com/clickhouse/client/api/insert/InsertResponse.java
+++ b/client-v2/src/main/java/com/clickhouse/client/api/insert/InsertResponse.java
@@ -3,21 +3,17 @@
import com.clickhouse.client.api.http.ClickHouseHttpProto;
import com.clickhouse.client.api.metrics.OperationMetrics;
import com.clickhouse.client.api.metrics.ServerMetrics;
+import com.clickhouse.client.api.transport.internal.TransportResponse;
-import java.util.Collections;
import java.util.Map;
public class InsertResponse implements AutoCloseable {
private OperationMetrics operationMetrics;
private final Map responseHeaders;
- public InsertResponse(OperationMetrics metrics) {
- this(metrics, Collections.emptyMap());
- }
-
- public InsertResponse(OperationMetrics metrics, Map responseHeaders) {
+ public InsertResponse(TransportResponse transportResponse, OperationMetrics metrics) {
this.operationMetrics = metrics;
- this.responseHeaders = responseHeaders;
+ this.responseHeaders = transportResponse.getHeaders();
}
@Override
diff --git a/client-v2/src/main/java/com/clickhouse/client/api/internal/ClientUtils.java b/client-v2/src/main/java/com/clickhouse/client/api/internal/ClientUtils.java
index 235d55cad..085c43181 100644
--- a/client-v2/src/main/java/com/clickhouse/client/api/internal/ClientUtils.java
+++ b/client-v2/src/main/java/com/clickhouse/client/api/internal/ClientUtils.java
@@ -1,5 +1,10 @@
package com.clickhouse.client.api.internal;
+import org.slf4j.Logger;
+
+import java.io.Closeable;
+import java.io.IOException;
+
/**
* Class containing utility methods used across the client.
*/
@@ -14,4 +19,14 @@ public static boolean isNotBlank(String str) {
public static boolean isBlank(String str) {
return str == null || str.trim().isEmpty();
}
+
+ public static void quiteClose(Closeable closeable, Logger log) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (Exception e) {
+ log.warn("Failed to close object " + closeable, e);
+ }
+ }
+ }
}
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 37fe4ab8c..89c31ac09 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
@@ -13,6 +13,8 @@
import com.clickhouse.client.api.enums.ProxyType;
import com.clickhouse.client.api.http.ClickHouseHttpProto;
import com.clickhouse.client.api.transport.Endpoint;
+import com.clickhouse.client.api.transport.internal.TransportRequest;
+import com.clickhouse.client.api.transport.internal.TransportResponse;
import com.clickhouse.client.config.ClickHouseDefaultSslContextProvider;
import com.clickhouse.data.ClickHouseFormat;
import net.jpountz.lz4.LZ4Factory;
@@ -44,6 +46,7 @@
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.NoHttpResponseException;
+import org.apache.hc.core5.http.ProtocolException;
import org.apache.hc.core5.http.URIScheme;
import org.apache.hc.core5.http.config.CharCodingConfig;
import org.apache.hc.core5.http.config.Http1Config;
@@ -513,9 +516,37 @@ private HttpPost createPostRequest(URI uri, Map requestConfig) {
return req;
}
- public ClassicHttpResponse executeRequest(Endpoint server, Map requestConfig,
- String body) throws Exception {
+ private static final class TransportRequestImpl implements TransportRequest {
+ private final HttpPost delegate;
+ private final Map config;
+ TransportRequestImpl(HttpPost delegate, Map config) {
+ this.delegate = delegate;
+ this.config = config;
+ }
+
+ @Override
+ public boolean cancel() throws Exception {
+ if (delegate.isCancelled()) {
+ return true;
+ }
+ return delegate.cancel();
+ }
+
+ @Override
+ public Map getConfig() {
+ return config;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public T getDelegate() {
+ return (T) delegate;
+ }
+ }
+
+ public TransportRequest createRequest(Endpoint server, Map requestConfig,
+ String body) {
boolean useMultipart = ClientConfigProperties.HTTP_SEND_PARAMS_IN_BODY.getOrDefault(requestConfig) &&
requestConfig.containsKey(HttpAPIClientHelper.KEY_STATEMENT_PARAMS);
@@ -538,27 +569,93 @@ public ClassicHttpResponse executeRequest(Endpoint server, Map r
req.setEntity(wrapRequestEntity(httpEntity, requestConfig));
} else {
- final String contentEncoding = req.containsHeader(HttpHeaders.CONTENT_ENCODING) ? req.getHeader(HttpHeaders.CONTENT_ENCODING).getValue() : null;
-
- HttpEntity httpEntity = new ByteArrayEntity(body.getBytes(StandardCharsets.UTF_8.name()), CONTENT_TYPE, contentEncoding);
+ final HttpEntity httpEntity;
+ try {
+ final String contentEncoding = req.containsHeader(HttpHeaders.CONTENT_ENCODING) ? req.getHeader(HttpHeaders.CONTENT_ENCODING).getValue() : null;
+ httpEntity = new ByteArrayEntity(body.getBytes(StandardCharsets.UTF_8.name()), CONTENT_TYPE, contentEncoding);
+ } catch (UnsupportedEncodingException | ProtocolException e) {
+ throw new ClientException("failed to create request body entity", e);
+ }
req.setEntity(wrapRequestEntity(httpEntity, requestConfig));
}
- // execute
- return doPostRequest(requestConfig, req);
+ return new TransportRequestImpl(req, requestConfig);
}
- public ClassicHttpResponse executeRequest(Endpoint server, Map requestConfig,
- IOCallback writeCallback) throws Exception {
+ private static final class TransportResponseImpl implements TransportResponse {
+
+ private final ClassicHttpResponse delegate;
+
+ TransportResponseImpl(ClassicHttpResponse delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public int getStatusCode() {
+ return 0;
+ }
+
+ @Override
+ public ClickHouseFormat getDataFormat() {
+ Header formatHeader = delegate.getFirstHeader(ClickHouseHttpProto.HEADER_FORMAT);
+ return formatHeader == null ? null : ClickHouseFormat.valueOf(formatHeader.getValue());
+ }
+
+ @Override
+ public String getSummaryJson() {
+ return HttpAPIClientHelper.getHeaderVal(delegate
+ .getFirstHeader(ClickHouseHttpProto.HEADER_SRV_SUMMARY), "{}");
+ }
+
+ @Override
+ public String getQueryId() {
+ return HttpAPIClientHelper.getHeaderVal(delegate
+ .getFirstHeader(ClickHouseHttpProto.HEADER_QUERY_ID), null);
+ }
+
+ @Override
+ public T getDelegate() {
+ return (T) delegate;
+ }
+
+ @Override
+ public Map getHeaders() {
+ return HttpAPIClientHelper.collectResponseHeaders(delegate);
+ }
+
+ @Override
+ public void close() throws IOException {
+ delegate.close();
+ }
+
+ @Override
+ public InputStream createDataInputStream() {
+ try {
+ return delegate.getEntity().getContent();
+ } catch (Exception e) {
+ throw new ClientException("Failed to construct input stream", e);
+ }
+ }
+ }
+
+ public TransportResponse executeRequest(TransportRequest transportRequest) throws Exception {
+ return new TransportResponseImpl(doPostRequest(transportRequest.getConfig(), transportRequest.getDelegate()));
+ }
+
+ public TransportRequest createRequest(Endpoint server, Map requestConfig, IOCallback writeCallback) {
final URI uri = createRequestURI(server, requestConfig, true);
final HttpPost req = createPostRequest(uri, requestConfig);
- String contentEncoding = req.containsHeader(HttpHeaders.CONTENT_ENCODING) ? req.getHeader(HttpHeaders.CONTENT_ENCODING).getValue() : null;
- req.setEntity(wrapRequestEntity(
- new EntityTemplate(-1, CONTENT_TYPE, contentEncoding , writeCallback),
- requestConfig));
+ try {
+ String contentEncoding = req.containsHeader(HttpHeaders.CONTENT_ENCODING) ? req.getHeader(HttpHeaders.CONTENT_ENCODING).getValue() : null;
+ req.setEntity(wrapRequestEntity(
+ new EntityTemplate(-1, CONTENT_TYPE, contentEncoding, writeCallback),
+ requestConfig));
+ } catch (ProtocolException e) {
+ throw new ClientException("failed to create request body entity", e);
+ }
- return doPostRequest(requestConfig, req);
+ return new TransportRequestImpl(req, requestConfig);
}
private ClassicHttpResponse doPostRequest(Map requestConfig, HttpPost req) throws Exception {
@@ -589,30 +686,20 @@ private ClassicHttpResponse doPostRequest(Map requestConfig, Htt
return httpResponse;
} catch (UnknownHostException e) {
- closeQuietly(httpResponse);
+ ClientUtils.quiteClose(httpResponse, LOG);
LOG.warn("Host '{}' unknown", req.getAuthority());
throw e;
} catch (ConnectException | NoRouteToHostException e) {
- closeQuietly(httpResponse);
+ ClientUtils.quiteClose(httpResponse, LOG);
LOG.warn("Failed to connect to '{}': {}", req.getAuthority(), e.getMessage());
throw e;
} catch (Exception e) {
- closeQuietly(httpResponse);
+ ClientUtils.quiteClose(httpResponse, LOG);
LOG.debug("Failed to execute request to '{}': {}", req.getAuthority(), e.getMessage(), e);
throw e;
}
}
- public static void closeQuietly(ClassicHttpResponse httpResponse) {
- if (httpResponse != null) {
- try {
- httpResponse.close();
- } catch (IOException e) {
- LOG.warn("Failed to close response");
- }
- }
- }
-
private static final ContentType CONTENT_TYPE = ContentType.create(ContentType.TEXT_PLAIN.getMimeType(), "UTF-8");
private void addHeaders(HttpPost req, Map requestConfig) {
@@ -804,7 +891,10 @@ public static int getHeaderInt(Header header, int defaultValue) {
ClickHouseHttpProto.HEADER_SRV_SUMMARY,
ClickHouseHttpProto.HEADER_SRV_DISPLAY_NAME,
ClickHouseHttpProto.HEADER_DATABASE,
- ClickHouseHttpProto.HEADER_DB_USER
+ ClickHouseHttpProto.HEADER_DB_USER,
+ ClickHouseHttpProto.HEADER_TIMEZONE,
+ ClickHouseHttpProto.HEADER_FORMAT,
+ ClickHouseHttpProto.HEADER_PROGRESS
));
/**
diff --git a/client-v2/src/main/java/com/clickhouse/client/api/query/QueryResponse.java b/client-v2/src/main/java/com/clickhouse/client/api/query/QueryResponse.java
index 6e237a18d..32c1d4dab 100644
--- a/client-v2/src/main/java/com/clickhouse/client/api/query/QueryResponse.java
+++ b/client-v2/src/main/java/com/clickhouse/client/api/query/QueryResponse.java
@@ -3,14 +3,14 @@
import com.clickhouse.client.api.ClientConfigProperties;
import com.clickhouse.client.api.ClientException;
import com.clickhouse.client.api.http.ClickHouseHttpProto;
+import com.clickhouse.client.api.internal.ClientUtils;
import com.clickhouse.client.api.metrics.OperationMetrics;
import com.clickhouse.client.api.metrics.ServerMetrics;
+import com.clickhouse.client.api.transport.internal.TransportResponse;
import com.clickhouse.data.ClickHouseFormat;
-import org.apache.hc.core5.http.ClassicHttpResponse;
-import org.apache.hc.core5.http.Header;
import java.io.InputStream;
-import java.util.Collections;
+import java.time.ZoneId;
import java.util.Map;
import java.util.TimeZone;
@@ -31,51 +31,43 @@ public class QueryResponse implements AutoCloseable {
private final ClickHouseFormat format;
- private QuerySettings settings;
+ private final QuerySettings settings;
- private OperationMetrics operationMetrics;
+ private final OperationMetrics operationMetrics;
- private ClassicHttpResponse httpResponse;
+ private final TransportResponse transportResponse;
private final Map responseHeaders;
- public QueryResponse(ClassicHttpResponse response, ClickHouseFormat format, QuerySettings settings,
- OperationMetrics operationMetrics) {
- this(response, format, settings, operationMetrics, Collections.emptyMap());
- }
-
- public QueryResponse(ClassicHttpResponse response, ClickHouseFormat format, QuerySettings settings,
- OperationMetrics operationMetrics, Map responseHeaders) {
- this.httpResponse = response;
+ public QueryResponse(TransportResponse response, ClickHouseFormat format, QuerySettings settings, OperationMetrics operationMetrics) {
+ this.transportResponse = response;
this.format = format;
this.operationMetrics = operationMetrics;
this.settings = settings;
- this.responseHeaders = responseHeaders;
+ this.responseHeaders = response.getHeaders();
- Header tzHeader = response.getFirstHeader(ClickHouseHttpProto.HEADER_TIMEZONE);
- if (tzHeader != null) {
+ String timeZoneHeader = responseHeaders.get(ClickHouseHttpProto.HEADER_TIMEZONE);
+ if (timeZoneHeader != null) {
+ TimeZone serverTz;
try {
- this.settings.setOption(ClientConfigProperties.SERVER_TIMEZONE.getKey(),
- TimeZone.getTimeZone(tzHeader.getValue()));
+ serverTz = TimeZone.getTimeZone(timeZoneHeader);
} catch (Exception e) {
throw new ClientException("Failed to parse server timezone", e);
}
+ this.settings.setOption(ClientConfigProperties.SERVER_TIMEZONE.getKey(),
+ serverTz);
}
}
public InputStream getInputStream() {
- try {
- return httpResponse.getEntity().getContent();
- } catch (Exception e) {
- throw new ClientException("Failed to construct input stream", e);
- }
+ return transportResponse.createDataInputStream();
}
@Override
public void close() throws Exception {
- if (httpResponse != null ) {
+ if (transportResponse != null ) {
try {
- httpResponse.close();
+ transportResponse.close();
} catch (Exception e) {
throw new ClientException("Failed to close response", e);
}
diff --git a/client-v2/src/main/java/com/clickhouse/client/api/transport/internal/TransportRequest.java b/client-v2/src/main/java/com/clickhouse/client/api/transport/internal/TransportRequest.java
new file mode 100644
index 000000000..6a1a66401
--- /dev/null
+++ b/client-v2/src/main/java/com/clickhouse/client/api/transport/internal/TransportRequest.java
@@ -0,0 +1,36 @@
+package com.clickhouse.client.api.transport.internal;
+
+import java.util.Map;
+
+public interface TransportRequest {
+
+
+ /**
+ * Gives access to transport delegate. Used strictly only by transport.
+ * @return internal transport request object
+ * @param - Type of delegate
+ */
+ T getDelegate();
+
+ /**
+ * Returns reference to request configuration. Implementation should
+ * store only copy because configuration map is created for each request separately
+ * @return request configuration map
+ */
+ Map getConfig();
+
+ /**
+ * Cancels request associated with the object. Implementation should
+ * treat this method like close() and release all resource.
+ * When request is canceled it cannot be reused. All reusable objects
+ * should be saved elsewhere.
+ * In many cases cancellation of IO operation is problematic and result
+ * of this method should not be used in core logic.
+ * Operation is idempotent and can be called on canceled request multiple
+ * times.
+ *
+ * @return result of operation. True if request was canceled for sure. False when result cannot be known.
+ * @throws Exception - when something extraordinary happens while canceling the request.
+ */
+ boolean cancel() throws Exception;
+}
diff --git a/client-v2/src/main/java/com/clickhouse/client/api/transport/internal/TransportResponse.java b/client-v2/src/main/java/com/clickhouse/client/api/transport/internal/TransportResponse.java
new file mode 100644
index 000000000..32fe89465
--- /dev/null
+++ b/client-v2/src/main/java/com/clickhouse/client/api/transport/internal/TransportResponse.java
@@ -0,0 +1,60 @@
+package com.clickhouse.client.api.transport.internal;
+
+import com.clickhouse.data.ClickHouseFormat;
+
+import java.io.Closeable;
+import java.io.InputStream;
+import java.util.Map;
+
+public interface TransportResponse extends Closeable {
+
+ /**
+ * Transport status code translated to one of values:
+ *
+ * - 503 - for service unavailable
+ * - 500 - server error
+ * - 400 - user error
+ * - 404 - endpoint not found
+ * - 403 - access not granted
+ * - 401 - no authentication information
+ * - 200 - ok
+ *
+ * @return integer value of status code
+ */
+ int getStatusCode();
+
+
+ /**
+ * Data format returned by server or calculated other way
+ * @return data format
+ */
+ ClickHouseFormat getDataFormat();
+
+ String getSummaryJson();
+
+ String getQueryId();
+
+ /**
+ * Gives access to transport delegate. Used strictly only by transport.
+ * @return internal transport response object
+ * @param - Type of delegate
+ */
+ T getDelegate();
+
+
+ /**
+ * Server headers.
+ * @return response headers
+ */
+ Map getHeaders();
+
+
+ /**
+ * Creates a new stream to read data. It is applicable only for
+ * blocking transports. In real life this should be called once.
+ * It is important to mention that each time new input stream is created.
+ *
+ * @return new data stream
+ */
+ InputStream createDataInputStream();
+}