33import com .clickhouse .client .ClickHouseNode ;
44import com .clickhouse .client .api .Client ;
55import com .clickhouse .client .api .ClientException ;
6+ import com .clickhouse .client .api .ClientMisconfigurationException ;
67import com .clickhouse .client .api .ServerException ;
8+ import com .clickhouse .client .api .enums .ProxyType ;
79import com .clickhouse .client .config .ClickHouseClientOption ;
810import com .clickhouse .client .http .ClickHouseHttpProto ;
911import com .clickhouse .client .http .config .ClickHouseHttpOption ;
10- import org .apache .hc .client5 .http .SchemePortResolver ;
1112import org .apache .hc .client5 .http .classic .methods .HttpPost ;
1213import org .apache .hc .client5 .http .config .RequestConfig ;
14+ import org .apache .hc .client5 .http .impl .auth .CredentialsProviderBuilder ;
1315import org .apache .hc .client5 .http .impl .classic .CloseableHttpClient ;
1416import org .apache .hc .client5 .http .impl .classic .HttpClientBuilder ;
15- import org .apache .hc .client5 .http .impl .routing . DefaultRoutePlanner ;
17+ import org .apache .hc .client5 .http .impl .io . PoolingHttpClientConnectionManagerBuilder ;
1618import org .apache .hc .client5 .http .protocol .HttpClientContext ;
1719import org .apache .hc .core5 .http .ClassicHttpResponse ;
1820import org .apache .hc .core5 .http .ContentType ;
1921import org .apache .hc .core5 .http .Header ;
2022import org .apache .hc .core5 .http .HttpHeaders ;
2123import org .apache .hc .core5 .http .HttpHost ;
24+ import org .apache .hc .core5 .http .HttpStatus ;
2225import org .apache .hc .core5 .http .NoHttpResponseException ;
26+ import org .apache .hc .core5 .http .io .SocketConfig ;
2327import org .apache .hc .core5 .http .io .entity .EntityTemplate ;
2428import org .apache .hc .core5 .io .IOCallback ;
2529import org .apache .hc .core5 .net .URIBuilder ;
2933import java .io .ByteArrayOutputStream ;
3034import java .io .IOException ;
3135import java .io .OutputStream ;
32- import java .io .Serializable ;
3336import java .net .ConnectException ;
37+ import java .net .InetSocketAddress ;
3438import java .net .NoRouteToHostException ;
3539import java .net .URI ;
3640import java .net .URISyntaxException ;
3741import java .net .UnknownHostException ;
42+ import java .util .Base64 ;
43+ import java .util .EnumSet ;
3844import java .util .Map ;
3945import java .util .concurrent .TimeUnit ;
4046import java .util .function .Function ;
@@ -50,25 +56,61 @@ public class HttpAPIClientHelper {
5056
5157 private RequestConfig baseRequestConfig ;
5258
59+ private String proxyAuthHeaderValue ;
60+
5361 public HttpAPIClientHelper (Map <String , String > configuration ) {
5462 this .chConfiguration = configuration ;
55- this .httpClient = createHttpClient (configuration , null );
56- this . baseRequestConfig = RequestConfig . custom ()
57- . setConnectionRequestTimeout ( 1000 , TimeUnit . MILLISECONDS )
58- . build ();
59- }
63+ this .httpClient = createHttpClient ();
64+
65+ RequestConfig . Builder reqConfBuilder = RequestConfig . custom ();
66+ MapUtils . applyLong ( chConfiguration , ClickHouseClientOption . CONNECTION_TIMEOUT . getKey (),
67+ ( t ) -> reqConfBuilder . setConnectionRequestTimeout ( t , TimeUnit . MILLISECONDS ));
6068
61- public CloseableHttpClient createHttpClient ( Map < String , String > chConfig , Map < String , Serializable > requestConfig ) {
62- HttpClientBuilder httpclient = HttpClientBuilder . create ();
69+ this . baseRequestConfig = reqConfBuilder . build ();
70+ }
6371
64- String proxyHost = chConfig .get (ClickHouseClientOption .PROXY_HOST .getKey ());
65- String proxyPort = chConfig .get (ClickHouseClientOption .PROXY_PORT .getKey ());
72+ public CloseableHttpClient createHttpClient () {
73+ HttpClientBuilder clientBuilder = HttpClientBuilder .create ();
74+ CredentialsProviderBuilder credProviderBuilder = CredentialsProviderBuilder .create ();
75+ SocketConfig .Builder soCfgBuilder = SocketConfig .custom ();
76+ PoolingHttpClientConnectionManagerBuilder connMgrBuilder = PoolingHttpClientConnectionManagerBuilder .create ();
77+
78+ MapUtils .applyInt (chConfiguration , ClickHouseClientOption .SOCKET_TIMEOUT .getKey (),
79+ (t ) -> soCfgBuilder .setSoTimeout (t , TimeUnit .MILLISECONDS ));
80+ MapUtils .applyInt (chConfiguration , ClickHouseClientOption .SOCKET_RCVBUF .getKey (),
81+ soCfgBuilder ::setRcvBufSize );
82+ MapUtils .applyInt (chConfiguration , ClickHouseClientOption .SOCKET_SNDBUF .getKey (),
83+ soCfgBuilder ::setSndBufSize );
84+
85+ String proxyHost = chConfiguration .get (ClickHouseClientOption .PROXY_HOST .getKey ());
86+ String proxyPort = chConfiguration .get (ClickHouseClientOption .PROXY_PORT .getKey ());
87+ HttpHost proxy = null ;
6688 if (proxyHost != null && proxyPort != null ) {
67- HttpHost proxy = new HttpHost (proxyHost , Integer .parseInt (proxyPort ));
68- httpclient .setProxy (proxy );
89+ proxy = new HttpHost (proxyHost , Integer .parseInt (proxyPort ));
6990 }
7091
71- return httpclient .build ();
92+
93+ String proxyTypeVal = chConfiguration .get (ClickHouseClientOption .PROXY_TYPE .getKey ());
94+ ProxyType proxyType = proxyTypeVal == null ? null : ProxyType .valueOf (proxyTypeVal );
95+ if (proxyType == ProxyType .HTTP ) {
96+ clientBuilder .setProxy (proxy );
97+ if (chConfiguration .containsKey ("proxy_password" ) && chConfiguration .containsKey ("proxy_user" )) {
98+ proxyAuthHeaderValue = "Basic " + Base64 .getEncoder ().encodeToString (
99+ (chConfiguration .get ("proxy_user" ) + ":" + chConfiguration .get ("proxy_password" )).getBytes ());
100+ }
101+ } else if (proxyType == ProxyType .SOCKS ) {
102+ soCfgBuilder .setSocksProxyAddress (new InetSocketAddress (proxyHost , Integer .parseInt (proxyPort )));
103+ }
104+
105+ if (chConfiguration .getOrDefault ("client.http.cookies_enabled" , "true" )
106+ .equalsIgnoreCase ("false" )) {
107+ clientBuilder .disableCookieManagement ();
108+ }
109+ clientBuilder .setDefaultCredentialsProvider (credProviderBuilder .build ());
110+
111+ connMgrBuilder .setDefaultSocketConfig (soCfgBuilder .build ());
112+ clientBuilder .setConnectionManager (connMgrBuilder .build ());
113+ return clientBuilder .build ();
72114 }
73115
74116 /**
@@ -113,13 +155,19 @@ public ClassicHttpResponse executeRequest(ClickHouseNode server, Map<String, Obj
113155
114156 try {
115157 ClassicHttpResponse httpResponse = httpClient .executeOpen (target , req , context );
116- if (httpResponse .getCode () >= 400 && httpResponse .getCode () < 500 ) {
158+ if (httpResponse .getCode () == HttpStatus .SC_PROXY_AUTHENTICATION_REQUIRED ) {
159+ throw new ClientMisconfigurationException ("Proxy authentication required. Please check your proxy settings." );
160+ } else if (httpResponse .getCode () >= HttpStatus .SC_BAD_REQUEST &&
161+ httpResponse .getCode () < HttpStatus .SC_SERVER_ERROR ) {
117162 try {
118163 throw readError (httpResponse );
119164 } finally {
120165 httpResponse .close ();
121166 }
122- } else if (httpResponse .getCode () >= 500 ) {
167+ } else if (httpResponse .getCode () == HttpStatus .SC_BAD_GATEWAY ) {
168+ httpResponse .close ();
169+ throw new ClientException ("Server returned '502 Bad gateway'. Check network and proxy settings." );
170+ } else if (httpResponse .getCode () >= HttpStatus .SC_INTERNAL_SERVER_ERROR ) {
123171 httpResponse .close ();
124172 return httpResponse ;
125173 }
@@ -133,6 +181,8 @@ public ClassicHttpResponse executeRequest(ClickHouseNode server, Map<String, Obj
133181 throw e ;
134182 } catch (NoHttpResponseException e ) {
135183 throw e ;
184+ } catch (ClientException e ) {
185+ throw e ;
136186 } catch (Exception e ) {
137187 throw new ClientException ("Failed to execute request" , e );
138188 }
@@ -150,6 +200,10 @@ private void addHeaders(HttpPost req, Map<String, String> chConfig, Map<String,
150200 }
151201 }
152202 req .addHeader (ClickHouseHttpProto .HEADER_DATABASE , chConfig .get (ClickHouseClientOption .DATABASE .getKey ()));
203+
204+ if (proxyAuthHeaderValue != null ) {
205+ req .addHeader (HttpHeaders .PROXY_AUTHORIZATION , proxyAuthHeaderValue );
206+ }
153207 }
154208 private void addQueryParams (URIBuilder req , Map <String , String > chConfig , Map <String , Object > requestConfig ) {
155209 if (requestConfig != null ) {
0 commit comments