Skip to content

Commit c1de3fa

Browse files
authored
HTTP API calls hang with 'Accept-Encoding: zstd' (#17408) (#17415)
Signed-off-by: Andriy Redko <drreta@gmail.com> (cherry picked from commit ca8e4f8)
1 parent 052b732 commit c1de3fa

File tree

3 files changed

+73
-2
lines changed

3 files changed

+73
-2
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
2929
### Fixed
3030
- Fix case insensitive and escaped query on wildcard ([#16827](https://github.com/opensearch-project/OpenSearch/pull/16827))
3131
- Fix illegal argument exception when creating a PIT ([#16781](https://github.com/opensearch-project/OpenSearch/pull/16781))
32+
- Fix HTTP API calls that hang with 'Accept-Encoding: zstd' ([#17408](https://github.com/opensearch-project/OpenSearch/pull/17408))
3233

3334
### Security
3435

modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpServerTransport.java

+68-1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@
6161

6262
import java.net.InetSocketAddress;
6363
import java.net.SocketOption;
64+
import java.util.ArrayList;
65+
import java.util.List;
6466
import java.util.concurrent.TimeUnit;
6567

6668
import io.netty.bootstrap.ServerBootstrap;
@@ -75,6 +77,12 @@
7577
import io.netty.channel.RecvByteBufAllocator;
7678
import io.netty.channel.socket.nio.NioChannelOption;
7779
import io.netty.handler.codec.ByteToMessageDecoder;
80+
import io.netty.handler.codec.compression.Brotli;
81+
import io.netty.handler.codec.compression.CompressionOptions;
82+
import io.netty.handler.codec.compression.DeflateOptions;
83+
import io.netty.handler.codec.compression.GzipOptions;
84+
import io.netty.handler.codec.compression.StandardCompressionOptions;
85+
import io.netty.handler.codec.compression.ZstdEncoder;
7886
import io.netty.handler.codec.http.HttpContentCompressor;
7987
import io.netty.handler.codec.http.HttpContentDecompressor;
8088
import io.netty.handler.codec.http.HttpObjectAggregator;
@@ -374,7 +382,11 @@ protected void initChannel(Channel ch) throws Exception {
374382
aggregator.setMaxCumulationBufferComponents(transport.maxCompositeBufferComponents);
375383
ch.pipeline().addLast("aggregator", aggregator);
376384
if (handlingSettings.isCompression()) {
377-
ch.pipeline().addLast("encoder_compress", new HttpContentCompressor(handlingSettings.getCompressionLevel()));
385+
ch.pipeline()
386+
.addLast(
387+
"encoder_compress",
388+
new HttpContentCompressor(defaultCompressionOptions(handlingSettings.getCompressionLevel()))
389+
);
378390
}
379391
ch.pipeline().addLast("request_creator", requestCreator);
380392
ch.pipeline().addLast("response_creator", responseCreator);
@@ -427,4 +439,59 @@ protected ChannelInboundHandlerAdapter createHeaderVerifier() {
427439
protected ChannelInboundHandlerAdapter createDecompressor() {
428440
return new HttpContentDecompressor();
429441
}
442+
443+
/**
444+
* Copy of {@link HttpContentCompressor} default compression options with ZSTD excluded:
445+
* although zstd-jni is on the classpath, {@link ZstdEncoder} requires direct buffers support
446+
* which by default {@link NettyAllocator} does not provide.
447+
*
448+
* @param compressionLevel
449+
* {@code 1} yields the fastest compression and {@code 9} yields the
450+
* best compression. {@code 0} means no compression. The default
451+
* compression level is {@code 6}.
452+
*
453+
* @return default compression options
454+
*/
455+
private static CompressionOptions[] defaultCompressionOptions(int compressionLevel) {
456+
return defaultCompressionOptions(compressionLevel, 15, 8);
457+
}
458+
459+
/**
460+
* Copy of {@link HttpContentCompressor} default compression options with ZSTD excluded:
461+
* although zstd-jni is on the classpath, {@link ZstdEncoder} requires direct buffers support
462+
* which by default {@link NettyAllocator} does not provide.
463+
*
464+
* @param compressionLevel
465+
* {@code 1} yields the fastest compression and {@code 9} yields the
466+
* best compression. {@code 0} means no compression. The default
467+
* compression level is {@code 6}.
468+
* @param windowBits
469+
* The base two logarithm of the size of the history buffer. The
470+
* value should be in the range {@code 9} to {@code 15} inclusive.
471+
* Larger values result in better compression at the expense of
472+
* memory usage. The default value is {@code 15}.
473+
* @param memLevel
474+
* How much memory should be allocated for the internal compression
475+
* state. {@code 1} uses minimum memory and {@code 9} uses maximum
476+
* memory. Larger values result in better and faster compression
477+
* at the expense of memory usage. The default value is {@code 8}
478+
*
479+
* @return default compression options
480+
*/
481+
private static CompressionOptions[] defaultCompressionOptions(int compressionLevel, int windowBits, int memLevel) {
482+
final List<CompressionOptions> options = new ArrayList<CompressionOptions>(4);
483+
final GzipOptions gzipOptions = StandardCompressionOptions.gzip(compressionLevel, windowBits, memLevel);
484+
final DeflateOptions deflateOptions = StandardCompressionOptions.deflate(compressionLevel, windowBits, memLevel);
485+
486+
options.add(gzipOptions);
487+
options.add(deflateOptions);
488+
options.add(StandardCompressionOptions.snappy());
489+
490+
if (Brotli.isAvailable()) {
491+
options.add(StandardCompressionOptions.brotli());
492+
}
493+
494+
return options.toArray(new CompressionOptions[0]);
495+
}
496+
430497
}

modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpServerTransportTests.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,10 @@ public void dispatchBadRequest(final RestChannel channel, final ThreadContext th
393393

394394
try (Netty4HttpClient client = new Netty4HttpClient()) {
395395
DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, url);
396-
request.headers().add(HttpHeaderNames.ACCEPT_ENCODING, randomFrom("deflate", "gzip"));
396+
// ZSTD is not supported at the moment by NettyAllocator (needs direct buffers),
397+
// and Brotly is not on classpath.
398+
final String contentEncoding = randomFrom("deflate", "gzip", "snappy", "br", "zstd");
399+
request.headers().add(HttpHeaderNames.ACCEPT_ENCODING, contentEncoding);
397400
long numOfHugeAllocations = getHugeAllocationCount();
398401
final FullHttpResponse response = client.send(remoteAddress.address(), request);
399402
try {

0 commit comments

Comments
 (0)