5353
5454import javax .net .ssl .SSLContext ;
5555import javax .net .ssl .SSLException ;
56+ import java .io .BufferedReader ;
5657import java .io .ByteArrayOutputStream ;
5758import java .io .IOException ;
59+ import java .io .InputStream ;
60+ import java .io .InputStreamReader ;
5861import java .io .OutputStream ;
5962import java .net .ConnectException ;
6063import java .net .InetSocketAddress ;
6164import java .net .NoRouteToHostException ;
6265import java .net .URI ;
6366import java .net .URISyntaxException ;
6467import java .net .UnknownHostException ;
68+ import java .nio .ByteBuffer ;
69+ import java .nio .charset .StandardCharsets ;
6570import java .security .NoSuchAlgorithmException ;
6671import java .util .Base64 ;
6772import java .util .EnumSet ;
@@ -272,18 +277,47 @@ public CloseableHttpClient createHttpClient() {
272277 return clientBuilder .build ();
273278 }
274279
280+ private static final String ERROR_CODE_PREFIX_PATTERN = "Code: %d. DB::Exception:" ;
275281 /**
276282 * Reads status line and if error tries to parse response body to get server error message.
277283 *
278284 * @param httpResponse - HTTP response
279285 * @return
280286 */
281287 public Exception readError (ClassicHttpResponse httpResponse ) {
282- try (ByteArrayOutputStream out = new ByteArrayOutputStream (ERROR_BODY_BUFFER_SIZE )) {
283- httpResponse .getEntity ().writeTo (out );
284- String message = out .toString ();
285- int serverCode = getHeaderInt (httpResponse .getFirstHeader (ClickHouseHttpProto .HEADER_EXCEPTION_CODE ), 0 );
286- return new ServerException (serverCode , message );
288+ int serverCode = getHeaderInt (httpResponse .getFirstHeader (ClickHouseHttpProto .HEADER_EXCEPTION_CODE ), 0 );
289+ try (InputStream body = httpResponse .getEntity ().getContent ()) {
290+
291+ byte [] buffer = new byte [ERROR_BODY_BUFFER_SIZE ];
292+ byte [] lookUpStr = String .format (ERROR_CODE_PREFIX_PATTERN , serverCode ).getBytes (StandardCharsets .UTF_8 );
293+ while (true ) {
294+ int rBytes = body .read (buffer );
295+ if (rBytes == -1 ) {
296+ break ;
297+ } else {
298+ for (int i = 0 ; i < rBytes ; i ++) {
299+ if (buffer [i ] == lookUpStr [0 ]) {
300+ boolean found = true ;
301+ for (int j = 1 ; j < Math .min (rBytes - i , lookUpStr .length ); j ++) {
302+ if (buffer [i + j ] != lookUpStr [j ]) {
303+ found = false ;
304+ break ;
305+ }
306+ }
307+ if (found ) {
308+ int start = i ;
309+ while (i < rBytes && buffer [i ] != '\n' ) {
310+ i ++;
311+ }
312+
313+ return new ServerException (serverCode , new String (buffer , start , i -start , StandardCharsets .UTF_8 ));
314+ }
315+ }
316+ }
317+ }
318+ }
319+
320+ return new ServerException (serverCode , ERROR_CODE_PREFIX_PATTERN .formatted (serverCode ) + " <Unreadable error message>" );
287321 } catch (IOException e ) {
288322 throw new ClientException ("Failed to read response body" , e );
289323 }
@@ -307,12 +341,13 @@ public ClassicHttpResponse executeRequest(ClickHouseNode server, Map<String, Obj
307341 .build ();
308342 req .setConfig (httpReqConfig );
309343 // setting entity. wrapping if compression is enabled
310- req .setEntity (wrapEntity (new EntityTemplate (-1 , CONTENT_TYPE , null , writeCallback )));
344+ req .setEntity (wrapEntity (new EntityTemplate (-1 , CONTENT_TYPE , null , writeCallback ), false ));
311345
312346 HttpClientContext context = HttpClientContext .create ();
313347
314348 try {
315349 ClassicHttpResponse httpResponse = httpClient .executeOpen (null , req , context );
350+ httpResponse .setEntity (wrapEntity (httpResponse .getEntity (), true ));
316351 if (httpResponse .getCode () == HttpStatus .SC_PROXY_AUTHENTICATION_REQUIRED ) {
317352 throw new ClientMisconfigurationException ("Proxy authentication required. Please check your proxy settings." );
318353 } else if (httpResponse .getCode () >= HttpStatus .SC_BAD_REQUEST &&
@@ -329,8 +364,6 @@ public ClassicHttpResponse executeRequest(ClickHouseNode server, Map<String, Obj
329364 httpResponse .close ();
330365 return httpResponse ;
331366 }
332-
333- httpResponse .setEntity (wrapEntity (httpResponse .getEntity ()));
334367 return httpResponse ;
335368
336369 } catch (UnknownHostException e ) {
@@ -413,13 +446,13 @@ private void addQueryParams(URIBuilder req, Map<String, String> chConfig, Map<St
413446 }
414447 }
415448
416- private HttpEntity wrapEntity (HttpEntity httpEntity ) {
449+ private HttpEntity wrapEntity (HttpEntity httpEntity , boolean isResponse ) {
417450 boolean serverCompression = chConfiguration .getOrDefault (ClickHouseClientOption .COMPRESS .getKey (), "false" ).equalsIgnoreCase ("true" );
418451 boolean clientCompression = chConfiguration .getOrDefault (ClickHouseClientOption .DECOMPRESS .getKey (), "false" ).equalsIgnoreCase ("true" );
419452 boolean useHttpCompression = chConfiguration .getOrDefault ("client.use_http_compression" , "false" ).equalsIgnoreCase ("true" );
420453 if (serverCompression || clientCompression ) {
421454 return new LZ4Entity (httpEntity , useHttpCompression , serverCompression , clientCompression ,
422- MapUtils .getInt (chConfiguration , "compression.lz4.uncompressed_buffer_size" ));
455+ MapUtils .getInt (chConfiguration , "compression.lz4.uncompressed_buffer_size" ), isResponse );
423456 } else {
424457 return httpEntity ;
425458 }
0 commit comments