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 .ApacheHttpConnectionImpl ;
911import com .clickhouse .client .http .ClickHouseHttpProto ;
1012import com .clickhouse .client .http .config .ClickHouseHttpOption ;
11- import org .apache .hc .client5 .http .SchemePortResolver ;
1213import org .apache .hc .client5 .http .classic .methods .HttpPost ;
1314import org .apache .hc .client5 .http .config .RequestConfig ;
15+ import org .apache .hc .client5 .http .impl .auth .CredentialsProviderBuilder ;
1416import org .apache .hc .client5 .http .impl .classic .CloseableHttpClient ;
1517import org .apache .hc .client5 .http .impl .classic .HttpClientBuilder ;
1618import org .apache .hc .client5 .http .impl .io .PoolingHttpClientConnectionManagerBuilder ;
17- import org .apache .hc .client5 .http .impl .routing . DefaultRoutePlanner ;
19+ import org .apache .hc .client5 .http .impl .io . PoolingHttpClientConnectionManagerBuilder ;
1820import org .apache .hc .client5 .http .protocol .HttpClientContext ;
1921import org .apache .hc .client5 .http .socket .ConnectionSocketFactory ;
2022import org .apache .hc .client5 .http .socket .LayeredConnectionSocketFactory ;
2527import org .apache .hc .core5 .http .Header ;
2628import org .apache .hc .core5 .http .HttpHeaders ;
2729import org .apache .hc .core5 .http .HttpHost ;
30+ import org .apache .hc .core5 .http .HttpStatus ;
2831import org .apache .hc .core5 .http .NoHttpResponseException ;
2932import org .apache .hc .core5 .http .config .RegistryBuilder ;
3033import org .apache .hc .core5 .http .io .SocketConfig ;
4043import java .io .ByteArrayOutputStream ;
4144import java .io .IOException ;
4245import java .io .OutputStream ;
43- import java .io .Serializable ;
4446import java .net .ConnectException ;
47+ import java .net .InetSocketAddress ;
4548import java .net .NoRouteToHostException ;
4649import java .net .URI ;
4750import java .net .URISyntaxException ;
4851import java .net .UnknownHostException ;
52+ import java .util .Base64 ;
53+ import java .util .EnumSet ;
4954import java .util .Map ;
5055import java .util .concurrent .TimeUnit ;
5156import java .util .function .Function ;
@@ -61,39 +66,62 @@ public class HttpAPIClientHelper {
6166
6267 private RequestConfig baseRequestConfig ;
6368
69+ private String proxyAuthHeaderValue ;
70+
6471 public HttpAPIClientHelper (Map <String , String > configuration ) {
6572 this .chConfiguration = configuration ;
66- this .httpClient = createHttpClient (configuration , null );
67- this .baseRequestConfig = RequestConfig .custom ()
68- .setConnectionRequestTimeout (1000 , TimeUnit .MILLISECONDS )
69- .build ();
73+ this .httpClient = createHttpClient ();
74+
75+ RequestConfig .Builder reqConfBuilder = RequestConfig .custom ();
76+ MapUtils .applyLong (chConfiguration , ClickHouseClientOption .CONNECTION_TIMEOUT .getKey (),
77+ (t ) -> reqConfBuilder .setConnectionRequestTimeout (t , TimeUnit .MILLISECONDS ));
78+
79+ this .baseRequestConfig = reqConfBuilder .build ();
7080 }
7181
72- public CloseableHttpClient createHttpClient (Map <String , String > chConfig , Map <String , Serializable > requestConfig ) {
73- HttpClientBuilder httpclient = HttpClientBuilder .create ();
82+ public CloseableHttpClient createHttpClient () {
83+ HttpClientBuilder clientBuilder = HttpClientBuilder .create ();
84+ CredentialsProviderBuilder credProviderBuilder = CredentialsProviderBuilder .create ();
85+ SocketConfig .Builder soCfgBuilder = SocketConfig .custom ();
86+ PoolingHttpClientConnectionManagerBuilder connMgrBuilder = PoolingHttpClientConnectionManagerBuilder .create ();
87+
88+
89+ MapUtils .applyInt (chConfiguration , ClickHouseClientOption .SOCKET_TIMEOUT .getKey (),
90+ (t ) -> soCfgBuilder .setSoTimeout (t , TimeUnit .MILLISECONDS ));
91+ MapUtils .applyInt (chConfiguration , ClickHouseClientOption .SOCKET_RCVBUF .getKey (),
92+ soCfgBuilder ::setRcvBufSize );
93+ MapUtils .applyInt (chConfiguration , ClickHouseClientOption .SOCKET_SNDBUF .getKey (),
94+ soCfgBuilder ::setSndBufSize );
7495
75-
76- String proxyHost = chConfig .get (ClickHouseClientOption .PROXY_HOST .getKey ());
77- String proxyPort = chConfig . get ( ClickHouseClientOption . PROXY_PORT . getKey ()) ;
96+ String proxyHost = chConfiguration . get ( ClickHouseClientOption . PROXY_HOST . getKey ());
97+ String proxyPort = chConfiguration .get (ClickHouseClientOption .PROXY_PORT .getKey ());
98+ HttpHost proxy = null ;
7899 if (proxyHost != null && proxyPort != null ) {
79- HttpHost proxy = new HttpHost (proxyHost , Integer .parseInt (proxyPort ));
80- httpclient .setProxy (proxy );
100+ proxy = new HttpHost (proxyHost , Integer .parseInt (proxyPort ));
81101 }
82102
83- // RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder.<ConnectionSocketFactory>create()
84- // .register("http", socketFactory.create(c, PlainConnectionSocketFactory.class))
85- // .register("https", socketFactory.create(c, SSLConnectionSocketFactory.class));
86103
104+ String proxyTypeVal = chConfiguration .get (ClickHouseClientOption .PROXY_TYPE .getKey ());
105+ ProxyType proxyType = proxyTypeVal == null ? null : ProxyType .valueOf (proxyTypeVal );
106+ if (proxyType == ProxyType .HTTP ) {
107+ clientBuilder .setProxy (proxy );
108+ if (chConfiguration .containsKey ("proxy_password" ) && chConfiguration .containsKey ("proxy_user" )) {
109+ proxyAuthHeaderValue = "Basic " + Base64 .getEncoder ().encodeToString (
110+ (chConfiguration .get ("proxy_user" ) + ":" + chConfiguration .get ("proxy_password" )).getBytes ());
111+ }
112+ } else if (proxyType == ProxyType .SOCKS ) {
113+ soCfgBuilder .setSocksProxyAddress (new InetSocketAddress (proxyHost , Integer .parseInt (proxyPort )));
114+ }
87115
88- PoolingHttpClientConnectionManagerBuilder connManagerBuilder = PoolingHttpClientConnectionManagerBuilder
89- .create ()
90- .setSSLSocketFactory (new SSLConnectionSocketFactory ((SSLSocketFactory ) SSLSocketFactory .getDefault (), (HostnameVerifier )
91- (hostname , session ) -> {
92- return true ;
93- }));
116+ if (chConfiguration .getOrDefault ("client.http.cookies_enabled" , "true" )
117+ .equalsIgnoreCase ("false" )) {
118+ clientBuilder .disableCookieManagement ();
119+ }
120+ clientBuilder .setDefaultCredentialsProvider (credProviderBuilder .build ());
94121
95- httpclient .setConnectionManager (connManagerBuilder .build ());
96- return httpclient .build ();
122+ connMgrBuilder .setDefaultSocketConfig (soCfgBuilder .build ());
123+ clientBuilder .setConnectionManager (connMgrBuilder .build ());
124+ return clientBuilder .build ();
97125 }
98126
99127 /**
@@ -138,13 +166,19 @@ public ClassicHttpResponse executeRequest(ClickHouseNode server, Map<String, Obj
138166
139167 try {
140168 ClassicHttpResponse httpResponse = httpClient .executeOpen (null , req , context );
141- if (httpResponse .getCode () >= 400 && httpResponse .getCode () < 500 ) {
169+ if (httpResponse .getCode () == HttpStatus .SC_PROXY_AUTHENTICATION_REQUIRED ) {
170+ throw new ClientMisconfigurationException ("Proxy authentication required. Please check your proxy settings." );
171+ } else if (httpResponse .getCode () >= HttpStatus .SC_BAD_REQUEST &&
172+ httpResponse .getCode () < HttpStatus .SC_SERVER_ERROR ) {
142173 try {
143174 throw readError (httpResponse );
144175 } finally {
145176 httpResponse .close ();
146177 }
147- } else if (httpResponse .getCode () >= 500 ) {
178+ } else if (httpResponse .getCode () == HttpStatus .SC_BAD_GATEWAY ) {
179+ httpResponse .close ();
180+ throw new ClientException ("Server returned '502 Bad gateway'. Check network and proxy settings." );
181+ } else if (httpResponse .getCode () >= HttpStatus .SC_INTERNAL_SERVER_ERROR ) {
148182 httpResponse .close ();
149183 return httpResponse ;
150184 }
@@ -160,6 +194,8 @@ public ClassicHttpResponse executeRequest(ClickHouseNode server, Map<String, Obj
160194 throw e ;
161195 } catch (NoHttpResponseException e ) {
162196 throw e ;
197+ } catch (ClientException e ) {
198+ throw e ;
163199 } catch (Exception e ) {
164200 throw new ClientException ("Failed to execute request" , e );
165201 }
@@ -175,6 +211,10 @@ private void addHeaders(HttpPost req, Map<String, String> chConfig, Map<String,
175211 }
176212 }
177213 req .addHeader (ClickHouseHttpProto .HEADER_DATABASE , chConfig .get (ClickHouseClientOption .DATABASE .getKey ()));
214+
215+ if (proxyAuthHeaderValue != null ) {
216+ req .addHeader (HttpHeaders .PROXY_AUTHORIZATION , proxyAuthHeaderValue );
217+ }
178218 }
179219 private void addQueryParams (URIBuilder req , Map <String , String > chConfig , Map <String , Object > requestConfig ) {
180220 if (requestConfig != null ) {
0 commit comments