11package com .clickhouse .client .api .internal ;
22
33import com .clickhouse .client .ClickHouseSslContextProvider ;
4- import com .clickhouse .client .api .*;
4+ import com .clickhouse .client .api .ClickHouseException ;
5+ import com .clickhouse .client .api .Client ;
6+ import com .clickhouse .client .api .ClientConfigProperties ;
7+ import com .clickhouse .client .api .ClientException ;
8+ import com .clickhouse .client .api .ClientFaultCause ;
9+ import com .clickhouse .client .api .ClientMisconfigurationException ;
10+ import com .clickhouse .client .api .ConnectionInitiationException ;
11+ import com .clickhouse .client .api .ConnectionReuseStrategy ;
12+ import com .clickhouse .client .api .DataTransferException ;
13+ import com .clickhouse .client .api .ServerException ;
514import com .clickhouse .client .api .enums .ProxyType ;
615import com .clickhouse .client .api .http .ClickHouseHttpProto ;
716import com .clickhouse .client .api .transport .Endpoint ;
6372import java .net .SocketTimeoutException ;
6473import java .net .URI ;
6574import java .net .URISyntaxException ;
75+ import java .net .URLEncoder ;
6676import java .net .UnknownHostException ;
6777import java .nio .charset .StandardCharsets ;
6878import java .security .NoSuchAlgorithmException ;
7989import java .util .concurrent .TimeUnit ;
8090import java .util .concurrent .atomic .AtomicLong ;
8191import java .util .function .Function ;
92+ import java .util .regex .Pattern ;
8293
8394public class HttpAPIClientHelper {
8495 private static final Logger LOG = LoggerFactory .getLogger (Client .class );
8596
8697 private static final int ERROR_BODY_BUFFER_SIZE = 1024 ; // Error messages are usually small
8798
99+ private static final Pattern PATTERN_HEADER_VALUE_ASCII = Pattern .compile (
100+ "\\ p{Graph}+(?:[ ]\\ p{Graph}+)*" );
101+
88102 private final CloseableHttpClient httpClient ;
89103
90104 private final RequestConfig baseRequestConfig ;
@@ -287,7 +301,7 @@ public CloseableHttpClient createHttpClient(boolean initSslContext, Map<String,
287301 SocketConfig socketConfig = soCfgBuilder .build ();
288302
289303 // Connection manager
290- if (ClientConfigProperties .CONNECTION_POOL_ENABLED .getOrDefault (configuration )) {
304+ if (ClientConfigProperties .CONNECTION_POOL_ENABLED .< Boolean > getOrDefault (configuration )) {
291305 clientBuilder .setConnectionManager (poolConnectionManager (sslConnectionSocketFactory , socketConfig , configuration ));
292306 } else {
293307 clientBuilder .setConnectionManager (basicConnectionManager (sslConnectionSocketFactory , socketConfig , configuration ));
@@ -430,36 +444,55 @@ public ClassicHttpResponse executeRequest(Endpoint server, Map<String, Object> r
430444 private static final ContentType CONTENT_TYPE = ContentType .create (ContentType .TEXT_PLAIN .getMimeType (), "UTF-8" );
431445
432446 private void addHeaders (HttpPost req , Map <String , Object > requestConfig ) {
433- req . addHeader (HttpHeaders .CONTENT_TYPE , CONTENT_TYPE .getMimeType ());
447+ addHeader (req , HttpHeaders .CONTENT_TYPE , CONTENT_TYPE .getMimeType ());
434448 if (requestConfig .containsKey (ClientConfigProperties .INPUT_OUTPUT_FORMAT .getKey ())) {
435- req .addHeader (ClickHouseHttpProto .HEADER_FORMAT , requestConfig .get (ClientConfigProperties .INPUT_OUTPUT_FORMAT .getKey ()));
449+ addHeader (
450+ req ,
451+ ClickHouseHttpProto .HEADER_FORMAT ,
452+ requestConfig .get (ClientConfigProperties .INPUT_OUTPUT_FORMAT .getKey ()));
436453 }
437-
438454 if (requestConfig .containsKey (ClientConfigProperties .QUERY_ID .getKey ())) {
439- req .addHeader (ClickHouseHttpProto .HEADER_QUERY_ID , requestConfig .get (ClientConfigProperties .QUERY_ID .getKey ()).toString ());
440- }
441-
442-
443- req .addHeader (ClickHouseHttpProto .HEADER_DATABASE , ClientConfigProperties .DATABASE .getOrDefault (requestConfig ));
444-
445-
446- if (ClientConfigProperties .SSL_AUTH .getOrDefault (requestConfig )) {
447- req .addHeader (ClickHouseHttpProto .HEADER_DB_USER , ClientConfigProperties .USER .getOrDefault (requestConfig ));
448- req .addHeader (ClickHouseHttpProto .HEADER_SSL_CERT_AUTH , "on" );
449- } else if (ClientConfigProperties .HTTP_USE_BASIC_AUTH .getOrDefault (requestConfig )) {
455+ addHeader (
456+ req ,
457+ ClickHouseHttpProto .HEADER_QUERY_ID ,
458+ requestConfig .get (ClientConfigProperties .QUERY_ID .getKey ()));
459+ }
460+ addHeader (
461+ req ,
462+ ClickHouseHttpProto .HEADER_DATABASE ,
463+ ClientConfigProperties .DATABASE .getOrDefault (requestConfig ));
464+
465+ if (ClientConfigProperties .SSL_AUTH .<Boolean >getOrDefault (requestConfig ).booleanValue ()) {
466+ addHeader (
467+ req ,
468+ ClickHouseHttpProto .HEADER_DB_USER ,
469+ ClientConfigProperties .USER .getOrDefault (requestConfig ));
470+ addHeader (
471+ req ,
472+ ClickHouseHttpProto .HEADER_SSL_CERT_AUTH ,
473+ "on" );
474+ } else if (ClientConfigProperties .HTTP_USE_BASIC_AUTH .<Boolean >getOrDefault (requestConfig ).booleanValue ()) {
450475 String user = ClientConfigProperties .USER .getOrDefault (requestConfig );
451476 String password = ClientConfigProperties .PASSWORD .getOrDefault (requestConfig );
452-
453- req .addHeader (HttpHeaders .AUTHORIZATION , "Basic " + Base64 .getEncoder ().encodeToString (
454- (user + ":" + password ).getBytes (StandardCharsets .UTF_8 ))
455- );
477+ // Use as-is, no encoding allowed
478+ req .addHeader (
479+ HttpHeaders .AUTHORIZATION ,
480+ "Basic " + Base64 .getEncoder ().encodeToString (
481+ (user + ":" + password ).getBytes (StandardCharsets .UTF_8 )));
456482 } else {
457- req .addHeader (ClickHouseHttpProto .HEADER_DB_USER , ClientConfigProperties .USER .getOrDefault (requestConfig ));
458- req .addHeader (ClickHouseHttpProto .HEADER_DB_PASSWORD , ClientConfigProperties .PASSWORD .getOrDefault (requestConfig ));
459-
483+ addHeader (
484+ req ,
485+ ClickHouseHttpProto .HEADER_DB_USER ,
486+ ClientConfigProperties .USER .getOrDefault (requestConfig ));
487+ addHeader (
488+ req ,
489+ ClickHouseHttpProto .HEADER_DB_PASSWORD ,
490+ ClientConfigProperties .PASSWORD .getOrDefault (requestConfig ));
460491 }
461492 if (proxyAuthHeaderValue != null ) {
462- req .addHeader (HttpHeaders .PROXY_AUTHORIZATION , proxyAuthHeaderValue );
493+ req .addHeader (
494+ HttpHeaders .PROXY_AUTHORIZATION ,
495+ proxyAuthHeaderValue );
463496 }
464497
465498 boolean clientCompression = ClientConfigProperties .COMPRESS_CLIENT_REQUEST .getOrDefault (requestConfig );
@@ -469,26 +502,30 @@ private void addHeaders(HttpPost req, Map<String, Object> requestConfig) {
469502
470503 if (useHttpCompression ) {
471504 if (serverCompression ) {
472- req . addHeader (HttpHeaders .ACCEPT_ENCODING , "lz4" );
505+ addHeader (req , HttpHeaders .ACCEPT_ENCODING , "lz4" );
473506 }
474507 if (clientCompression && !appCompressedData ) {
475- req . addHeader (HttpHeaders .CONTENT_ENCODING , "lz4" );
508+ addHeader (req , HttpHeaders .CONTENT_ENCODING , "lz4" );
476509 }
477510 }
478511
479512 for (String key : requestConfig .keySet ()) {
480513 if (key .startsWith (ClientConfigProperties .HTTP_HEADER_PREFIX )) {
481514 Object val = requestConfig .get (key );
482515 if (val != null ) {
483- req .setHeader (key .substring (ClientConfigProperties .HTTP_HEADER_PREFIX .length ()), String .valueOf (val ));
516+ addHeader (
517+ req ,
518+ key .substring (ClientConfigProperties .HTTP_HEADER_PREFIX .length ()),
519+ String .valueOf (val ));
484520 }
485521 }
486522 }
487523
488-
489524 // Special cases
490- if (req .containsHeader (HttpHeaders .AUTHORIZATION ) && (req .containsHeader (ClickHouseHttpProto .HEADER_DB_USER ) ||
491- req .containsHeader (ClickHouseHttpProto .HEADER_DB_PASSWORD ))) {
525+ if (req .containsHeader (HttpHeaders .AUTHORIZATION )
526+ && (req .containsHeader (ClickHouseHttpProto .HEADER_DB_USER ) ||
527+ req .containsHeader (ClickHouseHttpProto .HEADER_DB_PASSWORD )))
528+ {
492529 // user has set auth header for purpose, lets remove ours
493530 req .removeHeaders (ClickHouseHttpProto .HEADER_DB_USER );
494531 req .removeHeaders (ClickHouseHttpProto .HEADER_DB_PASSWORD );
@@ -668,7 +705,6 @@ private void correctUserAgentHeader(HttpRequest request, Map<String, Object> req
668705 } else if (userAgentHeader != null ) {
669706 userAgentValue = userAgentHeader .getValue () + " " + defaultUserAgent ;
670707 }
671-
672708 request .setHeader (HttpHeaders .USER_AGENT , userAgentValue );
673709 }
674710
@@ -720,6 +756,25 @@ public void close() {
720756 httpClient .close (CloseMode .IMMEDIATE );
721757 }
722758
759+ private static <T > void addHeader (HttpRequest req , String headerName ,
760+ T value )
761+ {
762+ if (value == null ) {
763+ return ;
764+ }
765+ String tString = value .toString ();
766+ if (tString .isBlank ()) {
767+ return ;
768+ }
769+ if (PATTERN_HEADER_VALUE_ASCII .matcher (tString ).matches ()) {
770+ req .addHeader (headerName , tString );
771+ } else {
772+ req .addHeader (
773+ headerName + "*" ,
774+ "UTF-8''" + URLEncoder .encode (tString , StandardCharsets .UTF_8 ));
775+ }
776+ }
777+
723778 /**
724779 * This factory is used only when no ssl connections are required (no https endpoints).
725780 * Internally http client would create factory and spend time if no supplied.
0 commit comments