4040import org .elasticsearch .common .compress .Compressor ;
4141import org .elasticsearch .common .compress .CompressorFactory ;
4242import org .elasticsearch .common .compress .NotCompressedException ;
43- import org .elasticsearch .common .io .Streams ;
4443import org .elasticsearch .common .io .stream .BytesStreamOutput ;
4544import org .elasticsearch .common .io .stream .NamedWriteableAwareStreamInput ;
4645import org .elasticsearch .common .io .stream .NamedWriteableRegistry ;
@@ -1094,18 +1093,18 @@ private void sendRequestToChannel(final DiscoveryNode node, final Channel target
10941093 if (compress ) {
10951094 options = TransportRequestOptions .builder (options ).withCompress (true ).build ();
10961095 }
1096+
1097+ // only compress if asked and the request is not bytes. Otherwise only
1098+ // the header part is compressed, and the "body" can't be extracted as compressed
1099+ final boolean compressMessage = options .compress () && canCompress (request );
1100+
10971101 status = TransportStatus .setRequest (status );
10981102 ReleasableBytesStreamOutput bStream = new ReleasableBytesStreamOutput (bigArrays );
1099- // we wrap this in a release once since if the onRequestSent callback throws an exception
1100- // we might release things twice and this should be prevented
1101- final Releasable toRelease = Releasables .releaseOnce (() -> Releasables .close (bStream .bytes ()));
1102- StreamOutput stream = Streams .flushOnCloseStream (bStream );
1103+ final CompressibleBytesOutputStream stream = new CompressibleBytesOutputStream (bStream , compressMessage );
1104+ boolean addedReleaseListener = false ;
11031105 try {
1104- // only compress if asked, and, the request is not bytes, since then only
1105- // the header part is compressed, and the "body" can't be extracted as compressed
1106- if (options .compress () && canCompress (request )) {
1106+ if (compressMessage ) {
11071107 status = TransportStatus .setCompress (status );
1108- stream = CompressorFactory .COMPRESSOR .streamOutput (stream );
11091108 }
11101109
11111110 // we pick the smallest of the 2, to support both backward and forward compatibility
@@ -1116,14 +1115,17 @@ private void sendRequestToChannel(final DiscoveryNode node, final Channel target
11161115 stream .setVersion (version );
11171116 threadPool .getThreadContext ().writeTo (stream );
11181117 stream .writeString (action );
1119- BytesReference message = buildMessage (requestId , status , node .getVersion (), request , stream , bStream );
1118+ BytesReference message = buildMessage (requestId , status , node .getVersion (), request , stream );
11201119 final TransportRequestOptions finalOptions = options ;
11211120 // this might be called in a different thread
1122- SendListener onRequestSent = new SendListener (toRelease ,
1123- () -> transportServiceAdapter .onRequestSent (node , requestId , action , request , finalOptions ));
1121+ SendListener onRequestSent = new SendListener (stream ,
1122+ () -> transportServiceAdapter .onRequestSent (node , requestId , action , request , finalOptions ));
11241123 internalSendMessage (targetChannel , message , onRequestSent );
1124+ addedReleaseListener = true ;
11251125 } finally {
1126- IOUtils .close (stream );
1126+ if (!addedReleaseListener ) {
1127+ IOUtils .close (stream );
1128+ }
11271129 }
11281130 }
11291131
@@ -1185,26 +1187,26 @@ private void sendResponse(Version nodeVersion, Channel channel, final TransportR
11851187 }
11861188 status = TransportStatus .setResponse (status ); // TODO share some code with sendRequest
11871189 ReleasableBytesStreamOutput bStream = new ReleasableBytesStreamOutput (bigArrays );
1188- // we wrap this in a release once since if the onRequestSent callback throws an exception
1189- // we might release things twice and this should be prevented
1190- final Releasable toRelease = Releasables .releaseOnce (() -> Releasables .close (bStream .bytes ()));
1191- StreamOutput stream = Streams .flushOnCloseStream (bStream );
1190+ CompressibleBytesOutputStream stream = new CompressibleBytesOutputStream (bStream , options .compress ());
1191+ boolean addedReleaseListener = false ;
11921192 try {
11931193 if (options .compress ()) {
11941194 status = TransportStatus .setCompress (status );
1195- stream = CompressorFactory .COMPRESSOR .streamOutput (stream );
11961195 }
11971196 threadPool .getThreadContext ().writeTo (stream );
11981197 stream .setVersion (nodeVersion );
1199- BytesReference reference = buildMessage (requestId , status , nodeVersion , response , stream , bStream );
1198+ BytesReference reference = buildMessage (requestId , status , nodeVersion , response , stream );
12001199
12011200 final TransportResponseOptions finalOptions = options ;
12021201 // this might be called in a different thread
1203- SendListener listener = new SendListener (toRelease ,
1204- () -> transportServiceAdapter .onResponseSent (requestId , action , response , finalOptions ));
1202+ SendListener listener = new SendListener (stream ,
1203+ () -> transportServiceAdapter .onResponseSent (requestId , action , response , finalOptions ));
12051204 internalSendMessage (channel , reference , listener );
1205+ addedReleaseListener = true ;
12061206 } finally {
1207- IOUtils .close (stream );
1207+ if (!addedReleaseListener ) {
1208+ IOUtils .close (stream );
1209+ }
12081210 }
12091211 }
12101212
@@ -1231,8 +1233,8 @@ final BytesReference buildHeader(long requestId, byte status, Version protocolVe
12311233 /**
12321234 * Serializes the given message into a bytes representation
12331235 */
1234- private BytesReference buildMessage (long requestId , byte status , Version nodeVersion , TransportMessage message , StreamOutput stream ,
1235- ReleasableBytesStreamOutput writtenBytes ) throws IOException {
1236+ private BytesReference buildMessage (long requestId , byte status , Version nodeVersion , TransportMessage message ,
1237+ CompressibleBytesOutputStream stream ) throws IOException {
12361238 final BytesReference zeroCopyBuffer ;
12371239 if (message instanceof BytesTransportRequest ) { // what a shitty optimization - we should use a direct send method instead
12381240 BytesTransportRequest bRequest = (BytesTransportRequest ) message ;
@@ -1243,12 +1245,12 @@ private BytesReference buildMessage(long requestId, byte status, Version nodeVer
12431245 message .writeTo (stream );
12441246 zeroCopyBuffer = BytesArray .EMPTY ;
12451247 }
1246- // we have to close the stream here - flush is not enough since we might be compressing the content
1247- // and if we do that the close method will write some marker bytes (EOS marker) and otherwise
1248- // we barf on the decompressing end when we read past EOF on purpose in the #validateRequest method.
1249- // this might be a problem in deflate after all but it's important to close it for now.
1250- stream . close ();
1251- final BytesReference messageBody = writtenBytes . bytes ();
1248+ // we have to call materializeBytes() here before accessing the bytes. A CompressibleBytesOutputStream
1249+ // might be implementing compression. And materializeBytes() ensures that some marker bytes (EOS marker)
1250+ // are written. Otherwise we barf on the decompressing end when we read past EOF on purpose in the
1251+ // #validateRequest method. this might be a problem in deflate after all but it's important to write
1252+ // the marker bytes.
1253+ final BytesReference messageBody = stream . materializeBytes ();
12521254 final BytesReference header = buildHeader (requestId , status , stream .getVersion (), messageBody .length () + zeroCopyBuffer .length ());
12531255 return new CompositeBytesReference (header , messageBody , zeroCopyBuffer );
12541256 }
0 commit comments