@@ -15,10 +15,10 @@ import type {
1515 LogWriter ,
1616 ResponseHeaders ,
1717} from '@clickhouse/client-common'
18- import { sleep } from '@clickhouse/client-common'
1918import {
2019 isSuccessfulResponse ,
2120 parseError ,
21+ sleep ,
2222 toSearchParams ,
2323 transformUrl ,
2424 withHttpSettings ,
@@ -63,8 +63,10 @@ export interface RequestParams {
6363 body ?: string | Stream . Readable
6464 // provided by the user and wrapped around internally
6565 abort_signal : AbortSignal
66- decompress_response ?: boolean
67- compress_request ?: boolean
66+ enable_response_compression ?: boolean
67+ enable_request_compression ?: boolean
68+ // if there are compression headers, attempt to decompress it
69+ try_decompress_response_stream ?: boolean
6870 parse_summary ?: boolean
6971}
7072
@@ -73,7 +75,6 @@ export abstract class NodeBaseConnection
7375{
7476 protected readonly defaultAuthHeader : string
7577 protected readonly defaultHeaders : Http . OutgoingHttpHeaders
76- protected readonly additionalHTTPHeaders : Record < string , string >
7778
7879 private readonly logger : LogWriter
7980 private readonly knownSockets = new WeakMap < net . Socket , SocketInfo > ( )
@@ -83,12 +84,11 @@ export abstract class NodeBaseConnection
8384 protected readonly params : NodeConnectionParams ,
8485 protected readonly agent : Http . Agent ,
8586 ) {
86- this . additionalHTTPHeaders = params . http_headers ?? { }
8787 this . defaultAuthHeader = `Basic ${ Buffer . from (
8888 `${ params . username } :${ params . password } ` ,
8989 ) . toString ( 'base64' ) } `
9090 this . defaultHeaders = {
91- ...this . additionalHTTPHeaders ,
91+ ...( params . http_headers ?? { } ) ,
9292 // KeepAlive agent for some reason does not set this on its own
9393 Connection : this . params . keep_alive . enabled ? 'keep-alive' : 'close' ,
9494 'User-Agent' : getUserAgent ( this . params . application_id ) ,
@@ -137,21 +137,23 @@ export abstract class NodeBaseConnection
137137 )
138138 const searchParams = toSearchParams ( {
139139 database : this . params . database ,
140- clickhouse_settings,
141140 query_params : params . query_params ,
142141 session_id : params . session_id ,
142+ clickhouse_settings,
143143 query_id,
144144 } )
145- const decompressResponse = clickhouse_settings . enable_http_compression === 1
146145 const { controller, controllerCleanup } = this . getAbortController ( params )
146+ // allows to enforce the compression via the settings even if the client instance has it disabled
147+ const enableResponseCompression =
148+ clickhouse_settings . enable_http_compression === 1
147149 try {
148150 const { stream, response_headers } = await this . request (
149151 {
150152 method : 'POST' ,
151153 url : transformUrl ( { url : this . params . url , searchParams } ) ,
152154 body : params . query ,
153155 abort_signal : controller . signal ,
154- decompress_response : decompressResponse ,
156+ enable_response_compression : enableResponseCompression ,
155157 headers : this . buildRequestHeaders ( params ) ,
156158 } ,
157159 'Query' ,
@@ -170,7 +172,7 @@ export abstract class NodeBaseConnection
170172 search_params : searchParams ,
171173 err : err as Error ,
172174 extra_args : {
173- decompress_response : decompressResponse ,
175+ decompress_response : enableResponseCompression ,
174176 clickhouse_settings,
175177 } ,
176178 } )
@@ -200,7 +202,7 @@ export abstract class NodeBaseConnection
200202 url : transformUrl ( { url : this . params . url , searchParams } ) ,
201203 body : params . values ,
202204 abort_signal : controller . signal ,
203- compress_request : this . params . compression . compress_request ,
205+ enable_request_compression : this . params . compression . compress_request ,
204206 parse_summary : true ,
205207 headers : this . buildRequestHeaders ( params ) ,
206208 } ,
@@ -371,16 +373,28 @@ export abstract class NodeBaseConnection
371373 ) : Promise < ConnExecResult < Stream . Readable > > {
372374 const query_id = this . getQueryId ( params . query_id )
373375 const sendQueryInParams = params . values !== undefined
376+ const clickhouse_settings = withHttpSettings (
377+ params . clickhouse_settings ,
378+ this . params . compression . decompress_response ,
379+ )
374380 const toSearchParamsOptions = {
375381 query : sendQueryInParams ? params . query : undefined ,
376382 database : this . params . database ,
377- clickhouse_settings : params . clickhouse_settings ,
378383 query_params : params . query_params ,
379384 session_id : params . session_id ,
385+ clickhouse_settings,
380386 query_id,
381387 }
382388 const searchParams = toSearchParams ( toSearchParamsOptions )
383389 const { controller, controllerCleanup } = this . getAbortController ( params )
390+ const tryDecompressResponseStream =
391+ params . op === 'Exec'
392+ ? // allows to disable stream decompression for the `Exec` operation only
393+ params . decompress_response_stream ??
394+ this . params . compression . decompress_response
395+ : // there is nothing useful in the response stream for the `Command` operation,
396+ // and it is immediately destroyed; never decompress it
397+ false
384398 try {
385399 const { stream, summary, response_headers } = await this . request (
386400 {
@@ -389,6 +403,10 @@ export abstract class NodeBaseConnection
389403 body : sendQueryInParams ? params . values : params . query ,
390404 abort_signal : controller . signal ,
391405 parse_summary : true ,
406+ enable_request_compression : this . params . compression . compress_request ,
407+ enable_response_compression :
408+ this . params . compression . decompress_response ,
409+ try_decompress_response_stream : tryDecompressResponseStream ,
392410 headers : this . buildRequestHeaders ( params ) ,
393411 } ,
394412 params . op ,
@@ -438,20 +456,30 @@ export abstract class NodeBaseConnection
438456 ) : Promise < void > => {
439457 this . logResponse ( op , request , params , _response , start )
440458
441- const decompressionResult = decompressResponse ( _response )
442- if ( isDecompressionError ( decompressionResult ) ) {
443- return reject ( decompressionResult . error )
459+ let responseStream : Stream . Readable
460+ const tryDecompressResponseStream =
461+ params . try_decompress_response_stream ?? true
462+ // even if the stream decompression is disabled, we have to decompress it in case of an error
463+ const isFailedResponse = ! isSuccessfulResponse ( _response . statusCode )
464+ if ( tryDecompressResponseStream || isFailedResponse ) {
465+ const decompressionResult = decompressResponse ( _response )
466+ if ( isDecompressionError ( decompressionResult ) ) {
467+ return reject ( decompressionResult . error )
468+ }
469+ responseStream = decompressionResult . response
470+ } else {
471+ responseStream = _response
444472 }
445- if ( isSuccessfulResponse ( _response . statusCode ) ) {
473+ if ( isFailedResponse ) {
474+ reject ( parseError ( await getAsText ( responseStream ) ) )
475+ } else {
446476 return resolve ( {
447- stream : decompressionResult . response ,
477+ stream : responseStream ,
448478 summary : params . parse_summary
449479 ? this . parseSummary ( op , _response )
450480 : undefined ,
451481 response_headers : { ..._response . headers } ,
452482 } )
453- } else {
454- reject ( parseError ( await getAsText ( decompressionResult . response ) ) )
455483 }
456484 }
457485
@@ -492,7 +520,7 @@ export abstract class NodeBaseConnection
492520 }
493521 }
494522
495- if ( params . compress_request ) {
523+ if ( params . enable_request_compression ) {
496524 Stream . pipeline ( bodyStream , Zlib . createGzip ( ) , request , callback )
497525 } else {
498526 Stream . pipeline ( bodyStream , request , callback )
@@ -626,4 +654,5 @@ interface SocketInfo {
626654type RunExecParams = ConnBaseQueryParams & {
627655 op : 'Exec' | 'Command'
628656 values ?: ConnExecParams < Stream . Readable > [ 'values' ]
657+ decompress_response_stream ?: boolean
629658}
0 commit comments