1
1
package com .clickhouse .client .api .internal ;
2
2
3
3
import 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 ;
5
14
import com .clickhouse .client .api .enums .ProxyType ;
6
15
import com .clickhouse .client .api .http .ClickHouseHttpProto ;
7
16
import com .clickhouse .client .api .transport .Endpoint ;
63
72
import java .net .SocketTimeoutException ;
64
73
import java .net .URI ;
65
74
import java .net .URISyntaxException ;
75
+ import java .net .URLEncoder ;
66
76
import java .net .UnknownHostException ;
67
77
import java .nio .charset .StandardCharsets ;
68
78
import java .security .NoSuchAlgorithmException ;
79
89
import java .util .concurrent .TimeUnit ;
80
90
import java .util .concurrent .atomic .AtomicLong ;
81
91
import java .util .function .Function ;
92
+ import java .util .regex .Pattern ;
82
93
83
94
public class HttpAPIClientHelper {
84
95
private static final Logger LOG = LoggerFactory .getLogger (Client .class );
85
96
86
97
private static final int ERROR_BODY_BUFFER_SIZE = 1024 ; // Error messages are usually small
87
98
99
+ private static final Pattern PATTERN_HEADER_VALUE_ASCII = Pattern .compile (
100
+ "\\ p{Graph}+(?:[ ]\\ p{Graph}+)*" );
101
+
88
102
private final CloseableHttpClient httpClient ;
89
103
90
104
private final RequestConfig baseRequestConfig ;
@@ -287,7 +301,7 @@ public CloseableHttpClient createHttpClient(boolean initSslContext, Map<String,
287
301
SocketConfig socketConfig = soCfgBuilder .build ();
288
302
289
303
// Connection manager
290
- if (ClientConfigProperties .CONNECTION_POOL_ENABLED .getOrDefault (configuration )) {
304
+ if (ClientConfigProperties .CONNECTION_POOL_ENABLED .< Boolean > getOrDefault (configuration )) {
291
305
clientBuilder .setConnectionManager (poolConnectionManager (sslConnectionSocketFactory , socketConfig , configuration ));
292
306
} else {
293
307
clientBuilder .setConnectionManager (basicConnectionManager (sslConnectionSocketFactory , socketConfig , configuration ));
@@ -430,36 +444,55 @@ public ClassicHttpResponse executeRequest(Endpoint server, Map<String, Object> r
430
444
private static final ContentType CONTENT_TYPE = ContentType .create (ContentType .TEXT_PLAIN .getMimeType (), "UTF-8" );
431
445
432
446
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 ());
434
448
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 ()));
436
453
}
437
-
438
454
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 ()) {
450
475
String user = ClientConfigProperties .USER .getOrDefault (requestConfig );
451
476
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 )));
456
482
} 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 ));
460
491
}
461
492
if (proxyAuthHeaderValue != null ) {
462
- req .addHeader (HttpHeaders .PROXY_AUTHORIZATION , proxyAuthHeaderValue );
493
+ req .addHeader (
494
+ HttpHeaders .PROXY_AUTHORIZATION ,
495
+ proxyAuthHeaderValue );
463
496
}
464
497
465
498
boolean clientCompression = ClientConfigProperties .COMPRESS_CLIENT_REQUEST .getOrDefault (requestConfig );
@@ -469,26 +502,30 @@ private void addHeaders(HttpPost req, Map<String, Object> requestConfig) {
469
502
470
503
if (useHttpCompression ) {
471
504
if (serverCompression ) {
472
- req . addHeader (HttpHeaders .ACCEPT_ENCODING , "lz4" );
505
+ addHeader (req , HttpHeaders .ACCEPT_ENCODING , "lz4" );
473
506
}
474
507
if (clientCompression && !appCompressedData ) {
475
- req . addHeader (HttpHeaders .CONTENT_ENCODING , "lz4" );
508
+ addHeader (req , HttpHeaders .CONTENT_ENCODING , "lz4" );
476
509
}
477
510
}
478
511
479
512
for (String key : requestConfig .keySet ()) {
480
513
if (key .startsWith (ClientConfigProperties .HTTP_HEADER_PREFIX )) {
481
514
Object val = requestConfig .get (key );
482
515
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 ));
484
520
}
485
521
}
486
522
}
487
523
488
-
489
524
// 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
+ {
492
529
// user has set auth header for purpose, lets remove ours
493
530
req .removeHeaders (ClickHouseHttpProto .HEADER_DB_USER );
494
531
req .removeHeaders (ClickHouseHttpProto .HEADER_DB_PASSWORD );
@@ -668,7 +705,6 @@ private void correctUserAgentHeader(HttpRequest request, Map<String, Object> req
668
705
} else if (userAgentHeader != null ) {
669
706
userAgentValue = userAgentHeader .getValue () + " " + defaultUserAgent ;
670
707
}
671
-
672
708
request .setHeader (HttpHeaders .USER_AGENT , userAgentValue );
673
709
}
674
710
@@ -720,6 +756,25 @@ public void close() {
720
756
httpClient .close (CloseMode .IMMEDIATE );
721
757
}
722
758
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
+
723
778
/**
724
779
* This factory is used only when no ssl connections are required (no https endpoints).
725
780
* Internally http client would create factory and spend time if no supplied.
0 commit comments