From 94a3d1d48c16aba8da657087254c5f947f83dfbe Mon Sep 17 00:00:00 2001 From: raccoonback Date: Sat, 28 Dec 2024 11:47:21 +0900 Subject: [PATCH 1/8] Supports compression level setting fix: https://github.com/reactor/reactor-netty/issues/3244 --- .../Http2StreamBridgeServerHandler.java | 4 ++ .../reactor/netty/http/server/Http3Codec.java | 10 +++- .../http/server/Http3ServerOperations.java | 3 +- .../Http3StreamBridgeServerHandler.java | 4 ++ .../reactor/netty/http/server/HttpServer.java | 21 +++++++ .../netty/http/server/HttpServerConfig.java | 59 +++++++++++++++---- .../http/server/HttpServerOperations.java | 8 ++- .../netty/http/server/HttpTrafficHandler.java | 5 ++ .../http/server/SimpleCompressionHandler.java | 34 +++++++++-- .../HttpCompressionClientServerTests.java | 43 ++++++++++++++ .../netty/http/server/HttpServerTests.java | 2 + 11 files changed, 169 insertions(+), 24 deletions(-) diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http2StreamBridgeServerHandler.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http2StreamBridgeServerHandler.java index 6adbb42d34..d143ce8347 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http2StreamBridgeServerHandler.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http2StreamBridgeServerHandler.java @@ -62,6 +62,7 @@ final class Http2StreamBridgeServerHandler extends ChannelDuplexHandler { final BiPredicate compress; + final int compressionLevel; final ServerCookieDecoder cookieDecoder; final ServerCookieEncoder cookieEncoder; final HttpServerFormDecoderProvider formDecoderProvider; @@ -84,6 +85,7 @@ final class Http2StreamBridgeServerHandler extends ChannelDuplexHandler { Http2StreamBridgeServerHandler( @Nullable BiPredicate compress, + int compressionLevel, ServerCookieDecoder decoder, ServerCookieEncoder encoder, HttpServerFormDecoderProvider formDecoderProvider, @@ -94,6 +96,7 @@ final class Http2StreamBridgeServerHandler extends ChannelDuplexHandler { @Nullable Duration readTimeout, @Nullable Duration requestTimeout) { this.compress = compress; + this.compressionLevel = compressionLevel; this.cookieDecoder = decoder; this.cookieEncoder = encoder; this.formDecoderProvider = formDecoderProvider; @@ -140,6 +143,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { listener, request, compress, + compressionLevel, connectionInfo, cookieDecoder, cookieEncoder, diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3Codec.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3Codec.java index 5378628d01..e9b8a02500 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3Codec.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3Codec.java @@ -63,6 +63,7 @@ final class Http3Codec extends ChannelInitializer { final Function methodTagValue; final ChannelMetricsRecorder metricsRecorder; final int minCompressionSize; + final int compressionLevel; final ChannelOperations.OnSetup opsFactory; final Duration readTimeout; final Duration requestTimeout; @@ -83,6 +84,7 @@ final class Http3Codec extends ChannelInitializer { @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, + int compressionLevel, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -101,6 +103,7 @@ final class Http3Codec extends ChannelInitializer { this.methodTagValue = methodTagValue; this.metricsRecorder = metricsRecorder; this.minCompressionSize = minCompressionSize; + this.compressionLevel = compressionLevel; this.opsFactory = opsFactory; this.readTimeout = readTimeout; this.requestTimeout = requestTimeout; @@ -118,13 +121,13 @@ protected void initChannel(QuicStreamChannel channel) { p.addLast(NettyPipeline.H3ToHttp11Codec, new Http3FrameToHttpObjectCodec(true, validate)) .addLast(NettyPipeline.HttpTrafficHandler, - new Http3StreamBridgeServerHandler(compressPredicate, cookieDecoder, cookieEncoder, formDecoderProvider, + new Http3StreamBridgeServerHandler(compressPredicate, compressionLevel, cookieDecoder, cookieEncoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, listener, mapHandle, readTimeout, requestTimeout)); boolean alwaysCompress = compressPredicate == null && minCompressionSize == 0; if (alwaysCompress) { - p.addLast(NettyPipeline.CompressionHandler, new SimpleCompressionHandler()); + p.addLast(NettyPipeline.CompressionHandler, SimpleCompressionHandler.create(compressionLevel)); } ChannelOperations.addReactiveBridge(channel, opsFactory, listener); @@ -166,6 +169,7 @@ static ChannelHandler newHttp3ServerConnectionHandler( @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, + int compressionLevel, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -173,7 +177,7 @@ static ChannelHandler newHttp3ServerConnectionHandler( boolean validate) { return new Http3ServerConnectionHandler( new Http3Codec(accessLogEnabled, accessLog, compressPredicate, decoder, encoder, formDecoderProvider, forwardedHeaderHandler, - httpMessageLogFactory, listener, mapHandle, methodTagValue, metricsRecorder, minCompressionSize, + httpMessageLogFactory, listener, mapHandle, methodTagValue, metricsRecorder, minCompressionSize, compressionLevel, opsFactory, readTimeout, requestTimeout, uriTagValue, validate)); } } \ No newline at end of file diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3ServerOperations.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3ServerOperations.java index deaa4ac6ee..8db8597a96 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3ServerOperations.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3ServerOperations.java @@ -43,6 +43,7 @@ final class Http3ServerOperations extends HttpServerOperations { ConnectionObserver listener, HttpRequest nettyRequest, @Nullable BiPredicate compressionPredicate, + int compressionLevel, ConnectionInfo connectionInfo, ServerCookieDecoder decoder, ServerCookieEncoder encoder, @@ -54,7 +55,7 @@ final class Http3ServerOperations extends HttpServerOperations { @Nullable Duration requestTimeout, boolean secured, ZonedDateTime timestamp) { - super(c, listener, nettyRequest, compressionPredicate, connectionInfo, decoder, encoder, formDecoderProvider, + super(c, listener, nettyRequest, compressionPredicate, compressionLevel, connectionInfo, decoder, encoder, formDecoderProvider, httpMessageLogFactory, isHttp2, mapHandle, readTimeout, requestTimeout, secured, timestamp, true); } diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3StreamBridgeServerHandler.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3StreamBridgeServerHandler.java index ff0cb00a85..2e386f4dfe 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3StreamBridgeServerHandler.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3StreamBridgeServerHandler.java @@ -54,6 +54,7 @@ final class Http3StreamBridgeServerHandler extends ChannelDuplexHandler { final BiPredicate compress; + final int compressionLevel; final ServerCookieDecoder cookieDecoder; final ServerCookieEncoder cookieEncoder; final HttpServerFormDecoderProvider formDecoderProvider; @@ -74,6 +75,7 @@ final class Http3StreamBridgeServerHandler extends ChannelDuplexHandler { Http3StreamBridgeServerHandler( @Nullable BiPredicate compress, + int compressionLevel, ServerCookieDecoder decoder, ServerCookieEncoder encoder, HttpServerFormDecoderProvider formDecoderProvider, @@ -84,6 +86,7 @@ final class Http3StreamBridgeServerHandler extends ChannelDuplexHandler { @Nullable Duration readTimeout, @Nullable Duration requestTimeout) { this.compress = compress; + this.compressionLevel = compressionLevel; this.cookieDecoder = decoder; this.cookieEncoder = encoder; this.formDecoderProvider = formDecoderProvider; @@ -131,6 +134,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { listener, request, compress, + compressionLevel, connectionInfo, cookieDecoder, cookieEncoder, diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServer.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServer.java index b8128dde7e..6e12527a4b 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServer.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServer.java @@ -35,6 +35,7 @@ import io.netty.handler.ssl.OpenSsl; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.util.SelfSignedCertificate; +import io.netty.util.internal.ObjectUtil; import org.reactivestreams.Publisher; import reactor.core.publisher.Mono; import reactor.netty.Connection; @@ -301,15 +302,35 @@ public final HttpServer compress(BiPredicate uriTagValue() { int maxKeepAliveRequests; Function methodTagValue; int minCompressionSize; + int compressionLevel; HttpProtocol[] protocols; int _protocols; ProxyProtocolSupportType proxyProtocolSupportType; @@ -344,6 +354,7 @@ public Function uriTagValue() { this.httpMessageLogFactory = ReactorNettyHttpMessageLogFactory.INSTANCE; this.maxKeepAliveRequests = -1; this.minCompressionSize = -1; + this.compressionLevel = 6; this.protocols = new HttpProtocol[]{HttpProtocol.HTTP11}; this._protocols = h11; this.proxyProtocolSupportType = ProxyProtocolSupportType.OFF; @@ -368,6 +379,7 @@ public Function uriTagValue() { this.maxKeepAliveRequests = parent.maxKeepAliveRequests; this.methodTagValue = parent.methodTagValue; this.minCompressionSize = parent.minCompressionSize; + this.compressionLevel = parent.compressionLevel; this.protocols = parent.protocols; this._protocols = parent._protocols; this.proxyProtocolSupportType = parent.proxyProtocolSupportType; @@ -492,6 +504,7 @@ static void addStreamHandlers(Channel ch, @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, + int compressionLevel, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -502,14 +515,14 @@ static void addStreamHandlers(Channel ch, } pipeline.addLast(NettyPipeline.H2ToHttp11Codec, HTTP2_STREAM_FRAME_TO_HTTP_OBJECT) .addLast(NettyPipeline.HttpTrafficHandler, - new Http2StreamBridgeServerHandler(compressPredicate, decoder, encoder, formDecoderProvider, + new Http2StreamBridgeServerHandler(compressPredicate, compressionLevel, decoder, encoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, listener, mapHandle, readTimeout, requestTimeout)); boolean alwaysCompress = compressPredicate == null && minCompressionSize == 0; if (alwaysCompress) { - pipeline.addLast(NettyPipeline.CompressionHandler, new SimpleCompressionHandler()); + pipeline.addLast(NettyPipeline.CompressionHandler, SimpleCompressionHandler.create(compressionLevel)); } ChannelOperations.addReactiveBridge(ch, opsFactory, listener); @@ -605,6 +618,7 @@ static void configureHttp3Pipeline( @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, + int compressionLevel, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -614,7 +628,7 @@ static void configureHttp3Pipeline( p.addLast(NettyPipeline.HttpCodec, newHttp3ServerConnectionHandler(accessLogEnabled, accessLog, compressPredicate, cookieDecoder, cookieEncoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, - listener, mapHandle, methodTagValue, metricsRecorder, minCompressionSize, opsFactory, readTimeout, + listener, mapHandle, methodTagValue, metricsRecorder, minCompressionSize, compressionLevel, opsFactory, readTimeout, requestTimeout, uriTagValue, validate)); if (metricsRecorder != null) { @@ -640,6 +654,7 @@ static void configureH2Pipeline(ChannelPipeline p, @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, + int compressionLevel, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -676,7 +691,7 @@ static void configureH2Pipeline(ChannelPipeline p, .addLast(NettyPipeline.H2MultiplexHandler, new Http2MultiplexHandler(new H2Codec(accessLogEnabled, accessLog, compressPredicate, cookieDecoder, cookieEncoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, listener, - mapHandle, methodTagValue, metricsRecorder, minCompressionSize, opsFactory, readTimeout, requestTimeout, uriTagValue))); + mapHandle, methodTagValue, metricsRecorder, minCompressionSize, compressionLevel, opsFactory, readTimeout, requestTimeout, uriTagValue))); IdleTimeoutHandler.addIdleTimeoutHandler(p, idleTimeout); @@ -710,6 +725,7 @@ static void configureHttp11OrH2CleartextPipeline(ChannelPipeline p, @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, + int compressionLevel, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -728,7 +744,7 @@ static void configureHttp11OrH2CleartextPipeline(ChannelPipeline p, Http11OrH2CleartextCodec upgrader = new Http11OrH2CleartextCodec(accessLogEnabled, accessLog, compressPredicate, cookieDecoder, cookieEncoder, p.get(NettyPipeline.LoggingHandler) != null, enableGracefulShutdown, formDecoderProvider, forwardedHeaderHandler, http2SettingsSpec, httpMessageLogFactory, listener, mapHandle, methodTagValue, metricsRecorder, - minCompressionSize, opsFactory, readTimeout, requestTimeout, uriTagValue, decoder.validateHeaders()); + minCompressionSize, compressionLevel, opsFactory, readTimeout, requestTimeout, uriTagValue, decoder.validateHeaders()); ChannelHandler http2ServerHandler = new H2CleartextCodec(upgrader, http2SettingsSpec != null ? http2SettingsSpec.maxStreams() : null); @@ -743,7 +759,7 @@ static void configureHttp11OrH2CleartextPipeline(ChannelPipeline p, NettyPipeline.H2CUpgradeHandler, h2cUpgradeHandler) .addBefore(NettyPipeline.ReactiveBridge, NettyPipeline.HttpTrafficHandler, - new HttpTrafficHandler(compressPredicate, cookieDecoder, cookieEncoder, formDecoderProvider, + new HttpTrafficHandler(compressPredicate, compressionLevel, cookieDecoder, cookieEncoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, idleTimeout, listener, mapHandle, maxKeepAliveRequests, readTimeout, requestTimeout, decoder.validateHeaders())); @@ -754,7 +770,7 @@ static void configureHttp11OrH2CleartextPipeline(ChannelPipeline p, boolean alwaysCompress = compressPredicate == null && minCompressionSize == 0; if (alwaysCompress) { - p.addBefore(NettyPipeline.HttpTrafficHandler, NettyPipeline.CompressionHandler, new SimpleCompressionHandler()); + p.addBefore(NettyPipeline.HttpTrafficHandler, NettyPipeline.CompressionHandler, SimpleCompressionHandler.create(compressionLevel)); } if (metricsRecorder != null) { @@ -798,6 +814,7 @@ static void configureHttp11Pipeline(ChannelPipeline p, @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, + int compressionLevel, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @Nullable Function uriTagValue) { @@ -814,7 +831,7 @@ static void configureHttp11Pipeline(ChannelPipeline p, new HttpServerCodec(decoderConfig)) .addBefore(NettyPipeline.ReactiveBridge, NettyPipeline.HttpTrafficHandler, - new HttpTrafficHandler(compressPredicate, cookieDecoder, cookieEncoder, formDecoderProvider, + new HttpTrafficHandler(compressPredicate, compressionLevel, cookieDecoder, cookieEncoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, idleTimeout, listener, mapHandle, maxKeepAliveRequests, readTimeout, requestTimeout, decoder.validateHeaders())); @@ -825,7 +842,7 @@ static void configureHttp11Pipeline(ChannelPipeline p, boolean alwaysCompress = compressPredicate == null && minCompressionSize == 0; if (alwaysCompress) { - p.addBefore(NettyPipeline.HttpTrafficHandler, NettyPipeline.CompressionHandler, new SimpleCompressionHandler()); + p.addBefore(NettyPipeline.HttpTrafficHandler, NettyPipeline.CompressionHandler, SimpleCompressionHandler.create(compressionLevel)); } if (metricsRecorder != null) { @@ -1008,6 +1025,7 @@ static final class H2Codec extends ChannelInitializer { final Function methodTagValue; final ChannelMetricsRecorder metricsRecorder; final int minCompressionSize; + final int compressionLevel; final ChannelOperations.OnSetup opsFactory; final Duration readTimeout; final Duration requestTimeout; @@ -1027,6 +1045,7 @@ static final class H2Codec extends ChannelInitializer { @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, + int compressionLevel, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -1044,6 +1063,7 @@ static final class H2Codec extends ChannelInitializer { this.methodTagValue = methodTagValue; this.metricsRecorder = metricsRecorder; this.minCompressionSize = minCompressionSize; + this.compressionLevel = compressionLevel; this.opsFactory = opsFactory; this.readTimeout = readTimeout; this.requestTimeout = requestTimeout; @@ -1055,7 +1075,7 @@ protected void initChannel(Channel ch) { ch.pipeline().remove(this); addStreamHandlers(ch, accessLogEnabled, accessLog, compressPredicate, cookieDecoder, cookieEncoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, listener, mapHandle, methodTagValue, metricsRecorder, - minCompressionSize, opsFactory, readTimeout, requestTimeout, uriTagValue); + minCompressionSize, compressionLevel, opsFactory, readTimeout, requestTimeout, uriTagValue); } } @@ -1078,6 +1098,7 @@ static final class Http11OrH2CleartextCodec extends ChannelInitializer final Function methodTagValue; final ChannelMetricsRecorder metricsRecorder; final int minCompressionSize; + final int compressionLevel; final ChannelOperations.OnSetup opsFactory; final Duration readTimeout; final Duration requestTimeout; @@ -1100,6 +1121,7 @@ static final class Http11OrH2CleartextCodec extends ChannelInitializer @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, + int compressionLevel, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -1140,6 +1162,7 @@ static final class Http11OrH2CleartextCodec extends ChannelInitializer this.methodTagValue = methodTagValue; this.metricsRecorder = metricsRecorder; this.minCompressionSize = minCompressionSize; + this.compressionLevel = compressionLevel; this.opsFactory = opsFactory; this.readTimeout = readTimeout; this.requestTimeout = requestTimeout; @@ -1154,7 +1177,7 @@ protected void initChannel(Channel ch) { ch.pipeline().remove(this); addStreamHandlers(ch, accessLogEnabled, accessLog, compressPredicate, cookieDecoder, cookieEncoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, listener, mapHandle, methodTagValue, - metricsRecorder, minCompressionSize, opsFactory, readTimeout, requestTimeout, uriTagValue); + metricsRecorder, minCompressionSize, compressionLevel, opsFactory, readTimeout, requestTimeout, uriTagValue); } @Override @@ -1213,6 +1236,7 @@ static final class H2OrHttp11Codec extends ApplicationProtocolNegotiationHandler final Function methodTagValue; final ChannelMetricsRecorder metricsRecorder; final int minCompressionSize; + final int compressionLevel; final ChannelOperations.OnSetup opsFactory; final Duration readTimeout; final Duration requestTimeout; @@ -1243,6 +1267,7 @@ static final class H2OrHttp11Codec extends ApplicationProtocolNegotiationHandler this.methodTagValue = initializer.methodTagValue; this.metricsRecorder = initializer.metricsRecorder; this.minCompressionSize = initializer.minCompressionSize; + this.compressionLevel = initializer.compressionLevel; this.opsFactory = initializer.opsFactory; this.readTimeout = initializer.readTimeout; this.requestTimeout = initializer.requestTimeout; @@ -1261,7 +1286,7 @@ protected void configurePipeline(ChannelHandlerContext ctx, String protocol) { if (ApplicationProtocolNames.HTTP_2.equals(protocol)) { configureH2Pipeline(p, accessLogEnabled, accessLog, compressPredicate, cookieDecoder, cookieEncoder, enableGracefulShutdown, formDecoderProvider, forwardedHeaderHandler, http2SettingsSpec, httpMessageLogFactory, idleTimeout, - listener, mapHandle, methodTagValue, metricsRecorder, minCompressionSize, opsFactory, readTimeout, requestTimeout, + listener, mapHandle, methodTagValue, metricsRecorder, minCompressionSize, compressionLevel, opsFactory, readTimeout, requestTimeout, uriTagValue, decoder.validateHeaders()); return; } @@ -1269,7 +1294,7 @@ protected void configurePipeline(ChannelHandlerContext ctx, String protocol) { if (!supportOnlyHttp2 && ApplicationProtocolNames.HTTP_1_1.equals(protocol)) { configureHttp11Pipeline(p, accessLogEnabled, accessLog, compressPredicate, cookieDecoder, cookieEncoder, true, decoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, idleTimeout, listener, - mapHandle, maxKeepAliveRequests, methodTagValue, metricsRecorder, minCompressionSize, readTimeout, requestTimeout, uriTagValue); + mapHandle, maxKeepAliveRequests, methodTagValue, metricsRecorder, minCompressionSize, compressionLevel, readTimeout, requestTimeout, uriTagValue); // When the server is configured with HTTP/1.1 and H2 and HTTP/1.1 is negotiated, // when channelActive event happens, this HttpTrafficHandler is still not in the pipeline, @@ -1302,6 +1327,7 @@ static final class HttpServerChannelInitializer implements ChannelPipelineConfig final Function methodTagValue; final ChannelMetricsRecorder metricsRecorder; final int minCompressionSize; + final int compressionLevel; final ChannelOperations.OnSetup opsFactory; final int protocols; final ProxyProtocolSupportType proxyProtocolSupportType; @@ -1329,6 +1355,7 @@ static final class HttpServerChannelInitializer implements ChannelPipelineConfig this.methodTagValue = config.methodTagValue; this.metricsRecorder = config.metricsRecorderInternal(); this.minCompressionSize = config.minCompressionSize; + this.compressionLevel = config.compressionLevel; this.opsFactory = config.channelOperationsProvider(); this.protocols = config._protocols; this.proxyProtocolSupportType = config.proxyProtocolSupportType; @@ -1383,6 +1410,7 @@ else if ((protocols & h11) == h11) { methodTagValue, metricsRecorder, minCompressionSize, + compressionLevel, readTimeout, requestTimeout, uriTagValue); @@ -1414,6 +1442,7 @@ else if ((protocols & h2) == h2) { methodTagValue, metricsRecorder, minCompressionSize, + compressionLevel, opsFactory, readTimeout, requestTimeout, @@ -1437,6 +1466,7 @@ else if ((protocols & h3) == h3) { methodTagValue, metricsRecorder, minCompressionSize, + compressionLevel, opsFactory, readTimeout, requestTimeout, @@ -1466,6 +1496,7 @@ else if ((protocols & h3) == h3) { methodTagValue, metricsRecorder, minCompressionSize, + compressionLevel, opsFactory, readTimeout, requestTimeout, @@ -1491,6 +1522,7 @@ else if ((protocols & h11) == h11) { methodTagValue, metricsRecorder, minCompressionSize, + compressionLevel, readTimeout, requestTimeout, uriTagValue); @@ -1514,6 +1546,7 @@ else if ((protocols & h2c) == h2c) { methodTagValue, metricsRecorder, minCompressionSize, + compressionLevel, opsFactory, readTimeout, requestTimeout, diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java index a420862efa..9740b42f16 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java @@ -138,6 +138,7 @@ class HttpServerOperations extends HttpOperations compressionPredicate; + int compressionLevel; boolean isWebsocket; Function> paramsResolver; String path; @@ -151,6 +152,7 @@ class HttpServerOperations extends HttpOperations compressionPredicate, + int compressionLevel, ConnectionInfo connectionInfo, ServerCookieDecoder decoder, ServerCookieEncoder encoder, @@ -192,6 +195,7 @@ class HttpServerOperations extends HttpOperations compress; + final int compressionLevel; final ServerCookieDecoder cookieDecoder; final ServerCookieEncoder cookieEncoder; final HttpServerFormDecoderProvider formDecoderProvider; @@ -112,6 +113,7 @@ final class HttpTrafficHandler extends ChannelDuplexHandler implements Runnable HttpTrafficHandler( @Nullable BiPredicate compress, + int compressionLevel, ServerCookieDecoder decoder, ServerCookieEncoder encoder, HttpServerFormDecoderProvider formDecoderProvider, @@ -128,6 +130,7 @@ final class HttpTrafficHandler extends ChannelDuplexHandler implements Runnable this.formDecoderProvider = formDecoderProvider; this.forwardedHeaderHandler = forwardedHeaderHandler; this.compress = compress; + this.compressionLevel = compressionLevel; this.cookieEncoder = encoder; this.cookieDecoder = decoder; this.httpMessageLogFactory = httpMessageLogFactory; @@ -243,6 +246,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { listener, request, compress, + compressionLevel, connectionInfo, cookieDecoder, cookieEncoder, @@ -595,6 +599,7 @@ public void run() { listener, nextRequest, compress, + compressionLevel, connectionInfo, cookieDecoder, cookieEncoder, diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/SimpleCompressionHandler.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/SimpleCompressionHandler.java index ea52b3e054..f47c0d0a8f 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/SimpleCompressionHandler.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/SimpleCompressionHandler.java @@ -15,11 +15,17 @@ */ package reactor.netty.http.server; +import java.util.ArrayList; +import java.util.List; + import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; import io.netty.handler.codec.DecoderException; +import io.netty.handler.codec.compression.Brotli; import io.netty.handler.codec.compression.CompressionOptions; +import io.netty.handler.codec.compression.StandardCompressionOptions; +import io.netty.handler.codec.compression.Zstd; import io.netty.handler.codec.http.DefaultHttpContent; import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; @@ -28,9 +34,7 @@ import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.LastHttpContent; import io.netty.util.ReferenceCountUtil; - -import java.util.ArrayList; -import java.util.List; +import io.netty.util.internal.ObjectUtil; /** * {@link HttpContentCompressor} to enable on-demand compression. @@ -42,8 +46,28 @@ final class SimpleCompressionHandler extends HttpContentCompressor { boolean decoded; HttpRequest request; - SimpleCompressionHandler() { - super((CompressionOptions[]) null); + private SimpleCompressionHandler(CompressionOptions... options) { + super(options); + } + + static SimpleCompressionHandler create(int compressionLevel) { + ObjectUtil.checkInRange(compressionLevel, 0, 9, "compressionLevel"); + + List options = new ArrayList<>(); + options.add(StandardCompressionOptions.gzip(compressionLevel, 15, 8)); + options.add(StandardCompressionOptions.deflate(compressionLevel, 15, 8)); + options.add(StandardCompressionOptions.snappy()); + + if (Zstd.isAvailable()) { + options.add(StandardCompressionOptions.zstd()); + } + if (Brotli.isAvailable()) { + options.add(StandardCompressionOptions.brotli()); + } + + return new SimpleCompressionHandler( + options.toArray(new CompressionOptions[0]) + ); } @Override diff --git a/reactor-netty-http/src/test/java/reactor/netty/http/HttpCompressionClientServerTests.java b/reactor-netty-http/src/test/java/reactor/netty/http/HttpCompressionClientServerTests.java index 89bbe61c96..1e77a60432 100644 --- a/reactor-netty-http/src/test/java/reactor/netty/http/HttpCompressionClientServerTests.java +++ b/reactor-netty-http/src/test/java/reactor/netty/http/HttpCompressionClientServerTests.java @@ -35,7 +35,10 @@ import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.compression.Brotli; +import io.netty.handler.codec.compression.ZlibCodecFactory; +import io.netty.handler.codec.compression.ZlibWrapper; import io.netty.handler.codec.compression.Zstd; import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaders; @@ -718,4 +721,44 @@ void serverCompressionEnabledResponseCompressionDisabled(HttpServer server, Http .expectComplete() .verify(Duration.ofSeconds(10)); } + + @ParameterizedCompressionTest + void serverCompressionEnabledByCompressionLevel(HttpServer server, HttpClient client) { + disposableServer = + server.compress(true, 4) + .handle((in, out) -> out.sendString(Mono.just("reply"))) + .bindNow(Duration.ofSeconds(10)); + + //don't activate compression on the client options to avoid auto-handling (which removes the header) + Tuple2 resp = + //edit the header manually to attempt to trigger compression on server side + client.port(disposableServer.port()) + .headers(h -> h.add("accept-encoding", "gzip")) + .get() + .uri("/test") + .responseSingle((res, buf) -> buf.asByteArray() + .zipWith(Mono.just(res.responseHeaders()))) + .block(Duration.ofSeconds(10)); + + EmbeddedChannel embeddedChannel = new EmbeddedChannel( + ZlibCodecFactory.newZlibEncoder( + ZlibWrapper.GZIP, + 4, + 15, + 8 + ) + ); + + ByteBuf byteBuf = Unpooled.directBuffer(32); + byteBuf.writeBytes("reply".getBytes(Charset.defaultCharset())); + + embeddedChannel.writeOutbound(byteBuf); + ByteBuf encodedByteBuf = embeddedChannel.readOutbound(); + + byte[] result = new byte[encodedByteBuf.readableBytes()]; + encodedByteBuf.getBytes(encodedByteBuf.readerIndex(), result); + + assertThat(resp).isNotNull(); + assertThat(resp.getT1()).startsWith(result); // Ignore the original data size and crc checksum comparison + } } diff --git a/reactor-netty-http/src/test/java/reactor/netty/http/server/HttpServerTests.java b/reactor-netty-http/src/test/java/reactor/netty/http/server/HttpServerTests.java index 7edbaaed3a..b428a69023 100644 --- a/reactor-netty-http/src/test/java/reactor/netty/http/server/HttpServerTests.java +++ b/reactor-netty-http/src/test/java/reactor/netty/http/server/HttpServerTests.java @@ -2214,6 +2214,7 @@ private void doTestStatus(HttpResponseStatus status) { ConnectionObserver.emptyListener(), new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"), null, + 6, new ConnectionInfo(localSocketAddress, DEFAULT_HOST_NAME, DEFAULT_HTTP_PORT, remoteSocketAddress, "http", true), ServerCookieDecoder.STRICT, ServerCookieEncoder.STRICT, @@ -3272,6 +3273,7 @@ private void doTestIsFormUrlencoded(String headerValue, boolean expectation) { ConnectionObserver.emptyListener(), request, null, + 6, new ConnectionInfo(localSocketAddress, DEFAULT_HOST_NAME, DEFAULT_HTTP_PORT, remoteSocketAddress, "http", true), ServerCookieDecoder.STRICT, ServerCookieEncoder.STRICT, From a0600244197844a363e1f9373e8589c3f1155318 Mon Sep 17 00:00:00 2001 From: raccoonback Date: Sun, 29 Dec 2024 16:59:40 +0900 Subject: [PATCH 2/8] Supports gzip, deflate, zstd compression level setting - gzip: only the range 0 to 9 is allowed - deflate: only the range 0 to 9 is allowed - zstd: only the range -7 to 22 is allowed brotli and snappy compression are supported by default. fix: https://github.com/reactor/reactor-netty/issues/3244 --- .../Http2StreamBridgeServerHandler.java | 8 +- .../reactor/netty/http/server/Http3Codec.java | 14 +- .../http/server/Http3ServerOperations.java | 4 +- .../Http3StreamBridgeServerHandler.java | 8 +- .../server/HttpCompressionSettingsSpec.java | 178 ++++++++++++++++++ .../reactor/netty/http/server/HttpServer.java | 49 +++-- .../netty/http/server/HttpServerConfig.java | 83 ++++---- .../http/server/HttpServerOperations.java | 12 +- .../netty/http/server/HttpTrafficHandler.java | 10 +- .../http/server/SimpleCompressionHandler.java | 22 +-- .../HttpCompressionClientServerTests.java | 71 ++++++- .../netty/http/server/HttpServerTests.java | 4 +- 12 files changed, 343 insertions(+), 120 deletions(-) create mode 100644 reactor-netty-http/src/main/java/reactor/netty/http/server/HttpCompressionSettingsSpec.java diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http2StreamBridgeServerHandler.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http2StreamBridgeServerHandler.java index d143ce8347..6a6251e6f5 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http2StreamBridgeServerHandler.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http2StreamBridgeServerHandler.java @@ -62,7 +62,7 @@ final class Http2StreamBridgeServerHandler extends ChannelDuplexHandler { final BiPredicate compress; - final int compressionLevel; + final HttpCompressionSettingsSpec compressionSettings; final ServerCookieDecoder cookieDecoder; final ServerCookieEncoder cookieEncoder; final HttpServerFormDecoderProvider formDecoderProvider; @@ -85,7 +85,7 @@ final class Http2StreamBridgeServerHandler extends ChannelDuplexHandler { Http2StreamBridgeServerHandler( @Nullable BiPredicate compress, - int compressionLevel, + HttpCompressionSettingsSpec compressionSettings, ServerCookieDecoder decoder, ServerCookieEncoder encoder, HttpServerFormDecoderProvider formDecoderProvider, @@ -96,7 +96,7 @@ final class Http2StreamBridgeServerHandler extends ChannelDuplexHandler { @Nullable Duration readTimeout, @Nullable Duration requestTimeout) { this.compress = compress; - this.compressionLevel = compressionLevel; + this.compressionSettings = compressionSettings; this.cookieDecoder = decoder; this.cookieEncoder = encoder; this.formDecoderProvider = formDecoderProvider; @@ -143,7 +143,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { listener, request, compress, - compressionLevel, + compressionSettings, connectionInfo, cookieDecoder, cookieEncoder, diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3Codec.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3Codec.java index e9b8a02500..de0f6a2f9f 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3Codec.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3Codec.java @@ -63,7 +63,7 @@ final class Http3Codec extends ChannelInitializer { final Function methodTagValue; final ChannelMetricsRecorder metricsRecorder; final int minCompressionSize; - final int compressionLevel; + final HttpCompressionSettingsSpec compressionSettings; final ChannelOperations.OnSetup opsFactory; final Duration readTimeout; final Duration requestTimeout; @@ -84,7 +84,7 @@ final class Http3Codec extends ChannelInitializer { @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - int compressionLevel, + HttpCompressionSettingsSpec compressionSettings, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -103,7 +103,7 @@ final class Http3Codec extends ChannelInitializer { this.methodTagValue = methodTagValue; this.metricsRecorder = metricsRecorder; this.minCompressionSize = minCompressionSize; - this.compressionLevel = compressionLevel; + this.compressionSettings = compressionSettings; this.opsFactory = opsFactory; this.readTimeout = readTimeout; this.requestTimeout = requestTimeout; @@ -121,13 +121,13 @@ protected void initChannel(QuicStreamChannel channel) { p.addLast(NettyPipeline.H3ToHttp11Codec, new Http3FrameToHttpObjectCodec(true, validate)) .addLast(NettyPipeline.HttpTrafficHandler, - new Http3StreamBridgeServerHandler(compressPredicate, compressionLevel, cookieDecoder, cookieEncoder, formDecoderProvider, + new Http3StreamBridgeServerHandler(compressPredicate, compressionSettings, cookieDecoder, cookieEncoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, listener, mapHandle, readTimeout, requestTimeout)); boolean alwaysCompress = compressPredicate == null && minCompressionSize == 0; if (alwaysCompress) { - p.addLast(NettyPipeline.CompressionHandler, SimpleCompressionHandler.create(compressionLevel)); + p.addLast(NettyPipeline.CompressionHandler, SimpleCompressionHandler.create(compressionSettings)); } ChannelOperations.addReactiveBridge(channel, opsFactory, listener); @@ -169,7 +169,7 @@ static ChannelHandler newHttp3ServerConnectionHandler( @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - int compressionLevel, + HttpCompressionSettingsSpec compressionSettings, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -177,7 +177,7 @@ static ChannelHandler newHttp3ServerConnectionHandler( boolean validate) { return new Http3ServerConnectionHandler( new Http3Codec(accessLogEnabled, accessLog, compressPredicate, decoder, encoder, formDecoderProvider, forwardedHeaderHandler, - httpMessageLogFactory, listener, mapHandle, methodTagValue, metricsRecorder, minCompressionSize, compressionLevel, + httpMessageLogFactory, listener, mapHandle, methodTagValue, metricsRecorder, minCompressionSize, compressionSettings, opsFactory, readTimeout, requestTimeout, uriTagValue, validate)); } } \ No newline at end of file diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3ServerOperations.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3ServerOperations.java index 8db8597a96..b36bae1d5c 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3ServerOperations.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3ServerOperations.java @@ -43,7 +43,7 @@ final class Http3ServerOperations extends HttpServerOperations { ConnectionObserver listener, HttpRequest nettyRequest, @Nullable BiPredicate compressionPredicate, - int compressionLevel, + HttpCompressionSettingsSpec compressionSettings, ConnectionInfo connectionInfo, ServerCookieDecoder decoder, ServerCookieEncoder encoder, @@ -55,7 +55,7 @@ final class Http3ServerOperations extends HttpServerOperations { @Nullable Duration requestTimeout, boolean secured, ZonedDateTime timestamp) { - super(c, listener, nettyRequest, compressionPredicate, compressionLevel, connectionInfo, decoder, encoder, formDecoderProvider, + super(c, listener, nettyRequest, compressionPredicate, compressionSettings, connectionInfo, decoder, encoder, formDecoderProvider, httpMessageLogFactory, isHttp2, mapHandle, readTimeout, requestTimeout, secured, timestamp, true); } diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3StreamBridgeServerHandler.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3StreamBridgeServerHandler.java index 2e386f4dfe..a33d199f85 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3StreamBridgeServerHandler.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3StreamBridgeServerHandler.java @@ -54,7 +54,7 @@ final class Http3StreamBridgeServerHandler extends ChannelDuplexHandler { final BiPredicate compress; - final int compressionLevel; + final HttpCompressionSettingsSpec compressionSettings; final ServerCookieDecoder cookieDecoder; final ServerCookieEncoder cookieEncoder; final HttpServerFormDecoderProvider formDecoderProvider; @@ -75,7 +75,7 @@ final class Http3StreamBridgeServerHandler extends ChannelDuplexHandler { Http3StreamBridgeServerHandler( @Nullable BiPredicate compress, - int compressionLevel, + HttpCompressionSettingsSpec compressionSettings, ServerCookieDecoder decoder, ServerCookieEncoder encoder, HttpServerFormDecoderProvider formDecoderProvider, @@ -86,7 +86,7 @@ final class Http3StreamBridgeServerHandler extends ChannelDuplexHandler { @Nullable Duration readTimeout, @Nullable Duration requestTimeout) { this.compress = compress; - this.compressionLevel = compressionLevel; + this.compressionSettings = compressionSettings; this.cookieDecoder = decoder; this.cookieEncoder = encoder; this.formDecoderProvider = formDecoderProvider; @@ -134,7 +134,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { listener, request, compress, - compressionLevel, + compressionSettings, connectionInfo, cookieDecoder, cookieEncoder, diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpCompressionSettingsSpec.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpCompressionSettingsSpec.java new file mode 100644 index 0000000000..a88701c854 --- /dev/null +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpCompressionSettingsSpec.java @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2020-2023 VMware, Inc. or its affiliates, All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package reactor.netty.http.server; + +import java.util.ArrayList; +import java.util.List; + +import io.netty.handler.codec.compression.Brotli; +import io.netty.handler.codec.compression.BrotliOptions; +import io.netty.handler.codec.compression.CompressionOptions; +import io.netty.handler.codec.compression.DeflateOptions; +import io.netty.handler.codec.compression.GzipOptions; +import io.netty.handler.codec.compression.SnappyOptions; +import io.netty.handler.codec.compression.StandardCompressionOptions; +import io.netty.handler.codec.compression.Zstd; +import io.netty.handler.codec.compression.ZstdOptions; +import io.netty.util.internal.ObjectUtil; + +/** + * HTTP Compression configuration builder for the {@link SimpleCompressionHandler}. + * + * @author raccoonback + */ +public final class HttpCompressionSettingsSpec { + + private final GzipOptions gzipOptions; + private final DeflateOptions deflateOptions; + private final SnappyOptions snappyOptions; + private BrotliOptions brotliOptions; + private ZstdOptions zstdOptions; + + private HttpCompressionSettingsSpec() { + gzipOptions = StandardCompressionOptions.gzip(); + deflateOptions = StandardCompressionOptions.deflate(); + snappyOptions = StandardCompressionOptions.snappy(); + + if (Brotli.isAvailable()) { + brotliOptions = StandardCompressionOptions.brotli(); + } + + if (Zstd.isAvailable()) { + zstdOptions = StandardCompressionOptions.zstd(); + } + } + + private HttpCompressionSettingsSpec(Build build) { + gzipOptions = build.gzipOptions; + deflateOptions = build.gzipOptions; + snappyOptions = StandardCompressionOptions.snappy(); + + if (Brotli.isAvailable()) { + brotliOptions = StandardCompressionOptions.brotli(); + } + + if (Zstd.isAvailable() && build.zstdOptions != null) { + zstdOptions = build.zstdOptions; + } + } + + /** + * Creates a builder for {@link HttpCompressionSettingsSpec}. + * + * @return a new {@link HttpCompressionSettingsSpec.Builder} + */ + public static Builder builder() { + return new Build(); + } + + static HttpCompressionSettingsSpec provideDefault() { + return new HttpCompressionSettingsSpec(); + } + + CompressionOptions[] adaptToOptions() { + List options = new ArrayList<>(); + options.add(this.gzipOptions); + options.add(this.deflateOptions); + options.add(this.snappyOptions); + + if (brotliOptions != null) { + options.add(this.brotliOptions); + } + + if (zstdOptions != null) { + options.add(this.zstdOptions); + } + + return options.toArray(new CompressionOptions[0]); + } + + public interface Builder { + + /** + * Build a new {@link HttpCompressionSettingsSpec}. + * + * @return a new {@link HttpCompressionSettingsSpec} + */ + HttpCompressionSettingsSpec build(); + + /** + * Sets the gzip compression level. + * + * @return a new {@link HttpCompressionSettingsSpec.Builder} + */ + Builder gzip(int compressionLevel); + + /** + * Sets the deflate compression level. + * + * @return a new {@link HttpCompressionSettingsSpec.Builder} + */ + Builder deflate(int compressionLevel); + + /** + * Sets the zstd compression level. + * + * @return a new {@link HttpCompressionSettingsSpec.Builder} + */ + Builder zstd(int compressionLevel); + } + + private static final class Build implements Builder { + + GzipOptions gzipOptions = StandardCompressionOptions.gzip(); + DeflateOptions deflateOptions = StandardCompressionOptions.deflate(); + ZstdOptions zstdOptions; + + private static final int DEFLATE_DEFAULT_WINDOW_BITS = 15; + private static final int DEFLATE_DEFAULT_MEMORY_LEVEL = 8; + private static final int ZSTD_DEFAULT_COMPRESSION_LEVEL = 3; + private static final int ZSTD_DEFAULT_BLOCK_SIZE = 65536; + private static final int ZSTD_MAX_BLOCK_SIZE = 1 << ZSTD_DEFAULT_COMPRESSION_LEVEL + 7 + 15; + + @Override + public HttpCompressionSettingsSpec build() { + return new HttpCompressionSettingsSpec(this); + } + + @Override + public Builder gzip(int compressionLevel) { + ObjectUtil.checkInRange(compressionLevel, 0, 9, "compressionLevel"); + + gzipOptions = StandardCompressionOptions.gzip(compressionLevel, DEFLATE_DEFAULT_WINDOW_BITS, DEFLATE_DEFAULT_MEMORY_LEVEL); + return this; + } + + @Override + public Builder deflate(int compressionLevel) { + ObjectUtil.checkInRange(compressionLevel, 0, 9, "compressionLevel"); + + this.deflateOptions = StandardCompressionOptions.deflate(compressionLevel, DEFLATE_DEFAULT_WINDOW_BITS, DEFLATE_DEFAULT_MEMORY_LEVEL); + return this; + } + + @Override + public Builder zstd(int compressionLevel) { + if (!Zstd.isAvailable()) { + throw new IllegalStateException("Unable to set compression level on zstd."); + } + ObjectUtil.checkInRange(compressionLevel, -7, 22, "compressionLevel"); + + this.zstdOptions = StandardCompressionOptions.zstd(compressionLevel, ZSTD_DEFAULT_BLOCK_SIZE, ZSTD_MAX_BLOCK_SIZE); + return this; + } + } +} diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServer.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServer.java index 6e12527a4b..8f9cbdc82d 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServer.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServer.java @@ -35,7 +35,6 @@ import io.netty.handler.ssl.OpenSsl; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.util.SelfSignedCertificate; -import io.netty.util.internal.ObjectUtil; import org.reactivestreams.Publisher; import reactor.core.publisher.Mono; import reactor.netty.Connection; @@ -309,28 +308,9 @@ public final HttpServer compress(BiPredicate + * {@code + * HttpServer.create() + * .compress(true) + * .compressOptions( + * builder -> builder.gzip(6) + * .deflate(6) + * .zstd(3) + * ) + * .bindNow(); + * } + * + * @return a new {@link HttpServer} + */ + public final HttpServer compressSettings(Consumer compressionSettings) { + Objects.requireNonNull(compressionSettings, "compressionSettings"); + + HttpCompressionSettingsSpec.Builder builder = HttpCompressionSettingsSpec.builder(); + compressionSettings.accept(builder); + + HttpServer dup = duplicate(); + dup.configuration().compressionSettings = builder.build(); + return dup; + } + /** * Configure the * {@link ServerCookieEncoder}; {@link ServerCookieDecoder} will be diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerConfig.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerConfig.java index aad99b4c84..4e5b562241 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerConfig.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerConfig.java @@ -228,15 +228,6 @@ public int minCompressionSize() { return minCompressionSize; } - /** - * Returns the specified compression level. - * - * @return compression level - */ - public int compressionLevel() { - return compressionLevel; - } - /** * Return the HTTP protocol to support. Default is {@link HttpProtocol#HTTP11}. * @@ -335,7 +326,7 @@ public Function uriTagValue() { int maxKeepAliveRequests; Function methodTagValue; int minCompressionSize; - int compressionLevel; + HttpCompressionSettingsSpec compressionSettings; HttpProtocol[] protocols; int _protocols; ProxyProtocolSupportType proxyProtocolSupportType; @@ -354,7 +345,7 @@ public Function uriTagValue() { this.httpMessageLogFactory = ReactorNettyHttpMessageLogFactory.INSTANCE; this.maxKeepAliveRequests = -1; this.minCompressionSize = -1; - this.compressionLevel = 6; + this.compressionSettings = HttpCompressionSettingsSpec.provideDefault(); this.protocols = new HttpProtocol[]{HttpProtocol.HTTP11}; this._protocols = h11; this.proxyProtocolSupportType = ProxyProtocolSupportType.OFF; @@ -379,7 +370,7 @@ public Function uriTagValue() { this.maxKeepAliveRequests = parent.maxKeepAliveRequests; this.methodTagValue = parent.methodTagValue; this.minCompressionSize = parent.minCompressionSize; - this.compressionLevel = parent.compressionLevel; + this.compressionSettings = parent.compressionSettings; this.protocols = parent.protocols; this._protocols = parent._protocols; this.proxyProtocolSupportType = parent.proxyProtocolSupportType; @@ -504,7 +495,7 @@ static void addStreamHandlers(Channel ch, @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - int compressionLevel, + HttpCompressionSettingsSpec compressionSettings, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -515,14 +506,14 @@ static void addStreamHandlers(Channel ch, } pipeline.addLast(NettyPipeline.H2ToHttp11Codec, HTTP2_STREAM_FRAME_TO_HTTP_OBJECT) .addLast(NettyPipeline.HttpTrafficHandler, - new Http2StreamBridgeServerHandler(compressPredicate, compressionLevel, decoder, encoder, formDecoderProvider, + new Http2StreamBridgeServerHandler(compressPredicate, compressionSettings, decoder, encoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, listener, mapHandle, readTimeout, requestTimeout)); boolean alwaysCompress = compressPredicate == null && minCompressionSize == 0; if (alwaysCompress) { - pipeline.addLast(NettyPipeline.CompressionHandler, SimpleCompressionHandler.create(compressionLevel)); + pipeline.addLast(NettyPipeline.CompressionHandler, SimpleCompressionHandler.create(compressionSettings)); } ChannelOperations.addReactiveBridge(ch, opsFactory, listener); @@ -618,7 +609,7 @@ static void configureHttp3Pipeline( @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - int compressionLevel, + HttpCompressionSettingsSpec compressionSettings, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -628,7 +619,7 @@ static void configureHttp3Pipeline( p.addLast(NettyPipeline.HttpCodec, newHttp3ServerConnectionHandler(accessLogEnabled, accessLog, compressPredicate, cookieDecoder, cookieEncoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, - listener, mapHandle, methodTagValue, metricsRecorder, minCompressionSize, compressionLevel, opsFactory, readTimeout, + listener, mapHandle, methodTagValue, metricsRecorder, minCompressionSize, compressionSettings, opsFactory, readTimeout, requestTimeout, uriTagValue, validate)); if (metricsRecorder != null) { @@ -654,7 +645,7 @@ static void configureH2Pipeline(ChannelPipeline p, @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - int compressionLevel, + HttpCompressionSettingsSpec compressionSettings, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -691,7 +682,7 @@ static void configureH2Pipeline(ChannelPipeline p, .addLast(NettyPipeline.H2MultiplexHandler, new Http2MultiplexHandler(new H2Codec(accessLogEnabled, accessLog, compressPredicate, cookieDecoder, cookieEncoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, listener, - mapHandle, methodTagValue, metricsRecorder, minCompressionSize, compressionLevel, opsFactory, readTimeout, requestTimeout, uriTagValue))); + mapHandle, methodTagValue, metricsRecorder, minCompressionSize, compressionSettings, opsFactory, readTimeout, requestTimeout, uriTagValue))); IdleTimeoutHandler.addIdleTimeoutHandler(p, idleTimeout); @@ -725,7 +716,7 @@ static void configureHttp11OrH2CleartextPipeline(ChannelPipeline p, @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - int compressionLevel, + HttpCompressionSettingsSpec compressionSettings, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -744,7 +735,7 @@ static void configureHttp11OrH2CleartextPipeline(ChannelPipeline p, Http11OrH2CleartextCodec upgrader = new Http11OrH2CleartextCodec(accessLogEnabled, accessLog, compressPredicate, cookieDecoder, cookieEncoder, p.get(NettyPipeline.LoggingHandler) != null, enableGracefulShutdown, formDecoderProvider, forwardedHeaderHandler, http2SettingsSpec, httpMessageLogFactory, listener, mapHandle, methodTagValue, metricsRecorder, - minCompressionSize, compressionLevel, opsFactory, readTimeout, requestTimeout, uriTagValue, decoder.validateHeaders()); + minCompressionSize, compressionSettings, opsFactory, readTimeout, requestTimeout, uriTagValue, decoder.validateHeaders()); ChannelHandler http2ServerHandler = new H2CleartextCodec(upgrader, http2SettingsSpec != null ? http2SettingsSpec.maxStreams() : null); @@ -759,7 +750,7 @@ static void configureHttp11OrH2CleartextPipeline(ChannelPipeline p, NettyPipeline.H2CUpgradeHandler, h2cUpgradeHandler) .addBefore(NettyPipeline.ReactiveBridge, NettyPipeline.HttpTrafficHandler, - new HttpTrafficHandler(compressPredicate, compressionLevel, cookieDecoder, cookieEncoder, formDecoderProvider, + new HttpTrafficHandler(compressPredicate, compressionSettings, cookieDecoder, cookieEncoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, idleTimeout, listener, mapHandle, maxKeepAliveRequests, readTimeout, requestTimeout, decoder.validateHeaders())); @@ -770,7 +761,7 @@ static void configureHttp11OrH2CleartextPipeline(ChannelPipeline p, boolean alwaysCompress = compressPredicate == null && minCompressionSize == 0; if (alwaysCompress) { - p.addBefore(NettyPipeline.HttpTrafficHandler, NettyPipeline.CompressionHandler, SimpleCompressionHandler.create(compressionLevel)); + p.addBefore(NettyPipeline.HttpTrafficHandler, NettyPipeline.CompressionHandler, SimpleCompressionHandler.create(compressionSettings)); } if (metricsRecorder != null) { @@ -814,7 +805,7 @@ static void configureHttp11Pipeline(ChannelPipeline p, @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - int compressionLevel, + HttpCompressionSettingsSpec compressionSettings, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @Nullable Function uriTagValue) { @@ -831,7 +822,7 @@ static void configureHttp11Pipeline(ChannelPipeline p, new HttpServerCodec(decoderConfig)) .addBefore(NettyPipeline.ReactiveBridge, NettyPipeline.HttpTrafficHandler, - new HttpTrafficHandler(compressPredicate, compressionLevel, cookieDecoder, cookieEncoder, formDecoderProvider, + new HttpTrafficHandler(compressPredicate, compressionSettings, cookieDecoder, cookieEncoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, idleTimeout, listener, mapHandle, maxKeepAliveRequests, readTimeout, requestTimeout, decoder.validateHeaders())); @@ -842,7 +833,7 @@ static void configureHttp11Pipeline(ChannelPipeline p, boolean alwaysCompress = compressPredicate == null && minCompressionSize == 0; if (alwaysCompress) { - p.addBefore(NettyPipeline.HttpTrafficHandler, NettyPipeline.CompressionHandler, SimpleCompressionHandler.create(compressionLevel)); + p.addBefore(NettyPipeline.HttpTrafficHandler, NettyPipeline.CompressionHandler, SimpleCompressionHandler.create(compressionSettings)); } if (metricsRecorder != null) { @@ -1025,7 +1016,7 @@ static final class H2Codec extends ChannelInitializer { final Function methodTagValue; final ChannelMetricsRecorder metricsRecorder; final int minCompressionSize; - final int compressionLevel; + final HttpCompressionSettingsSpec compressionSettings; final ChannelOperations.OnSetup opsFactory; final Duration readTimeout; final Duration requestTimeout; @@ -1045,7 +1036,7 @@ static final class H2Codec extends ChannelInitializer { @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - int compressionLevel, + HttpCompressionSettingsSpec compressionSettings, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -1063,7 +1054,7 @@ static final class H2Codec extends ChannelInitializer { this.methodTagValue = methodTagValue; this.metricsRecorder = metricsRecorder; this.minCompressionSize = minCompressionSize; - this.compressionLevel = compressionLevel; + this.compressionSettings = compressionSettings; this.opsFactory = opsFactory; this.readTimeout = readTimeout; this.requestTimeout = requestTimeout; @@ -1075,7 +1066,7 @@ protected void initChannel(Channel ch) { ch.pipeline().remove(this); addStreamHandlers(ch, accessLogEnabled, accessLog, compressPredicate, cookieDecoder, cookieEncoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, listener, mapHandle, methodTagValue, metricsRecorder, - minCompressionSize, compressionLevel, opsFactory, readTimeout, requestTimeout, uriTagValue); + minCompressionSize, compressionSettings, opsFactory, readTimeout, requestTimeout, uriTagValue); } } @@ -1098,7 +1089,7 @@ static final class Http11OrH2CleartextCodec extends ChannelInitializer final Function methodTagValue; final ChannelMetricsRecorder metricsRecorder; final int minCompressionSize; - final int compressionLevel; + final HttpCompressionSettingsSpec compressionSettings; final ChannelOperations.OnSetup opsFactory; final Duration readTimeout; final Duration requestTimeout; @@ -1121,7 +1112,7 @@ static final class Http11OrH2CleartextCodec extends ChannelInitializer @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - int compressionLevel, + HttpCompressionSettingsSpec compressionSettings, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -1162,7 +1153,7 @@ static final class Http11OrH2CleartextCodec extends ChannelInitializer this.methodTagValue = methodTagValue; this.metricsRecorder = metricsRecorder; this.minCompressionSize = minCompressionSize; - this.compressionLevel = compressionLevel; + this.compressionSettings = compressionSettings; this.opsFactory = opsFactory; this.readTimeout = readTimeout; this.requestTimeout = requestTimeout; @@ -1177,7 +1168,7 @@ protected void initChannel(Channel ch) { ch.pipeline().remove(this); addStreamHandlers(ch, accessLogEnabled, accessLog, compressPredicate, cookieDecoder, cookieEncoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, listener, mapHandle, methodTagValue, - metricsRecorder, minCompressionSize, compressionLevel, opsFactory, readTimeout, requestTimeout, uriTagValue); + metricsRecorder, minCompressionSize, compressionSettings, opsFactory, readTimeout, requestTimeout, uriTagValue); } @Override @@ -1236,7 +1227,7 @@ static final class H2OrHttp11Codec extends ApplicationProtocolNegotiationHandler final Function methodTagValue; final ChannelMetricsRecorder metricsRecorder; final int minCompressionSize; - final int compressionLevel; + final HttpCompressionSettingsSpec compressionSettings; final ChannelOperations.OnSetup opsFactory; final Duration readTimeout; final Duration requestTimeout; @@ -1267,7 +1258,7 @@ static final class H2OrHttp11Codec extends ApplicationProtocolNegotiationHandler this.methodTagValue = initializer.methodTagValue; this.metricsRecorder = initializer.metricsRecorder; this.minCompressionSize = initializer.minCompressionSize; - this.compressionLevel = initializer.compressionLevel; + this.compressionSettings = initializer.compressionSettings; this.opsFactory = initializer.opsFactory; this.readTimeout = initializer.readTimeout; this.requestTimeout = initializer.requestTimeout; @@ -1286,7 +1277,7 @@ protected void configurePipeline(ChannelHandlerContext ctx, String protocol) { if (ApplicationProtocolNames.HTTP_2.equals(protocol)) { configureH2Pipeline(p, accessLogEnabled, accessLog, compressPredicate, cookieDecoder, cookieEncoder, enableGracefulShutdown, formDecoderProvider, forwardedHeaderHandler, http2SettingsSpec, httpMessageLogFactory, idleTimeout, - listener, mapHandle, methodTagValue, metricsRecorder, minCompressionSize, compressionLevel, opsFactory, readTimeout, requestTimeout, + listener, mapHandle, methodTagValue, metricsRecorder, minCompressionSize, compressionSettings, opsFactory, readTimeout, requestTimeout, uriTagValue, decoder.validateHeaders()); return; } @@ -1294,7 +1285,7 @@ protected void configurePipeline(ChannelHandlerContext ctx, String protocol) { if (!supportOnlyHttp2 && ApplicationProtocolNames.HTTP_1_1.equals(protocol)) { configureHttp11Pipeline(p, accessLogEnabled, accessLog, compressPredicate, cookieDecoder, cookieEncoder, true, decoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, idleTimeout, listener, - mapHandle, maxKeepAliveRequests, methodTagValue, metricsRecorder, minCompressionSize, compressionLevel, readTimeout, requestTimeout, uriTagValue); + mapHandle, maxKeepAliveRequests, methodTagValue, metricsRecorder, minCompressionSize, compressionSettings, readTimeout, requestTimeout, uriTagValue); // When the server is configured with HTTP/1.1 and H2 and HTTP/1.1 is negotiated, // when channelActive event happens, this HttpTrafficHandler is still not in the pipeline, @@ -1327,7 +1318,7 @@ static final class HttpServerChannelInitializer implements ChannelPipelineConfig final Function methodTagValue; final ChannelMetricsRecorder metricsRecorder; final int minCompressionSize; - final int compressionLevel; + final HttpCompressionSettingsSpec compressionSettings; final ChannelOperations.OnSetup opsFactory; final int protocols; final ProxyProtocolSupportType proxyProtocolSupportType; @@ -1355,7 +1346,7 @@ static final class HttpServerChannelInitializer implements ChannelPipelineConfig this.methodTagValue = config.methodTagValue; this.metricsRecorder = config.metricsRecorderInternal(); this.minCompressionSize = config.minCompressionSize; - this.compressionLevel = config.compressionLevel; + this.compressionSettings = config.compressionSettings; this.opsFactory = config.channelOperationsProvider(); this.protocols = config._protocols; this.proxyProtocolSupportType = config.proxyProtocolSupportType; @@ -1410,7 +1401,7 @@ else if ((protocols & h11) == h11) { methodTagValue, metricsRecorder, minCompressionSize, - compressionLevel, + compressionSettings, readTimeout, requestTimeout, uriTagValue); @@ -1442,7 +1433,7 @@ else if ((protocols & h2) == h2) { methodTagValue, metricsRecorder, minCompressionSize, - compressionLevel, + compressionSettings, opsFactory, readTimeout, requestTimeout, @@ -1466,7 +1457,7 @@ else if ((protocols & h3) == h3) { methodTagValue, metricsRecorder, minCompressionSize, - compressionLevel, + compressionSettings, opsFactory, readTimeout, requestTimeout, @@ -1496,7 +1487,7 @@ else if ((protocols & h3) == h3) { methodTagValue, metricsRecorder, minCompressionSize, - compressionLevel, + compressionSettings, opsFactory, readTimeout, requestTimeout, @@ -1522,7 +1513,7 @@ else if ((protocols & h11) == h11) { methodTagValue, metricsRecorder, minCompressionSize, - compressionLevel, + compressionSettings, readTimeout, requestTimeout, uriTagValue); @@ -1546,7 +1537,7 @@ else if ((protocols & h2c) == h2c) { methodTagValue, metricsRecorder, minCompressionSize, - compressionLevel, + compressionSettings, opsFactory, readTimeout, requestTimeout, diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java index 9740b42f16..3acffaf6a8 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java @@ -138,7 +138,7 @@ class HttpServerOperations extends HttpOperations compressionPredicate; - int compressionLevel; + HttpCompressionSettingsSpec compressionSettings; boolean isWebsocket; Function> paramsResolver; String path; @@ -152,7 +152,7 @@ class HttpServerOperations extends HttpOperations compressionPredicate, - int compressionLevel, + HttpCompressionSettingsSpec compressionSettings, ConnectionInfo connectionInfo, ServerCookieDecoder decoder, ServerCookieEncoder encoder, @@ -195,7 +195,7 @@ class HttpServerOperations extends HttpOperations compress; - final int compressionLevel; + final HttpCompressionSettingsSpec compressionSettings; final ServerCookieDecoder cookieDecoder; final ServerCookieEncoder cookieEncoder; final HttpServerFormDecoderProvider formDecoderProvider; @@ -113,7 +113,7 @@ final class HttpTrafficHandler extends ChannelDuplexHandler implements Runnable HttpTrafficHandler( @Nullable BiPredicate compress, - int compressionLevel, + HttpCompressionSettingsSpec compressionSettings, ServerCookieDecoder decoder, ServerCookieEncoder encoder, HttpServerFormDecoderProvider formDecoderProvider, @@ -130,7 +130,7 @@ final class HttpTrafficHandler extends ChannelDuplexHandler implements Runnable this.formDecoderProvider = formDecoderProvider; this.forwardedHeaderHandler = forwardedHeaderHandler; this.compress = compress; - this.compressionLevel = compressionLevel; + this.compressionSettings = compressionSettings; this.cookieEncoder = encoder; this.cookieDecoder = decoder; this.httpMessageLogFactory = httpMessageLogFactory; @@ -246,7 +246,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { listener, request, compress, - compressionLevel, + compressionSettings, connectionInfo, cookieDecoder, cookieEncoder, @@ -599,7 +599,7 @@ public void run() { listener, nextRequest, compress, - compressionLevel, + compressionSettings, connectionInfo, cookieDecoder, cookieEncoder, diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/SimpleCompressionHandler.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/SimpleCompressionHandler.java index f47c0d0a8f..36f4872a4b 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/SimpleCompressionHandler.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/SimpleCompressionHandler.java @@ -22,10 +22,7 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; import io.netty.handler.codec.DecoderException; -import io.netty.handler.codec.compression.Brotli; import io.netty.handler.codec.compression.CompressionOptions; -import io.netty.handler.codec.compression.StandardCompressionOptions; -import io.netty.handler.codec.compression.Zstd; import io.netty.handler.codec.http.DefaultHttpContent; import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; @@ -34,7 +31,6 @@ import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.LastHttpContent; import io.netty.util.ReferenceCountUtil; -import io.netty.util.internal.ObjectUtil; /** * {@link HttpContentCompressor} to enable on-demand compression. @@ -50,23 +46,9 @@ private SimpleCompressionHandler(CompressionOptions... options) { super(options); } - static SimpleCompressionHandler create(int compressionLevel) { - ObjectUtil.checkInRange(compressionLevel, 0, 9, "compressionLevel"); - - List options = new ArrayList<>(); - options.add(StandardCompressionOptions.gzip(compressionLevel, 15, 8)); - options.add(StandardCompressionOptions.deflate(compressionLevel, 15, 8)); - options.add(StandardCompressionOptions.snappy()); - - if (Zstd.isAvailable()) { - options.add(StandardCompressionOptions.zstd()); - } - if (Brotli.isAvailable()) { - options.add(StandardCompressionOptions.brotli()); - } - + static SimpleCompressionHandler create(HttpCompressionSettingsSpec compressionSettings) { return new SimpleCompressionHandler( - options.toArray(new CompressionOptions[0]) + compressionSettings.adaptToOptions() ); } diff --git a/reactor-netty-http/src/test/java/reactor/netty/http/HttpCompressionClientServerTests.java b/reactor-netty-http/src/test/java/reactor/netty/http/HttpCompressionClientServerTests.java index 1e77a60432..dd3ea652b9 100644 --- a/reactor-netty-http/src/test/java/reactor/netty/http/HttpCompressionClientServerTests.java +++ b/reactor-netty-http/src/test/java/reactor/netty/http/HttpCompressionClientServerTests.java @@ -723,15 +723,14 @@ void serverCompressionEnabledResponseCompressionDisabled(HttpServer server, Http } @ParameterizedCompressionTest - void serverCompressionEnabledByCompressionLevel(HttpServer server, HttpClient client) { + void serverCompressionWithCompressionLevelSettings(HttpServer server, HttpClient client) { disposableServer = - server.compress(true, 4) + server.compress(true) + .compressSettings(builder -> builder.gzip(4)) .handle((in, out) -> out.sendString(Mono.just("reply"))) .bindNow(Duration.ofSeconds(10)); - //don't activate compression on the client options to avoid auto-handling (which removes the header) Tuple2 resp = - //edit the header manually to attempt to trigger compression on server side client.port(disposableServer.port()) .headers(h -> h.add("accept-encoding", "gzip")) .get() @@ -761,4 +760,68 @@ void serverCompressionEnabledByCompressionLevel(HttpServer server, HttpClient cl assertThat(resp).isNotNull(); assertThat(resp.getT1()).startsWith(result); // Ignore the original data size and crc checksum comparison } + + @ParameterizedCompressionTest + void serverCompressionEnabledWithGzipCompressionLevelSettings(HttpServer server, HttpClient client) throws Exception { + disposableServer = + server.compress(true) + .compressSettings(builder -> builder.gzip(4)) + .handle((in, out) -> out.sendString(Mono.just("reply"))) + .bindNow(Duration.ofSeconds(10)); + + Tuple2 resp = + client.port(disposableServer.port()) + .headers(h -> h.add("accept-encoding", "gzip")) + .get() + .uri("/test") + .responseSingle((res, buf) -> buf.asByteArray() + .zipWith(Mono.just(res.responseHeaders()))) + .block(Duration.ofSeconds(10)); + + assertThat(resp).isNotNull(); + assertThat(resp.getT2().get("content-encoding")).isEqualTo("gzip"); + + assertThat(new String(resp.getT1(), Charset.defaultCharset())).isNotEqualTo("reply"); + + GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(resp.getT1())); + byte[] deflatedBuf = new byte[1024]; + int readable = gis.read(deflatedBuf); + gis.close(); + + assertThat(readable).isGreaterThan(0); + + String deflated = new String(deflatedBuf, 0, readable, Charset.defaultCharset()); + + assertThat(deflated).isEqualTo("reply"); + } + + @ParameterizedCompressionTest + void serverCompressionEnabledWithZstdCompressionLevel(HttpServer server, HttpClient client) { + assertThat(Zstd.isAvailable()).isTrue(); + disposableServer = + server.compress(true) + .compressSettings(builder -> builder.zstd(22)) + .handle((in, out) -> out.sendString(Mono.just("reply"))) + .bindNow(Duration.ofSeconds(10)); + + Tuple2 resp = + client.port(disposableServer.port()) + .compress(false) + .headers(h -> h.add("Accept-Encoding", "zstd")) + .get() + .uri("/test") + .responseSingle((res, buf) -> buf.asByteArray() + .zipWith(Mono.just(res.responseHeaders()))) + .block(Duration.ofSeconds(10)); + + assertThat(resp).isNotNull(); + assertThat(resp.getT2().get("content-encoding")).isEqualTo("zstd"); + + final byte[] compressedData = resp.getT1(); + assertThat(new String(compressedData, Charset.defaultCharset())).isNotEqualTo("reply"); + + final byte[] decompressedData = com.github.luben.zstd.Zstd.decompress(compressedData, 1_000); + assertThat(decompressedData).isNotEmpty(); + assertThat(new String(decompressedData, Charset.defaultCharset())).isEqualTo("reply"); + } } diff --git a/reactor-netty-http/src/test/java/reactor/netty/http/server/HttpServerTests.java b/reactor-netty-http/src/test/java/reactor/netty/http/server/HttpServerTests.java index b428a69023..5eb027d1f7 100644 --- a/reactor-netty-http/src/test/java/reactor/netty/http/server/HttpServerTests.java +++ b/reactor-netty-http/src/test/java/reactor/netty/http/server/HttpServerTests.java @@ -2214,7 +2214,7 @@ private void doTestStatus(HttpResponseStatus status) { ConnectionObserver.emptyListener(), new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"), null, - 6, + HttpCompressionSettingsSpec.provideDefault(), new ConnectionInfo(localSocketAddress, DEFAULT_HOST_NAME, DEFAULT_HTTP_PORT, remoteSocketAddress, "http", true), ServerCookieDecoder.STRICT, ServerCookieEncoder.STRICT, @@ -3273,7 +3273,7 @@ private void doTestIsFormUrlencoded(String headerValue, boolean expectation) { ConnectionObserver.emptyListener(), request, null, - 6, + HttpCompressionSettingsSpec.provideDefault(), new ConnectionInfo(localSocketAddress, DEFAULT_HOST_NAME, DEFAULT_HTTP_PORT, remoteSocketAddress, "http", true), ServerCookieDecoder.STRICT, ServerCookieEncoder.STRICT, From 38f3bccb3181cb693e779c0a34e59a3b7ba60291 Mon Sep 17 00:00:00 2001 From: raccoonback Date: Sun, 29 Dec 2024 17:26:31 +0900 Subject: [PATCH 3/8] Write a guide document fix: https://github.com/reactor/reactor-netty/issues/3244 --- docs/modules/ROOT/pages/http-server.adoc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/modules/ROOT/pages/http-server.adoc b/docs/modules/ROOT/pages/http-server.adoc index c4c64fb42f..1ecb645763 100644 --- a/docs/modules/ROOT/pages/http-server.adoc +++ b/docs/modules/ROOT/pages/http-server.adoc @@ -162,6 +162,16 @@ You can configure the `HTTP` server to send a compressed response, depending on * `compress(int)`: The compression is performed once the response size exceeds the given value (in bytes). * `compress(BiPredicate)`: The compression is performed if the predicate returns `true`. +* `compressSettings(Consumer compressionSettings`: Specifies the compression level for GZIP, DEFLATE, and ZSTD. ++ +[NOTE] +==== +Supported Compression Levels + +* gzip : only the range 0 to 9 is allowed. (default: 6) +* deflate : only the range 0 to 9 is allowed. (default: 6) +* zstd : only the range -7 to 22 is allowed. (default: 3) +==== The following example uses the `compress` method (set to `true`) to enable compression: From 2c653c1e9a3f064f65893929607c8a1c404acf4f Mon Sep 17 00:00:00 2001 From: raccoonback Date: Tue, 7 Jan 2025 23:55:36 +0900 Subject: [PATCH 4/8] Refactor compression options interface to follow Netty's style - Updated the interface to accept `CompressionOptions[]` instead of using method overloading. - Aligns with Netty's design for consistency and maintainability, addressing feedback from PR #3567. --- docs/modules/ROOT/pages/http-server.adoc | 4 +- .../Http2StreamBridgeServerHandler.java | 9 +- .../reactor/netty/http/server/Http3Codec.java | 15 +- .../http/server/Http3ServerOperations.java | 5 +- .../Http3StreamBridgeServerHandler.java | 9 +- .../server/HttpCompressionSettingsSpec.java | 178 ------------------ .../reactor/netty/http/server/HttpServer.java | 35 ++-- .../netty/http/server/HttpServerConfig.java | 75 ++++---- .../http/server/HttpServerOperations.java | 13 +- .../netty/http/server/HttpTrafficHandler.java | 11 +- .../http/server/SimpleCompressionHandler.java | 5 +- .../http/server/compression/BrotliOption.java | 37 ++++ .../server/compression/DeflateOption.java | 45 +++++ .../http/server/compression/GzipOption.java | 45 +++++ .../compression/HttpCompressionOption.java | 24 +++ .../HttpCompressionOptionsSpec.java | 100 ++++++++++ .../http/server/compression/SnappyOption.java | 37 ++++ .../StandardHttpCompressionOptions.java | 129 +++++++++++++ .../http/server/compression/ZstdOption.java | 45 +++++ .../HttpCompressionClientServerTests.java | 7 +- .../netty/http/server/HttpServerTests.java | 5 +- 21 files changed, 562 insertions(+), 271 deletions(-) delete mode 100644 reactor-netty-http/src/main/java/reactor/netty/http/server/HttpCompressionSettingsSpec.java create mode 100644 reactor-netty-http/src/main/java/reactor/netty/http/server/compression/BrotliOption.java create mode 100644 reactor-netty-http/src/main/java/reactor/netty/http/server/compression/DeflateOption.java create mode 100644 reactor-netty-http/src/main/java/reactor/netty/http/server/compression/GzipOption.java create mode 100644 reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOption.java create mode 100644 reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOptionsSpec.java create mode 100644 reactor-netty-http/src/main/java/reactor/netty/http/server/compression/SnappyOption.java create mode 100644 reactor-netty-http/src/main/java/reactor/netty/http/server/compression/StandardHttpCompressionOptions.java create mode 100644 reactor-netty-http/src/main/java/reactor/netty/http/server/compression/ZstdOption.java diff --git a/docs/modules/ROOT/pages/http-server.adoc b/docs/modules/ROOT/pages/http-server.adoc index 1ecb645763..29bd92c696 100644 --- a/docs/modules/ROOT/pages/http-server.adoc +++ b/docs/modules/ROOT/pages/http-server.adoc @@ -162,7 +162,7 @@ You can configure the `HTTP` server to send a compressed response, depending on * `compress(int)`: The compression is performed once the response size exceeds the given value (in bytes). * `compress(BiPredicate)`: The compression is performed if the predicate returns `true`. -* `compressSettings(Consumer compressionSettings`: Specifies the compression level for GZIP, DEFLATE, and ZSTD. +* `compressOptions(HttpCompressionOption... compressionOptions)`: Specifies the compression level for GZIP, DEFLATE, and ZSTD. + [NOTE] ==== @@ -170,7 +170,7 @@ Supported Compression Levels * gzip : only the range 0 to 9 is allowed. (default: 6) * deflate : only the range 0 to 9 is allowed. (default: 6) -* zstd : only the range -7 to 22 is allowed. (default: 3) +* zstd : only the range -131072 to 22 is allowed. (default: 3) ==== The following example uses the `compress` method (set to `true`) to enable compression: diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http2StreamBridgeServerHandler.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http2StreamBridgeServerHandler.java index 6a6251e6f5..0b783e652b 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http2StreamBridgeServerHandler.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http2StreamBridgeServerHandler.java @@ -47,6 +47,7 @@ import reactor.netty.ReactorNetty; import reactor.netty.http.logging.HttpMessageArgProviderFactory; import reactor.netty.http.logging.HttpMessageLogFactory; +import reactor.netty.http.server.compression.HttpCompressionOptionsSpec; import reactor.util.annotation.Nullable; import static io.netty.handler.codec.http.LastHttpContent.EMPTY_LAST_CONTENT; @@ -62,7 +63,7 @@ final class Http2StreamBridgeServerHandler extends ChannelDuplexHandler { final BiPredicate compress; - final HttpCompressionSettingsSpec compressionSettings; + final HttpCompressionOptionsSpec compressionOptions; final ServerCookieDecoder cookieDecoder; final ServerCookieEncoder cookieEncoder; final HttpServerFormDecoderProvider formDecoderProvider; @@ -85,7 +86,7 @@ final class Http2StreamBridgeServerHandler extends ChannelDuplexHandler { Http2StreamBridgeServerHandler( @Nullable BiPredicate compress, - HttpCompressionSettingsSpec compressionSettings, + HttpCompressionOptionsSpec compressionOptions, ServerCookieDecoder decoder, ServerCookieEncoder encoder, HttpServerFormDecoderProvider formDecoderProvider, @@ -96,7 +97,7 @@ final class Http2StreamBridgeServerHandler extends ChannelDuplexHandler { @Nullable Duration readTimeout, @Nullable Duration requestTimeout) { this.compress = compress; - this.compressionSettings = compressionSettings; + this.compressionOptions = compressionOptions; this.cookieDecoder = decoder; this.cookieEncoder = encoder; this.formDecoderProvider = formDecoderProvider; @@ -143,7 +144,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { listener, request, compress, - compressionSettings, + compressionOptions, connectionInfo, cookieDecoder, cookieEncoder, diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3Codec.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3Codec.java index de0f6a2f9f..05d77d0a48 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3Codec.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3Codec.java @@ -31,6 +31,7 @@ import reactor.netty.channel.ChannelMetricsRecorder; import reactor.netty.channel.ChannelOperations; import reactor.netty.http.logging.HttpMessageLogFactory; +import reactor.netty.http.server.compression.HttpCompressionOptionsSpec; import reactor.netty.http.server.logging.AccessLog; import reactor.netty.http.server.logging.AccessLogArgProvider; import reactor.netty.http.server.logging.AccessLogHandlerFactory; @@ -63,7 +64,7 @@ final class Http3Codec extends ChannelInitializer { final Function methodTagValue; final ChannelMetricsRecorder metricsRecorder; final int minCompressionSize; - final HttpCompressionSettingsSpec compressionSettings; + final HttpCompressionOptionsSpec compressionOptions; final ChannelOperations.OnSetup opsFactory; final Duration readTimeout; final Duration requestTimeout; @@ -84,7 +85,7 @@ final class Http3Codec extends ChannelInitializer { @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - HttpCompressionSettingsSpec compressionSettings, + HttpCompressionOptionsSpec compressionOptions, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -103,7 +104,7 @@ final class Http3Codec extends ChannelInitializer { this.methodTagValue = methodTagValue; this.metricsRecorder = metricsRecorder; this.minCompressionSize = minCompressionSize; - this.compressionSettings = compressionSettings; + this.compressionOptions = compressionOptions; this.opsFactory = opsFactory; this.readTimeout = readTimeout; this.requestTimeout = requestTimeout; @@ -121,13 +122,13 @@ protected void initChannel(QuicStreamChannel channel) { p.addLast(NettyPipeline.H3ToHttp11Codec, new Http3FrameToHttpObjectCodec(true, validate)) .addLast(NettyPipeline.HttpTrafficHandler, - new Http3StreamBridgeServerHandler(compressPredicate, compressionSettings, cookieDecoder, cookieEncoder, formDecoderProvider, + new Http3StreamBridgeServerHandler(compressPredicate, compressionOptions, cookieDecoder, cookieEncoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, listener, mapHandle, readTimeout, requestTimeout)); boolean alwaysCompress = compressPredicate == null && minCompressionSize == 0; if (alwaysCompress) { - p.addLast(NettyPipeline.CompressionHandler, SimpleCompressionHandler.create(compressionSettings)); + p.addLast(NettyPipeline.CompressionHandler, SimpleCompressionHandler.create(compressionOptions)); } ChannelOperations.addReactiveBridge(channel, opsFactory, listener); @@ -169,7 +170,7 @@ static ChannelHandler newHttp3ServerConnectionHandler( @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - HttpCompressionSettingsSpec compressionSettings, + HttpCompressionOptionsSpec compressionOptions, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -177,7 +178,7 @@ static ChannelHandler newHttp3ServerConnectionHandler( boolean validate) { return new Http3ServerConnectionHandler( new Http3Codec(accessLogEnabled, accessLog, compressPredicate, decoder, encoder, formDecoderProvider, forwardedHeaderHandler, - httpMessageLogFactory, listener, mapHandle, methodTagValue, metricsRecorder, minCompressionSize, compressionSettings, + httpMessageLogFactory, listener, mapHandle, methodTagValue, metricsRecorder, minCompressionSize, compressionOptions, opsFactory, readTimeout, requestTimeout, uriTagValue, validate)); } } \ No newline at end of file diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3ServerOperations.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3ServerOperations.java index b36bae1d5c..0570357b44 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3ServerOperations.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3ServerOperations.java @@ -24,6 +24,7 @@ import reactor.netty.Connection; import reactor.netty.ConnectionObserver; import reactor.netty.http.logging.HttpMessageLogFactory; +import reactor.netty.http.server.compression.HttpCompressionOptionsSpec; import reactor.util.annotation.Nullable; import java.net.SocketAddress; @@ -43,7 +44,7 @@ final class Http3ServerOperations extends HttpServerOperations { ConnectionObserver listener, HttpRequest nettyRequest, @Nullable BiPredicate compressionPredicate, - HttpCompressionSettingsSpec compressionSettings, + HttpCompressionOptionsSpec compressionOptions, ConnectionInfo connectionInfo, ServerCookieDecoder decoder, ServerCookieEncoder encoder, @@ -55,7 +56,7 @@ final class Http3ServerOperations extends HttpServerOperations { @Nullable Duration requestTimeout, boolean secured, ZonedDateTime timestamp) { - super(c, listener, nettyRequest, compressionPredicate, compressionSettings, connectionInfo, decoder, encoder, formDecoderProvider, + super(c, listener, nettyRequest, compressionPredicate, compressionOptions, connectionInfo, decoder, encoder, formDecoderProvider, httpMessageLogFactory, isHttp2, mapHandle, readTimeout, requestTimeout, secured, timestamp, true); } diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3StreamBridgeServerHandler.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3StreamBridgeServerHandler.java index a33d199f85..2fcd949e44 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3StreamBridgeServerHandler.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3StreamBridgeServerHandler.java @@ -41,6 +41,7 @@ import reactor.netty.ReactorNetty; import reactor.netty.http.logging.HttpMessageArgProviderFactory; import reactor.netty.http.logging.HttpMessageLogFactory; +import reactor.netty.http.server.compression.HttpCompressionOptionsSpec; import reactor.util.annotation.Nullable; import java.net.SocketAddress; @@ -54,7 +55,7 @@ final class Http3StreamBridgeServerHandler extends ChannelDuplexHandler { final BiPredicate compress; - final HttpCompressionSettingsSpec compressionSettings; + final HttpCompressionOptionsSpec compressionOptions; final ServerCookieDecoder cookieDecoder; final ServerCookieEncoder cookieEncoder; final HttpServerFormDecoderProvider formDecoderProvider; @@ -75,7 +76,7 @@ final class Http3StreamBridgeServerHandler extends ChannelDuplexHandler { Http3StreamBridgeServerHandler( @Nullable BiPredicate compress, - HttpCompressionSettingsSpec compressionSettings, + HttpCompressionOptionsSpec compressionOptions, ServerCookieDecoder decoder, ServerCookieEncoder encoder, HttpServerFormDecoderProvider formDecoderProvider, @@ -86,7 +87,7 @@ final class Http3StreamBridgeServerHandler extends ChannelDuplexHandler { @Nullable Duration readTimeout, @Nullable Duration requestTimeout) { this.compress = compress; - this.compressionSettings = compressionSettings; + this.compressionOptions = compressionOptions; this.cookieDecoder = decoder; this.cookieEncoder = encoder; this.formDecoderProvider = formDecoderProvider; @@ -134,7 +135,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { listener, request, compress, - compressionSettings, + compressionOptions, connectionInfo, cookieDecoder, cookieEncoder, diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpCompressionSettingsSpec.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpCompressionSettingsSpec.java deleted file mode 100644 index a88701c854..0000000000 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpCompressionSettingsSpec.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (c) 2020-2023 VMware, Inc. or its affiliates, All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package reactor.netty.http.server; - -import java.util.ArrayList; -import java.util.List; - -import io.netty.handler.codec.compression.Brotli; -import io.netty.handler.codec.compression.BrotliOptions; -import io.netty.handler.codec.compression.CompressionOptions; -import io.netty.handler.codec.compression.DeflateOptions; -import io.netty.handler.codec.compression.GzipOptions; -import io.netty.handler.codec.compression.SnappyOptions; -import io.netty.handler.codec.compression.StandardCompressionOptions; -import io.netty.handler.codec.compression.Zstd; -import io.netty.handler.codec.compression.ZstdOptions; -import io.netty.util.internal.ObjectUtil; - -/** - * HTTP Compression configuration builder for the {@link SimpleCompressionHandler}. - * - * @author raccoonback - */ -public final class HttpCompressionSettingsSpec { - - private final GzipOptions gzipOptions; - private final DeflateOptions deflateOptions; - private final SnappyOptions snappyOptions; - private BrotliOptions brotliOptions; - private ZstdOptions zstdOptions; - - private HttpCompressionSettingsSpec() { - gzipOptions = StandardCompressionOptions.gzip(); - deflateOptions = StandardCompressionOptions.deflate(); - snappyOptions = StandardCompressionOptions.snappy(); - - if (Brotli.isAvailable()) { - brotliOptions = StandardCompressionOptions.brotli(); - } - - if (Zstd.isAvailable()) { - zstdOptions = StandardCompressionOptions.zstd(); - } - } - - private HttpCompressionSettingsSpec(Build build) { - gzipOptions = build.gzipOptions; - deflateOptions = build.gzipOptions; - snappyOptions = StandardCompressionOptions.snappy(); - - if (Brotli.isAvailable()) { - brotliOptions = StandardCompressionOptions.brotli(); - } - - if (Zstd.isAvailable() && build.zstdOptions != null) { - zstdOptions = build.zstdOptions; - } - } - - /** - * Creates a builder for {@link HttpCompressionSettingsSpec}. - * - * @return a new {@link HttpCompressionSettingsSpec.Builder} - */ - public static Builder builder() { - return new Build(); - } - - static HttpCompressionSettingsSpec provideDefault() { - return new HttpCompressionSettingsSpec(); - } - - CompressionOptions[] adaptToOptions() { - List options = new ArrayList<>(); - options.add(this.gzipOptions); - options.add(this.deflateOptions); - options.add(this.snappyOptions); - - if (brotliOptions != null) { - options.add(this.brotliOptions); - } - - if (zstdOptions != null) { - options.add(this.zstdOptions); - } - - return options.toArray(new CompressionOptions[0]); - } - - public interface Builder { - - /** - * Build a new {@link HttpCompressionSettingsSpec}. - * - * @return a new {@link HttpCompressionSettingsSpec} - */ - HttpCompressionSettingsSpec build(); - - /** - * Sets the gzip compression level. - * - * @return a new {@link HttpCompressionSettingsSpec.Builder} - */ - Builder gzip(int compressionLevel); - - /** - * Sets the deflate compression level. - * - * @return a new {@link HttpCompressionSettingsSpec.Builder} - */ - Builder deflate(int compressionLevel); - - /** - * Sets the zstd compression level. - * - * @return a new {@link HttpCompressionSettingsSpec.Builder} - */ - Builder zstd(int compressionLevel); - } - - private static final class Build implements Builder { - - GzipOptions gzipOptions = StandardCompressionOptions.gzip(); - DeflateOptions deflateOptions = StandardCompressionOptions.deflate(); - ZstdOptions zstdOptions; - - private static final int DEFLATE_DEFAULT_WINDOW_BITS = 15; - private static final int DEFLATE_DEFAULT_MEMORY_LEVEL = 8; - private static final int ZSTD_DEFAULT_COMPRESSION_LEVEL = 3; - private static final int ZSTD_DEFAULT_BLOCK_SIZE = 65536; - private static final int ZSTD_MAX_BLOCK_SIZE = 1 << ZSTD_DEFAULT_COMPRESSION_LEVEL + 7 + 15; - - @Override - public HttpCompressionSettingsSpec build() { - return new HttpCompressionSettingsSpec(this); - } - - @Override - public Builder gzip(int compressionLevel) { - ObjectUtil.checkInRange(compressionLevel, 0, 9, "compressionLevel"); - - gzipOptions = StandardCompressionOptions.gzip(compressionLevel, DEFLATE_DEFAULT_WINDOW_BITS, DEFLATE_DEFAULT_MEMORY_LEVEL); - return this; - } - - @Override - public Builder deflate(int compressionLevel) { - ObjectUtil.checkInRange(compressionLevel, 0, 9, "compressionLevel"); - - this.deflateOptions = StandardCompressionOptions.deflate(compressionLevel, DEFLATE_DEFAULT_WINDOW_BITS, DEFLATE_DEFAULT_MEMORY_LEVEL); - return this; - } - - @Override - public Builder zstd(int compressionLevel) { - if (!Zstd.isAvailable()) { - throw new IllegalStateException("Unable to set compression level on zstd."); - } - ObjectUtil.checkInRange(compressionLevel, -7, 22, "compressionLevel"); - - this.zstdOptions = StandardCompressionOptions.zstd(compressionLevel, ZSTD_DEFAULT_BLOCK_SIZE, ZSTD_MAX_BLOCK_SIZE); - return this; - } - } -} diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServer.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServer.java index 8f9cbdc82d..5fe9d5124c 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServer.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServer.java @@ -45,6 +45,8 @@ import reactor.netty.http.HttpProtocol; import reactor.netty.http.logging.HttpMessageLogFactory; import reactor.netty.http.logging.ReactorNettyHttpMessageLogFactory; +import reactor.netty.http.server.compression.HttpCompressionOption; +import reactor.netty.http.server.compression.HttpCompressionOptionsSpec; import reactor.netty.http.server.logging.AccessLog; import reactor.netty.http.server.logging.AccessLogArgProvider; import reactor.netty.http.server.logging.AccessLogFactory; @@ -340,29 +342,24 @@ public final HttpServer compress(int minResponseSize) { /** * Specifies GZIP, DEFLATE, ZSTD Compression Level. * - * @param compressionSettings configures {@link HttpCompressionSettingsSpec} after enable compress. - *
-	 * {@code
-	 *     HttpServer.create()
-	 *                      .compress(true)
-	 * 					.compressOptions(
-	 * 					    builder -> builder.gzip(6)
-	 * 					                        .deflate(6)
-	 * 					                        .zstd(3)
-	 * 					 )
-	 * 					.bindNow();
-	 * }
-	 * 
+ * @param compressionOptions configures {@link HttpCompressionOption} after enable compress. + *
+	 *  {@code
+	 *      HttpServer.create()
+	 *                       .compress(true)
+	 *  					.compressOptions(
+	 *                         StandardHttpCompressionOptions.gzip(6, 15, 8)
+	 *  					 )
+	 *  					.bindNow();
+	 *  }
+	 *  
* @return a new {@link HttpServer} */ - public final HttpServer compressSettings(Consumer compressionSettings) { - Objects.requireNonNull(compressionSettings, "compressionSettings"); - - HttpCompressionSettingsSpec.Builder builder = HttpCompressionSettingsSpec.builder(); - compressionSettings.accept(builder); + public final HttpServer compressOptions(HttpCompressionOption... compressionOptions) { + Objects.requireNonNull(compressionOptions, "compressionOptions"); HttpServer dup = duplicate(); - dup.configuration().compressionSettings = builder.build(); + dup.configuration().compressionOptions = new HttpCompressionOptionsSpec(compressionOptions); return dup; } diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerConfig.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerConfig.java index 4e5b562241..92906567ef 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerConfig.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerConfig.java @@ -68,6 +68,7 @@ import reactor.netty.http.HttpResources; import reactor.netty.http.logging.HttpMessageLogFactory; import reactor.netty.http.logging.ReactorNettyHttpMessageLogFactory; +import reactor.netty.http.server.compression.HttpCompressionOptionsSpec; import reactor.netty.http.server.logging.AccessLog; import reactor.netty.http.server.logging.AccessLogArgProvider; import reactor.netty.http.server.logging.AccessLogHandlerFactory; @@ -326,7 +327,7 @@ public Function uriTagValue() { int maxKeepAliveRequests; Function methodTagValue; int minCompressionSize; - HttpCompressionSettingsSpec compressionSettings; + HttpCompressionOptionsSpec compressionOptions; HttpProtocol[] protocols; int _protocols; ProxyProtocolSupportType proxyProtocolSupportType; @@ -345,7 +346,7 @@ public Function uriTagValue() { this.httpMessageLogFactory = ReactorNettyHttpMessageLogFactory.INSTANCE; this.maxKeepAliveRequests = -1; this.minCompressionSize = -1; - this.compressionSettings = HttpCompressionSettingsSpec.provideDefault(); + this.compressionOptions = HttpCompressionOptionsSpec.provideDefault(); this.protocols = new HttpProtocol[]{HttpProtocol.HTTP11}; this._protocols = h11; this.proxyProtocolSupportType = ProxyProtocolSupportType.OFF; @@ -370,7 +371,7 @@ public Function uriTagValue() { this.maxKeepAliveRequests = parent.maxKeepAliveRequests; this.methodTagValue = parent.methodTagValue; this.minCompressionSize = parent.minCompressionSize; - this.compressionSettings = parent.compressionSettings; + this.compressionOptions = parent.compressionOptions; this.protocols = parent.protocols; this._protocols = parent._protocols; this.proxyProtocolSupportType = parent.proxyProtocolSupportType; @@ -495,7 +496,7 @@ static void addStreamHandlers(Channel ch, @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - HttpCompressionSettingsSpec compressionSettings, + HttpCompressionOptionsSpec compressionOptions, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -506,14 +507,14 @@ static void addStreamHandlers(Channel ch, } pipeline.addLast(NettyPipeline.H2ToHttp11Codec, HTTP2_STREAM_FRAME_TO_HTTP_OBJECT) .addLast(NettyPipeline.HttpTrafficHandler, - new Http2StreamBridgeServerHandler(compressPredicate, compressionSettings, decoder, encoder, formDecoderProvider, + new Http2StreamBridgeServerHandler(compressPredicate, compressionOptions, decoder, encoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, listener, mapHandle, readTimeout, requestTimeout)); boolean alwaysCompress = compressPredicate == null && minCompressionSize == 0; if (alwaysCompress) { - pipeline.addLast(NettyPipeline.CompressionHandler, SimpleCompressionHandler.create(compressionSettings)); + pipeline.addLast(NettyPipeline.CompressionHandler, SimpleCompressionHandler.create(compressionOptions)); } ChannelOperations.addReactiveBridge(ch, opsFactory, listener); @@ -609,7 +610,7 @@ static void configureHttp3Pipeline( @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - HttpCompressionSettingsSpec compressionSettings, + HttpCompressionOptionsSpec compressionOptions, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -619,7 +620,7 @@ static void configureHttp3Pipeline( p.addLast(NettyPipeline.HttpCodec, newHttp3ServerConnectionHandler(accessLogEnabled, accessLog, compressPredicate, cookieDecoder, cookieEncoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, - listener, mapHandle, methodTagValue, metricsRecorder, minCompressionSize, compressionSettings, opsFactory, readTimeout, + listener, mapHandle, methodTagValue, metricsRecorder, minCompressionSize, compressionOptions, opsFactory, readTimeout, requestTimeout, uriTagValue, validate)); if (metricsRecorder != null) { @@ -645,7 +646,7 @@ static void configureH2Pipeline(ChannelPipeline p, @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - HttpCompressionSettingsSpec compressionSettings, + HttpCompressionOptionsSpec compressionOptions, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -682,7 +683,7 @@ static void configureH2Pipeline(ChannelPipeline p, .addLast(NettyPipeline.H2MultiplexHandler, new Http2MultiplexHandler(new H2Codec(accessLogEnabled, accessLog, compressPredicate, cookieDecoder, cookieEncoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, listener, - mapHandle, methodTagValue, metricsRecorder, minCompressionSize, compressionSettings, opsFactory, readTimeout, requestTimeout, uriTagValue))); + mapHandle, methodTagValue, metricsRecorder, minCompressionSize, compressionOptions, opsFactory, readTimeout, requestTimeout, uriTagValue))); IdleTimeoutHandler.addIdleTimeoutHandler(p, idleTimeout); @@ -716,7 +717,7 @@ static void configureHttp11OrH2CleartextPipeline(ChannelPipeline p, @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - HttpCompressionSettingsSpec compressionSettings, + HttpCompressionOptionsSpec compressionOptions, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -735,7 +736,7 @@ static void configureHttp11OrH2CleartextPipeline(ChannelPipeline p, Http11OrH2CleartextCodec upgrader = new Http11OrH2CleartextCodec(accessLogEnabled, accessLog, compressPredicate, cookieDecoder, cookieEncoder, p.get(NettyPipeline.LoggingHandler) != null, enableGracefulShutdown, formDecoderProvider, forwardedHeaderHandler, http2SettingsSpec, httpMessageLogFactory, listener, mapHandle, methodTagValue, metricsRecorder, - minCompressionSize, compressionSettings, opsFactory, readTimeout, requestTimeout, uriTagValue, decoder.validateHeaders()); + minCompressionSize, compressionOptions, opsFactory, readTimeout, requestTimeout, uriTagValue, decoder.validateHeaders()); ChannelHandler http2ServerHandler = new H2CleartextCodec(upgrader, http2SettingsSpec != null ? http2SettingsSpec.maxStreams() : null); @@ -750,7 +751,7 @@ static void configureHttp11OrH2CleartextPipeline(ChannelPipeline p, NettyPipeline.H2CUpgradeHandler, h2cUpgradeHandler) .addBefore(NettyPipeline.ReactiveBridge, NettyPipeline.HttpTrafficHandler, - new HttpTrafficHandler(compressPredicate, compressionSettings, cookieDecoder, cookieEncoder, formDecoderProvider, + new HttpTrafficHandler(compressPredicate, compressionOptions, cookieDecoder, cookieEncoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, idleTimeout, listener, mapHandle, maxKeepAliveRequests, readTimeout, requestTimeout, decoder.validateHeaders())); @@ -761,7 +762,7 @@ static void configureHttp11OrH2CleartextPipeline(ChannelPipeline p, boolean alwaysCompress = compressPredicate == null && minCompressionSize == 0; if (alwaysCompress) { - p.addBefore(NettyPipeline.HttpTrafficHandler, NettyPipeline.CompressionHandler, SimpleCompressionHandler.create(compressionSettings)); + p.addBefore(NettyPipeline.HttpTrafficHandler, NettyPipeline.CompressionHandler, SimpleCompressionHandler.create(compressionOptions)); } if (metricsRecorder != null) { @@ -805,7 +806,7 @@ static void configureHttp11Pipeline(ChannelPipeline p, @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - HttpCompressionSettingsSpec compressionSettings, + HttpCompressionOptionsSpec compressionOptions, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @Nullable Function uriTagValue) { @@ -822,7 +823,7 @@ static void configureHttp11Pipeline(ChannelPipeline p, new HttpServerCodec(decoderConfig)) .addBefore(NettyPipeline.ReactiveBridge, NettyPipeline.HttpTrafficHandler, - new HttpTrafficHandler(compressPredicate, compressionSettings, cookieDecoder, cookieEncoder, formDecoderProvider, + new HttpTrafficHandler(compressPredicate, compressionOptions, cookieDecoder, cookieEncoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, idleTimeout, listener, mapHandle, maxKeepAliveRequests, readTimeout, requestTimeout, decoder.validateHeaders())); @@ -833,7 +834,7 @@ static void configureHttp11Pipeline(ChannelPipeline p, boolean alwaysCompress = compressPredicate == null && minCompressionSize == 0; if (alwaysCompress) { - p.addBefore(NettyPipeline.HttpTrafficHandler, NettyPipeline.CompressionHandler, SimpleCompressionHandler.create(compressionSettings)); + p.addBefore(NettyPipeline.HttpTrafficHandler, NettyPipeline.CompressionHandler, SimpleCompressionHandler.create(compressionOptions)); } if (metricsRecorder != null) { @@ -1016,7 +1017,7 @@ static final class H2Codec extends ChannelInitializer { final Function methodTagValue; final ChannelMetricsRecorder metricsRecorder; final int minCompressionSize; - final HttpCompressionSettingsSpec compressionSettings; + final HttpCompressionOptionsSpec compressionOptions; final ChannelOperations.OnSetup opsFactory; final Duration readTimeout; final Duration requestTimeout; @@ -1036,7 +1037,7 @@ static final class H2Codec extends ChannelInitializer { @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - HttpCompressionSettingsSpec compressionSettings, + HttpCompressionOptionsSpec compressionOptions, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -1054,7 +1055,7 @@ static final class H2Codec extends ChannelInitializer { this.methodTagValue = methodTagValue; this.metricsRecorder = metricsRecorder; this.minCompressionSize = minCompressionSize; - this.compressionSettings = compressionSettings; + this.compressionOptions = compressionOptions; this.opsFactory = opsFactory; this.readTimeout = readTimeout; this.requestTimeout = requestTimeout; @@ -1066,7 +1067,7 @@ protected void initChannel(Channel ch) { ch.pipeline().remove(this); addStreamHandlers(ch, accessLogEnabled, accessLog, compressPredicate, cookieDecoder, cookieEncoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, listener, mapHandle, methodTagValue, metricsRecorder, - minCompressionSize, compressionSettings, opsFactory, readTimeout, requestTimeout, uriTagValue); + minCompressionSize, compressionOptions, opsFactory, readTimeout, requestTimeout, uriTagValue); } } @@ -1089,7 +1090,7 @@ static final class Http11OrH2CleartextCodec extends ChannelInitializer final Function methodTagValue; final ChannelMetricsRecorder metricsRecorder; final int minCompressionSize; - final HttpCompressionSettingsSpec compressionSettings; + final HttpCompressionOptionsSpec compressionOptions; final ChannelOperations.OnSetup opsFactory; final Duration readTimeout; final Duration requestTimeout; @@ -1112,7 +1113,7 @@ static final class Http11OrH2CleartextCodec extends ChannelInitializer @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - HttpCompressionSettingsSpec compressionSettings, + HttpCompressionOptionsSpec compressionOptions, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -1153,7 +1154,7 @@ static final class Http11OrH2CleartextCodec extends ChannelInitializer this.methodTagValue = methodTagValue; this.metricsRecorder = metricsRecorder; this.minCompressionSize = minCompressionSize; - this.compressionSettings = compressionSettings; + this.compressionOptions = compressionOptions; this.opsFactory = opsFactory; this.readTimeout = readTimeout; this.requestTimeout = requestTimeout; @@ -1168,7 +1169,7 @@ protected void initChannel(Channel ch) { ch.pipeline().remove(this); addStreamHandlers(ch, accessLogEnabled, accessLog, compressPredicate, cookieDecoder, cookieEncoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, listener, mapHandle, methodTagValue, - metricsRecorder, minCompressionSize, compressionSettings, opsFactory, readTimeout, requestTimeout, uriTagValue); + metricsRecorder, minCompressionSize, compressionOptions, opsFactory, readTimeout, requestTimeout, uriTagValue); } @Override @@ -1227,7 +1228,7 @@ static final class H2OrHttp11Codec extends ApplicationProtocolNegotiationHandler final Function methodTagValue; final ChannelMetricsRecorder metricsRecorder; final int minCompressionSize; - final HttpCompressionSettingsSpec compressionSettings; + final HttpCompressionOptionsSpec compressionOptions; final ChannelOperations.OnSetup opsFactory; final Duration readTimeout; final Duration requestTimeout; @@ -1258,7 +1259,7 @@ static final class H2OrHttp11Codec extends ApplicationProtocolNegotiationHandler this.methodTagValue = initializer.methodTagValue; this.metricsRecorder = initializer.metricsRecorder; this.minCompressionSize = initializer.minCompressionSize; - this.compressionSettings = initializer.compressionSettings; + this.compressionOptions = initializer.compressionOptions; this.opsFactory = initializer.opsFactory; this.readTimeout = initializer.readTimeout; this.requestTimeout = initializer.requestTimeout; @@ -1277,7 +1278,7 @@ protected void configurePipeline(ChannelHandlerContext ctx, String protocol) { if (ApplicationProtocolNames.HTTP_2.equals(protocol)) { configureH2Pipeline(p, accessLogEnabled, accessLog, compressPredicate, cookieDecoder, cookieEncoder, enableGracefulShutdown, formDecoderProvider, forwardedHeaderHandler, http2SettingsSpec, httpMessageLogFactory, idleTimeout, - listener, mapHandle, methodTagValue, metricsRecorder, minCompressionSize, compressionSettings, opsFactory, readTimeout, requestTimeout, + listener, mapHandle, methodTagValue, metricsRecorder, minCompressionSize, compressionOptions, opsFactory, readTimeout, requestTimeout, uriTagValue, decoder.validateHeaders()); return; } @@ -1285,7 +1286,7 @@ protected void configurePipeline(ChannelHandlerContext ctx, String protocol) { if (!supportOnlyHttp2 && ApplicationProtocolNames.HTTP_1_1.equals(protocol)) { configureHttp11Pipeline(p, accessLogEnabled, accessLog, compressPredicate, cookieDecoder, cookieEncoder, true, decoder, formDecoderProvider, forwardedHeaderHandler, httpMessageLogFactory, idleTimeout, listener, - mapHandle, maxKeepAliveRequests, methodTagValue, metricsRecorder, minCompressionSize, compressionSettings, readTimeout, requestTimeout, uriTagValue); + mapHandle, maxKeepAliveRequests, methodTagValue, metricsRecorder, minCompressionSize, compressionOptions, readTimeout, requestTimeout, uriTagValue); // When the server is configured with HTTP/1.1 and H2 and HTTP/1.1 is negotiated, // when channelActive event happens, this HttpTrafficHandler is still not in the pipeline, @@ -1318,7 +1319,7 @@ static final class HttpServerChannelInitializer implements ChannelPipelineConfig final Function methodTagValue; final ChannelMetricsRecorder metricsRecorder; final int minCompressionSize; - final HttpCompressionSettingsSpec compressionSettings; + final HttpCompressionOptionsSpec compressionOptions; final ChannelOperations.OnSetup opsFactory; final int protocols; final ProxyProtocolSupportType proxyProtocolSupportType; @@ -1346,7 +1347,7 @@ static final class HttpServerChannelInitializer implements ChannelPipelineConfig this.methodTagValue = config.methodTagValue; this.metricsRecorder = config.metricsRecorderInternal(); this.minCompressionSize = config.minCompressionSize; - this.compressionSettings = config.compressionSettings; + this.compressionOptions = config.compressionOptions; this.opsFactory = config.channelOperationsProvider(); this.protocols = config._protocols; this.proxyProtocolSupportType = config.proxyProtocolSupportType; @@ -1401,7 +1402,7 @@ else if ((protocols & h11) == h11) { methodTagValue, metricsRecorder, minCompressionSize, - compressionSettings, + compressionOptions, readTimeout, requestTimeout, uriTagValue); @@ -1433,7 +1434,7 @@ else if ((protocols & h2) == h2) { methodTagValue, metricsRecorder, minCompressionSize, - compressionSettings, + compressionOptions, opsFactory, readTimeout, requestTimeout, @@ -1457,7 +1458,7 @@ else if ((protocols & h3) == h3) { methodTagValue, metricsRecorder, minCompressionSize, - compressionSettings, + compressionOptions, opsFactory, readTimeout, requestTimeout, @@ -1487,7 +1488,7 @@ else if ((protocols & h3) == h3) { methodTagValue, metricsRecorder, minCompressionSize, - compressionSettings, + compressionOptions, opsFactory, readTimeout, requestTimeout, @@ -1513,7 +1514,7 @@ else if ((protocols & h11) == h11) { methodTagValue, metricsRecorder, minCompressionSize, - compressionSettings, + compressionOptions, readTimeout, requestTimeout, uriTagValue); @@ -1537,7 +1538,7 @@ else if ((protocols & h2c) == h2c) { methodTagValue, metricsRecorder, minCompressionSize, - compressionSettings, + compressionOptions, opsFactory, readTimeout, requestTimeout, diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java index 3acffaf6a8..507cb4c1d5 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java @@ -94,6 +94,7 @@ import reactor.netty.http.HttpOperations; import reactor.netty.http.logging.HttpMessageArgProviderFactory; import reactor.netty.http.logging.HttpMessageLogFactory; +import reactor.netty.http.server.compression.HttpCompressionOptionsSpec; import reactor.netty.http.websocket.WebsocketInbound; import reactor.netty.http.websocket.WebsocketOutbound; import reactor.util.Logger; @@ -138,7 +139,7 @@ class HttpServerOperations extends HttpOperations compressionPredicate; - HttpCompressionSettingsSpec compressionSettings; + HttpCompressionOptionsSpec compressionOptions; boolean isWebsocket; Function> paramsResolver; String path; @@ -152,7 +153,7 @@ class HttpServerOperations extends HttpOperations compressionPredicate, - HttpCompressionSettingsSpec compressionSettings, + HttpCompressionOptionsSpec compressionOptions, ConnectionInfo connectionInfo, ServerCookieDecoder decoder, ServerCookieEncoder encoder, @@ -195,7 +196,7 @@ class HttpServerOperations extends HttpOperations compress; - final HttpCompressionSettingsSpec compressionSettings; + final HttpCompressionOptionsSpec compressionOptions; final ServerCookieDecoder cookieDecoder; final ServerCookieEncoder cookieEncoder; final HttpServerFormDecoderProvider formDecoderProvider; @@ -113,7 +114,7 @@ final class HttpTrafficHandler extends ChannelDuplexHandler implements Runnable HttpTrafficHandler( @Nullable BiPredicate compress, - HttpCompressionSettingsSpec compressionSettings, + HttpCompressionOptionsSpec compressionOptions, ServerCookieDecoder decoder, ServerCookieEncoder encoder, HttpServerFormDecoderProvider formDecoderProvider, @@ -130,7 +131,7 @@ final class HttpTrafficHandler extends ChannelDuplexHandler implements Runnable this.formDecoderProvider = formDecoderProvider; this.forwardedHeaderHandler = forwardedHeaderHandler; this.compress = compress; - this.compressionSettings = compressionSettings; + this.compressionOptions = compressionOptions; this.cookieEncoder = encoder; this.cookieDecoder = decoder; this.httpMessageLogFactory = httpMessageLogFactory; @@ -246,7 +247,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { listener, request, compress, - compressionSettings, + compressionOptions, connectionInfo, cookieDecoder, cookieEncoder, @@ -599,7 +600,7 @@ public void run() { listener, nextRequest, compress, - compressionSettings, + compressionOptions, connectionInfo, cookieDecoder, cookieEncoder, diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/SimpleCompressionHandler.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/SimpleCompressionHandler.java index 36f4872a4b..e7e318bf55 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/SimpleCompressionHandler.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/SimpleCompressionHandler.java @@ -31,6 +31,7 @@ import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.LastHttpContent; import io.netty.util.ReferenceCountUtil; +import reactor.netty.http.server.compression.HttpCompressionOptionsSpec; /** * {@link HttpContentCompressor} to enable on-demand compression. @@ -46,9 +47,9 @@ private SimpleCompressionHandler(CompressionOptions... options) { super(options); } - static SimpleCompressionHandler create(HttpCompressionSettingsSpec compressionSettings) { + static SimpleCompressionHandler create(HttpCompressionOptionsSpec compressionOptions) { return new SimpleCompressionHandler( - compressionSettings.adaptToOptions() + compressionOptions.adapt() ); } diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/BrotliOption.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/BrotliOption.java new file mode 100644 index 0000000000..4a73564e56 --- /dev/null +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/BrotliOption.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024-2025 VMware, Inc. or its affiliates, All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package reactor.netty.http.server.compression; + +import io.netty.handler.codec.compression.CompressionOptions; +import io.netty.handler.codec.compression.StandardCompressionOptions; + +/** + * Brotli compression option configuration + * + * @author raccoonback + */ +final class BrotliOption implements HttpCompressionOption { + + private final CompressionOptions option; + + BrotliOption() { + this.option = StandardCompressionOptions.brotli(); + } + + CompressionOptions adapt() { + return option; + } +} diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/DeflateOption.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/DeflateOption.java new file mode 100644 index 0000000000..194f44cdd4 --- /dev/null +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/DeflateOption.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024-2025 VMware, Inc. or its affiliates, All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package reactor.netty.http.server.compression; + +import io.netty.handler.codec.compression.CompressionOptions; +import io.netty.handler.codec.compression.StandardCompressionOptions; + +/** + * Deflate compression option configuration + * + * @author raccoonback + */ +final class DeflateOption implements HttpCompressionOption { + + private final CompressionOptions option; + + DeflateOption() { + this.option = StandardCompressionOptions.gzip(); + } + + DeflateOption(int compressionLevel, int windowBits, int memoryLevel) { + this.option = StandardCompressionOptions.gzip( + compressionLevel, + windowBits, + memoryLevel + ); + } + + CompressionOptions adapt() { + return option; + } +} diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/GzipOption.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/GzipOption.java new file mode 100644 index 0000000000..33cc244659 --- /dev/null +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/GzipOption.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024-2025 VMware, Inc. or its affiliates, All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package reactor.netty.http.server.compression; + +import io.netty.handler.codec.compression.CompressionOptions; +import io.netty.handler.codec.compression.StandardCompressionOptions; + +/** + * GZIP compression option configuration + * + * @author raccoonback + */ +final class GzipOption implements HttpCompressionOption { + + private final CompressionOptions option; + + GzipOption() { + this.option = StandardCompressionOptions.gzip(); + } + + GzipOption(int compressionLevel, int windowBits, int memoryLevel) { + this.option = StandardCompressionOptions.gzip( + compressionLevel, + windowBits, + memoryLevel + ); + } + + CompressionOptions adapt() { + return option; + } +} diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOption.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOption.java new file mode 100644 index 0000000000..8f8067eb56 --- /dev/null +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOption.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2024-2025 VMware, Inc. or its affiliates, All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package reactor.netty.http.server.compression; + +/** + * HTTP compression option configuration + * + * @author raccoonback + */ +public interface HttpCompressionOption { +} diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOptionsSpec.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOptionsSpec.java new file mode 100644 index 0000000000..00fef49168 --- /dev/null +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOptionsSpec.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2024-2025 VMware, Inc. or its affiliates, All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package reactor.netty.http.server.compression; + +import io.netty.handler.codec.compression.Brotli; +import io.netty.handler.codec.compression.CompressionOptions; +import io.netty.handler.codec.compression.Zstd; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * HTTP Compression configuration for the SimpleCompressionHandler. + * + * @author raccoonback + */ +public final class HttpCompressionOptionsSpec { + + private GzipOption gzip; + private DeflateOption deflate; + private SnappyOption snappy; + private BrotliOption brotli; + private ZstdOption zstd; + + private HttpCompressionOptionsSpec() { + gzip = StandardHttpCompressionOptions.gzip(); + deflate = StandardHttpCompressionOptions.deflate(); + snappy = StandardHttpCompressionOptions.snappy(); + + if (Brotli.isAvailable()) { + brotli = StandardHttpCompressionOptions.brotli(); + } + + if (Zstd.isAvailable()) { + zstd = StandardHttpCompressionOptions.zstd(); + } + } + + public HttpCompressionOptionsSpec(HttpCompressionOption... compressionOptions) { + this(); + Arrays.stream(compressionOptions) + .forEach(this::initializeOption); + } + + private void initializeOption(HttpCompressionOption option) { + if (option instanceof GzipOption) { + this.gzip = (GzipOption) option; + } + else if (option instanceof DeflateOption) { + this.deflate = (DeflateOption) option; + } + else if (option instanceof SnappyOption) { + this.snappy = (SnappyOption) option; + } + else if (Brotli.isAvailable() && option instanceof BrotliOption) { + this.brotli = (BrotliOption) option; + } + else if (Zstd.isAvailable() && option instanceof ZstdOption) { + this.zstd = (ZstdOption) option; + } + } + + public static HttpCompressionOptionsSpec provideDefault() { + return new HttpCompressionOptionsSpec(); + } + + public CompressionOptions[] adapt() { + List options = new ArrayList<>( + Arrays.asList( + gzip.adapt(), + deflate.adapt(), + snappy.adapt() + ) + ); + + if (brotli != null) { + options.add(brotli.adapt()); + } + + if (zstd != null) { + options.add(zstd.adapt()); + } + + return options.toArray(new CompressionOptions[0]); + } +} diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/SnappyOption.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/SnappyOption.java new file mode 100644 index 0000000000..c09fc0e635 --- /dev/null +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/SnappyOption.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024-2025 VMware, Inc. or its affiliates, All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package reactor.netty.http.server.compression; + +import io.netty.handler.codec.compression.CompressionOptions; +import io.netty.handler.codec.compression.StandardCompressionOptions; + +/** + * Snappy compression option configuration + * + * @author raccoonback + */ +final class SnappyOption implements HttpCompressionOption { + + private final CompressionOptions option; + + SnappyOption() { + this.option = StandardCompressionOptions.snappy(); + } + + CompressionOptions adapt() { + return option; + } +} diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/StandardHttpCompressionOptions.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/StandardHttpCompressionOptions.java new file mode 100644 index 0000000000..13b3ea00e0 --- /dev/null +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/StandardHttpCompressionOptions.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2024-2025 VMware, Inc. or its affiliates, All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package reactor.netty.http.server.compression; + +import io.netty.handler.codec.compression.Brotli; +import io.netty.handler.codec.compression.Zstd; +import io.netty.util.internal.ObjectUtil; + +/** + * Standard Compression Options for {@link GzipOption}, {@link DeflateOption} and {@link ZstdOption} + * + * @author raccoonback + */ +public final class StandardHttpCompressionOptions { + + private StandardHttpCompressionOptions() { + } + + /** + * Default GZIP options. + * The default compression level is 6. + * + * @return a new {@link GzipOption} + */ + public static GzipOption gzip() { + return new GzipOption(); + } + + /** + * Set GZIP options. + * + * @return a new {@link GzipOption} + * @throws IllegalStateException If invalid parameters. + */ + public static GzipOption gzip(int compressionLevel, int windowBits, int memoryLevel) { + ObjectUtil.checkInRange(compressionLevel, 0, 9, "compressionLevel"); + ObjectUtil.checkInRange(windowBits, 9, 15, "windowBits"); + ObjectUtil.checkInRange(memoryLevel, 1, 9, "memLevel"); + + return new GzipOption(compressionLevel, windowBits, memoryLevel); + } + + /** + * Default Deflate options. + * The default compression level is 6. + * + * @return a new {@link DeflateOption} + */ + public static DeflateOption deflate() { + return new DeflateOption(); + } + + /** + * Set Deflate options. + * + * @return a new {@link DeflateOption} + * @throws IllegalStateException If invalid parameters. + */ + public static DeflateOption deflate(int compressionLevel, int windowBits, int memoryLevel) { + ObjectUtil.checkInRange(compressionLevel, 0, 9, "compressionLevel"); + ObjectUtil.checkInRange(windowBits, 9, 15, "windowBits"); + ObjectUtil.checkInRange(memoryLevel, 1, 9, "memLevel"); + + return new DeflateOption(compressionLevel, windowBits, memoryLevel); + } + + /** + * Default ZSTD options. + * The default compression level is 3. + * + * @return a new {@link ZstdOption} + */ + public static ZstdOption zstd() { + return new ZstdOption(); + } + + /** + * Set ZSTD options. + * + * @return a new {@link ZstdOption} + * @throws IllegalStateException If invalid parameters. + */ + public static ZstdOption zstd(int compressionLevel, int blockSize, int maxEncodeSize) { + ObjectUtil.checkInRange(compressionLevel, -(1 << 17), 22, "compressionLevel"); + ObjectUtil.checkPositive(blockSize, "blockSize"); + ObjectUtil.checkPositive(maxEncodeSize, "maxEncodeSize"); + + if (!Zstd.isAvailable()) { + throw new IllegalStateException("ZSTD is not available", Zstd.cause()); + } + + return new ZstdOption(compressionLevel, blockSize, maxEncodeSize); + } + + /** + * Default Brotli options. + * + * @return a new {@link BrotliOption} + */ + public static BrotliOption brotli() { + if (!Brotli.isAvailable()) { + throw new IllegalStateException("Brotli is not available", Brotli.cause()); + } + + return new BrotliOption(); + } + + /** + * Default Snappy options. + * + * @return a new {@link SnappyOption} + */ + public static SnappyOption snappy() { + return new SnappyOption(); + } +} diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/ZstdOption.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/ZstdOption.java new file mode 100644 index 0000000000..fdd628ddbe --- /dev/null +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/ZstdOption.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024-2025 VMware, Inc. or its affiliates, All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package reactor.netty.http.server.compression; + +import io.netty.handler.codec.compression.CompressionOptions; +import io.netty.handler.codec.compression.StandardCompressionOptions; + +/** + * ZSTD compression option configuration + * + * @author raccoonback + */ +final class ZstdOption implements HttpCompressionOption { + + private final CompressionOptions option; + + ZstdOption() { + this.option = StandardCompressionOptions.zstd(); + } + + ZstdOption(int compressionLevel, int windowBits, int memoryLevel) { + this.option = StandardCompressionOptions.zstd( + compressionLevel, + windowBits, + memoryLevel + ); + } + + CompressionOptions adapt() { + return option; + } +} diff --git a/reactor-netty-http/src/test/java/reactor/netty/http/HttpCompressionClientServerTests.java b/reactor-netty-http/src/test/java/reactor/netty/http/HttpCompressionClientServerTests.java index dd3ea652b9..a2cabe731f 100644 --- a/reactor-netty-http/src/test/java/reactor/netty/http/HttpCompressionClientServerTests.java +++ b/reactor-netty-http/src/test/java/reactor/netty/http/HttpCompressionClientServerTests.java @@ -59,6 +59,7 @@ import reactor.netty.http.client.HttpClient; import reactor.netty.http.server.HttpServer; import reactor.netty.http.server.HttpServerResponse; +import reactor.netty.http.server.compression.StandardHttpCompressionOptions; import reactor.test.StepVerifier; import reactor.util.function.Tuple2; @@ -726,7 +727,7 @@ void serverCompressionEnabledResponseCompressionDisabled(HttpServer server, Http void serverCompressionWithCompressionLevelSettings(HttpServer server, HttpClient client) { disposableServer = server.compress(true) - .compressSettings(builder -> builder.gzip(4)) + .compressOptions(StandardHttpCompressionOptions.gzip(4, 15, 8)) .handle((in, out) -> out.sendString(Mono.just("reply"))) .bindNow(Duration.ofSeconds(10)); @@ -765,7 +766,7 @@ void serverCompressionWithCompressionLevelSettings(HttpServer server, HttpClient void serverCompressionEnabledWithGzipCompressionLevelSettings(HttpServer server, HttpClient client) throws Exception { disposableServer = server.compress(true) - .compressSettings(builder -> builder.gzip(4)) + .compressOptions(StandardHttpCompressionOptions.gzip(4, 15, 8)) .handle((in, out) -> out.sendString(Mono.just("reply"))) .bindNow(Duration.ofSeconds(10)); @@ -800,7 +801,7 @@ void serverCompressionEnabledWithZstdCompressionLevel(HttpServer server, HttpCli assertThat(Zstd.isAvailable()).isTrue(); disposableServer = server.compress(true) - .compressSettings(builder -> builder.zstd(22)) + .compressOptions(StandardHttpCompressionOptions.zstd(12, 65536, 65536)) .handle((in, out) -> out.sendString(Mono.just("reply"))) .bindNow(Duration.ofSeconds(10)); diff --git a/reactor-netty-http/src/test/java/reactor/netty/http/server/HttpServerTests.java b/reactor-netty-http/src/test/java/reactor/netty/http/server/HttpServerTests.java index 5eb027d1f7..05b740e210 100644 --- a/reactor-netty-http/src/test/java/reactor/netty/http/server/HttpServerTests.java +++ b/reactor-netty-http/src/test/java/reactor/netty/http/server/HttpServerTests.java @@ -145,6 +145,7 @@ import reactor.netty.http.client.HttpClientRequest; import reactor.netty.http.client.PrematureCloseException; import reactor.netty.http.logging.ReactorNettyHttpMessageLogFactory; +import reactor.netty.http.server.compression.HttpCompressionOptionsSpec; import reactor.netty.resources.ConnectionProvider; import reactor.netty.resources.LoopResources; import reactor.netty.tcp.SslProvider; @@ -2214,7 +2215,7 @@ private void doTestStatus(HttpResponseStatus status) { ConnectionObserver.emptyListener(), new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"), null, - HttpCompressionSettingsSpec.provideDefault(), + HttpCompressionOptionsSpec.provideDefault(), new ConnectionInfo(localSocketAddress, DEFAULT_HOST_NAME, DEFAULT_HTTP_PORT, remoteSocketAddress, "http", true), ServerCookieDecoder.STRICT, ServerCookieEncoder.STRICT, @@ -3273,7 +3274,7 @@ private void doTestIsFormUrlencoded(String headerValue, boolean expectation) { ConnectionObserver.emptyListener(), request, null, - HttpCompressionSettingsSpec.provideDefault(), + HttpCompressionOptionsSpec.provideDefault(), new ConnectionInfo(localSocketAddress, DEFAULT_HOST_NAME, DEFAULT_HTTP_PORT, remoteSocketAddress, "http", true), ServerCookieDecoder.STRICT, ServerCookieEncoder.STRICT, From 5373a2ab645e9972e8b286ac0ea2bf8a3e9e7907 Mon Sep 17 00:00:00 2001 From: raccoonback Date: Wed, 8 Jan 2025 00:09:52 +0900 Subject: [PATCH 5/8] Update license year --- .../netty/http/server/Http2StreamBridgeServerHandler.java | 2 +- .../src/main/java/reactor/netty/http/server/Http3Codec.java | 2 +- .../java/reactor/netty/http/server/Http3ServerOperations.java | 2 +- .../netty/http/server/Http3StreamBridgeServerHandler.java | 2 +- .../src/main/java/reactor/netty/http/server/HttpServer.java | 2 +- .../main/java/reactor/netty/http/server/HttpServerConfig.java | 2 +- .../java/reactor/netty/http/server/HttpServerOperations.java | 2 +- .../main/java/reactor/netty/http/server/HttpTrafficHandler.java | 2 +- .../reactor/netty/http/server/SimpleCompressionHandler.java | 2 +- .../reactor/netty/http/server/compression/BrotliOption.java | 2 +- .../reactor/netty/http/server/compression/DeflateOption.java | 2 +- .../java/reactor/netty/http/server/compression/GzipOption.java | 2 +- .../netty/http/server/compression/HttpCompressionOption.java | 2 +- .../http/server/compression/HttpCompressionOptionsSpec.java | 2 +- .../reactor/netty/http/server/compression/SnappyOption.java | 2 +- .../http/server/compression/StandardHttpCompressionOptions.java | 2 +- .../java/reactor/netty/http/server/compression/ZstdOption.java | 2 +- .../reactor/netty/http/HttpCompressionClientServerTests.java | 2 +- .../test/java/reactor/netty/http/server/HttpServerTests.java | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http2StreamBridgeServerHandler.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http2StreamBridgeServerHandler.java index 0b783e652b..3bc110e625 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http2StreamBridgeServerHandler.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http2StreamBridgeServerHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2024 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2018-2025 VMware, Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3Codec.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3Codec.java index 05d77d0a48..1c72cceadd 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3Codec.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3Codec.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2024-2025 VMware, Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3ServerOperations.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3ServerOperations.java index 0570357b44..419501154c 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3ServerOperations.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3ServerOperations.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2024-2025 VMware, Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3StreamBridgeServerHandler.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3StreamBridgeServerHandler.java index 2fcd949e44..7dc4dda650 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3StreamBridgeServerHandler.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3StreamBridgeServerHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2024-2025 VMware, Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServer.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServer.java index 5fe9d5124c..6d93e480b6 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServer.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2024 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2011-2025 VMware, Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerConfig.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerConfig.java index 92906567ef..acac358b0f 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerConfig.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2024 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2020-2025 VMware, Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java index 507cb4c1d5..103b952111 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2024 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2011-2025 VMware, Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpTrafficHandler.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpTrafficHandler.java index c3a84eda12..03cd7889e5 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpTrafficHandler.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpTrafficHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2024 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2011-2025 VMware, Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/SimpleCompressionHandler.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/SimpleCompressionHandler.java index e7e318bf55..79b46b6b27 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/SimpleCompressionHandler.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/SimpleCompressionHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2024 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2018-2025 VMware, Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/BrotliOption.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/BrotliOption.java index 4a73564e56..5c5c9134a2 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/BrotliOption.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/BrotliOption.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024-2025 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2025 VMware, Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/DeflateOption.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/DeflateOption.java index 194f44cdd4..ab8830ff08 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/DeflateOption.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/DeflateOption.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024-2025 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2025 VMware, Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/GzipOption.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/GzipOption.java index 33cc244659..5c55407e64 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/GzipOption.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/GzipOption.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024-2025 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2025 VMware, Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOption.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOption.java index 8f8067eb56..5c0b81e6f7 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOption.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOption.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024-2025 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2025 VMware, Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOptionsSpec.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOptionsSpec.java index 00fef49168..56da24e6b9 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOptionsSpec.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOptionsSpec.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024-2025 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2025 VMware, Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/SnappyOption.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/SnappyOption.java index c09fc0e635..6d382cb91f 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/SnappyOption.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/SnappyOption.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024-2025 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2025 VMware, Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/StandardHttpCompressionOptions.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/StandardHttpCompressionOptions.java index 13b3ea00e0..0ac9374bb9 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/StandardHttpCompressionOptions.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/StandardHttpCompressionOptions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024-2025 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2025 VMware, Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/ZstdOption.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/ZstdOption.java index fdd628ddbe..d362995360 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/ZstdOption.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/ZstdOption.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024-2025 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2025 VMware, Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/reactor-netty-http/src/test/java/reactor/netty/http/HttpCompressionClientServerTests.java b/reactor-netty-http/src/test/java/reactor/netty/http/HttpCompressionClientServerTests.java index a2cabe731f..059ef1a9dc 100644 --- a/reactor-netty-http/src/test/java/reactor/netty/http/HttpCompressionClientServerTests.java +++ b/reactor-netty-http/src/test/java/reactor/netty/http/HttpCompressionClientServerTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2024 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2017-2025 VMware, Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/reactor-netty-http/src/test/java/reactor/netty/http/server/HttpServerTests.java b/reactor-netty-http/src/test/java/reactor/netty/http/server/HttpServerTests.java index 05b740e210..57a5ccff1a 100644 --- a/reactor-netty-http/src/test/java/reactor/netty/http/server/HttpServerTests.java +++ b/reactor-netty-http/src/test/java/reactor/netty/http/server/HttpServerTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2024 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2011-2025 VMware, Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 100d2da84f2faf65479308a75c890c6ea62d869f Mon Sep 17 00:00:00 2001 From: raccoonback Date: Wed, 8 Jan 2025 00:28:17 +0900 Subject: [PATCH 6/8] Fix checkstyle --- .../reactor/netty/http/server/compression/BrotliOption.java | 2 +- .../reactor/netty/http/server/compression/DeflateOption.java | 2 +- .../java/reactor/netty/http/server/compression/GzipOption.java | 2 +- .../netty/http/server/compression/HttpCompressionOption.java | 2 +- .../reactor/netty/http/server/compression/SnappyOption.java | 2 +- .../http/server/compression/StandardHttpCompressionOptions.java | 2 +- .../java/reactor/netty/http/server/compression/ZstdOption.java | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/BrotliOption.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/BrotliOption.java index 5c5c9134a2..0cdbee2f0a 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/BrotliOption.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/BrotliOption.java @@ -19,7 +19,7 @@ import io.netty.handler.codec.compression.StandardCompressionOptions; /** - * Brotli compression option configuration + * Brotli compression option configuration. * * @author raccoonback */ diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/DeflateOption.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/DeflateOption.java index ab8830ff08..87a57a903a 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/DeflateOption.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/DeflateOption.java @@ -19,7 +19,7 @@ import io.netty.handler.codec.compression.StandardCompressionOptions; /** - * Deflate compression option configuration + * Deflate compression option configuration. * * @author raccoonback */ diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/GzipOption.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/GzipOption.java index 5c55407e64..b5f00abeb8 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/GzipOption.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/GzipOption.java @@ -19,7 +19,7 @@ import io.netty.handler.codec.compression.StandardCompressionOptions; /** - * GZIP compression option configuration + * GZIP compression option configuration. * * @author raccoonback */ diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOption.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOption.java index 5c0b81e6f7..7eed9f06bb 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOption.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOption.java @@ -16,7 +16,7 @@ package reactor.netty.http.server.compression; /** - * HTTP compression option configuration + * HTTP compression option configuration. * * @author raccoonback */ diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/SnappyOption.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/SnappyOption.java index 6d382cb91f..33fecadab5 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/SnappyOption.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/SnappyOption.java @@ -19,7 +19,7 @@ import io.netty.handler.codec.compression.StandardCompressionOptions; /** - * Snappy compression option configuration + * Snappy compression option configuration. * * @author raccoonback */ diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/StandardHttpCompressionOptions.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/StandardHttpCompressionOptions.java index 0ac9374bb9..29447f6d63 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/StandardHttpCompressionOptions.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/StandardHttpCompressionOptions.java @@ -20,7 +20,7 @@ import io.netty.util.internal.ObjectUtil; /** - * Standard Compression Options for {@link GzipOption}, {@link DeflateOption} and {@link ZstdOption} + * Standard Compression Options for {@link GzipOption}, {@link DeflateOption} and {@link ZstdOption}. * * @author raccoonback */ diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/ZstdOption.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/ZstdOption.java index d362995360..b83e8cc880 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/ZstdOption.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/ZstdOption.java @@ -19,7 +19,7 @@ import io.netty.handler.codec.compression.StandardCompressionOptions; /** - * ZSTD compression option configuration + * ZSTD compression option configuration. * * @author raccoonback */ From 7eee9c9b663723f9dd9d0abb80585e743ae5fc1a Mon Sep 17 00:00:00 2001 From: raccoonback Date: Thu, 9 Jan 2025 21:52:58 +0900 Subject: [PATCH 7/8] Set compressionOptions to null as the default state when compression is disabled - Updated compressionOptions to default to null when compression is not explicitly configured. - Ensures a clean default state, reflecting that no compression settings are applied unless enabled. --- .../server/Http2StreamBridgeServerHandler.java | 2 +- .../reactor/netty/http/server/Http3Codec.java | 4 ++-- .../http/server/Http3ServerOperations.java | 2 +- .../server/Http3StreamBridgeServerHandler.java | 2 +- .../netty/http/server/HttpServerConfig.java | 16 ++++++++-------- .../netty/http/server/HttpServerOperations.java | 4 ++-- .../netty/http/server/HttpTrafficHandler.java | 2 +- .../http/server/SimpleCompressionHandler.java | 17 +++++++++++++---- .../compression/HttpCompressionOptionsSpec.java | 4 ---- .../netty/http/server/HttpServerTests.java | 5 ++--- 10 files changed, 31 insertions(+), 27 deletions(-) diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http2StreamBridgeServerHandler.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http2StreamBridgeServerHandler.java index 3bc110e625..9ea5eba9cb 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http2StreamBridgeServerHandler.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http2StreamBridgeServerHandler.java @@ -86,7 +86,7 @@ final class Http2StreamBridgeServerHandler extends ChannelDuplexHandler { Http2StreamBridgeServerHandler( @Nullable BiPredicate compress, - HttpCompressionOptionsSpec compressionOptions, + @Nullable HttpCompressionOptionsSpec compressionOptions, ServerCookieDecoder decoder, ServerCookieEncoder encoder, HttpServerFormDecoderProvider formDecoderProvider, diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3Codec.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3Codec.java index 1c72cceadd..450ed85194 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3Codec.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3Codec.java @@ -85,7 +85,7 @@ final class Http3Codec extends ChannelInitializer { @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - HttpCompressionOptionsSpec compressionOptions, + @Nullable HttpCompressionOptionsSpec compressionOptions, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -170,7 +170,7 @@ static ChannelHandler newHttp3ServerConnectionHandler( @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - HttpCompressionOptionsSpec compressionOptions, + @Nullable HttpCompressionOptionsSpec compressionOptions, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3ServerOperations.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3ServerOperations.java index 419501154c..695b265c18 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3ServerOperations.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3ServerOperations.java @@ -44,7 +44,7 @@ final class Http3ServerOperations extends HttpServerOperations { ConnectionObserver listener, HttpRequest nettyRequest, @Nullable BiPredicate compressionPredicate, - HttpCompressionOptionsSpec compressionOptions, + @Nullable HttpCompressionOptionsSpec compressionOptions, ConnectionInfo connectionInfo, ServerCookieDecoder decoder, ServerCookieEncoder encoder, diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3StreamBridgeServerHandler.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3StreamBridgeServerHandler.java index 7dc4dda650..0066a824a4 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3StreamBridgeServerHandler.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/Http3StreamBridgeServerHandler.java @@ -76,7 +76,7 @@ final class Http3StreamBridgeServerHandler extends ChannelDuplexHandler { Http3StreamBridgeServerHandler( @Nullable BiPredicate compress, - HttpCompressionOptionsSpec compressionOptions, + @Nullable HttpCompressionOptionsSpec compressionOptions, ServerCookieDecoder decoder, ServerCookieEncoder encoder, HttpServerFormDecoderProvider formDecoderProvider, diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerConfig.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerConfig.java index acac358b0f..7f098f6d1b 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerConfig.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerConfig.java @@ -346,7 +346,7 @@ public Function uriTagValue() { this.httpMessageLogFactory = ReactorNettyHttpMessageLogFactory.INSTANCE; this.maxKeepAliveRequests = -1; this.minCompressionSize = -1; - this.compressionOptions = HttpCompressionOptionsSpec.provideDefault(); + this.compressionOptions = null; this.protocols = new HttpProtocol[]{HttpProtocol.HTTP11}; this._protocols = h11; this.proxyProtocolSupportType = ProxyProtocolSupportType.OFF; @@ -496,7 +496,7 @@ static void addStreamHandlers(Channel ch, @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - HttpCompressionOptionsSpec compressionOptions, + @Nullable HttpCompressionOptionsSpec compressionOptions, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -610,7 +610,7 @@ static void configureHttp3Pipeline( @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - HttpCompressionOptionsSpec compressionOptions, + @Nullable HttpCompressionOptionsSpec compressionOptions, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -646,7 +646,7 @@ static void configureH2Pipeline(ChannelPipeline p, @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - HttpCompressionOptionsSpec compressionOptions, + @Nullable HttpCompressionOptionsSpec compressionOptions, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -717,7 +717,7 @@ static void configureHttp11OrH2CleartextPipeline(ChannelPipeline p, @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - HttpCompressionOptionsSpec compressionOptions, + @Nullable HttpCompressionOptionsSpec compressionOptions, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -806,7 +806,7 @@ static void configureHttp11Pipeline(ChannelPipeline p, @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - HttpCompressionOptionsSpec compressionOptions, + @Nullable HttpCompressionOptionsSpec compressionOptions, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @Nullable Function uriTagValue) { @@ -1037,7 +1037,7 @@ static final class H2Codec extends ChannelInitializer { @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - HttpCompressionOptionsSpec compressionOptions, + @Nullable HttpCompressionOptionsSpec compressionOptions, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, @@ -1113,7 +1113,7 @@ static final class Http11OrH2CleartextCodec extends ChannelInitializer @Nullable Function methodTagValue, @Nullable ChannelMetricsRecorder metricsRecorder, int minCompressionSize, - HttpCompressionOptionsSpec compressionOptions, + @Nullable HttpCompressionOptionsSpec compressionOptions, ChannelOperations.OnSetup opsFactory, @Nullable Duration readTimeout, @Nullable Duration requestTimeout, diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java index 103b952111..ce30b1c683 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java @@ -180,7 +180,7 @@ class HttpServerOperations extends HttpOperations compressionPredicate, - HttpCompressionOptionsSpec compressionOptions, + @Nullable HttpCompressionOptionsSpec compressionOptions, ConnectionInfo connectionInfo, ServerCookieDecoder decoder, ServerCookieEncoder encoder, @@ -1325,7 +1325,7 @@ static final class FailedHttpServerRequest extends HttpServerOperations { ZonedDateTime timestamp, ConnectionInfo connectionInfo, boolean validateHeaders) { - super(c, listener, nettyRequest, null, HttpCompressionOptionsSpec.provideDefault(), connectionInfo, + super(c, listener, nettyRequest, null, null, connectionInfo, ServerCookieDecoder.STRICT, ServerCookieEncoder.STRICT, DEFAULT_FORM_DECODER_SPEC, httpMessageLogFactory, isHttp2, null, null, null, secure, timestamp, validateHeaders); this.customResponse = nettyResponse; diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpTrafficHandler.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpTrafficHandler.java index 03cd7889e5..e307655123 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpTrafficHandler.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpTrafficHandler.java @@ -114,7 +114,7 @@ final class HttpTrafficHandler extends ChannelDuplexHandler implements Runnable HttpTrafficHandler( @Nullable BiPredicate compress, - HttpCompressionOptionsSpec compressionOptions, + @Nullable HttpCompressionOptionsSpec compressionOptions, ServerCookieDecoder decoder, ServerCookieEncoder encoder, HttpServerFormDecoderProvider formDecoderProvider, diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/SimpleCompressionHandler.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/SimpleCompressionHandler.java index 79b46b6b27..bc3f50b5bc 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/SimpleCompressionHandler.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/SimpleCompressionHandler.java @@ -15,9 +15,6 @@ */ package reactor.netty.http.server; -import java.util.ArrayList; -import java.util.List; - import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; @@ -32,6 +29,10 @@ import io.netty.handler.codec.http.LastHttpContent; import io.netty.util.ReferenceCountUtil; import reactor.netty.http.server.compression.HttpCompressionOptionsSpec; +import reactor.util.annotation.Nullable; + +import java.util.ArrayList; +import java.util.List; /** * {@link HttpContentCompressor} to enable on-demand compression. @@ -43,11 +44,19 @@ final class SimpleCompressionHandler extends HttpContentCompressor { boolean decoded; HttpRequest request; + private SimpleCompressionHandler() { + super((CompressionOptions[]) null); + } + private SimpleCompressionHandler(CompressionOptions... options) { super(options); } - static SimpleCompressionHandler create(HttpCompressionOptionsSpec compressionOptions) { + static SimpleCompressionHandler create(@Nullable HttpCompressionOptionsSpec compressionOptions) { + if (compressionOptions == null) { + return new SimpleCompressionHandler(); + } + return new SimpleCompressionHandler( compressionOptions.adapt() ); diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOptionsSpec.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOptionsSpec.java index 56da24e6b9..dcf8f76d13 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOptionsSpec.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOptionsSpec.java @@ -74,10 +74,6 @@ else if (Zstd.isAvailable() && option instanceof ZstdOption) { } } - public static HttpCompressionOptionsSpec provideDefault() { - return new HttpCompressionOptionsSpec(); - } - public CompressionOptions[] adapt() { List options = new ArrayList<>( Arrays.asList( diff --git a/reactor-netty-http/src/test/java/reactor/netty/http/server/HttpServerTests.java b/reactor-netty-http/src/test/java/reactor/netty/http/server/HttpServerTests.java index 57a5ccff1a..d3ee56ad04 100644 --- a/reactor-netty-http/src/test/java/reactor/netty/http/server/HttpServerTests.java +++ b/reactor-netty-http/src/test/java/reactor/netty/http/server/HttpServerTests.java @@ -145,7 +145,6 @@ import reactor.netty.http.client.HttpClientRequest; import reactor.netty.http.client.PrematureCloseException; import reactor.netty.http.logging.ReactorNettyHttpMessageLogFactory; -import reactor.netty.http.server.compression.HttpCompressionOptionsSpec; import reactor.netty.resources.ConnectionProvider; import reactor.netty.resources.LoopResources; import reactor.netty.tcp.SslProvider; @@ -2215,7 +2214,7 @@ private void doTestStatus(HttpResponseStatus status) { ConnectionObserver.emptyListener(), new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"), null, - HttpCompressionOptionsSpec.provideDefault(), + null, new ConnectionInfo(localSocketAddress, DEFAULT_HOST_NAME, DEFAULT_HTTP_PORT, remoteSocketAddress, "http", true), ServerCookieDecoder.STRICT, ServerCookieEncoder.STRICT, @@ -3274,7 +3273,7 @@ private void doTestIsFormUrlencoded(String headerValue, boolean expectation) { ConnectionObserver.emptyListener(), request, null, - HttpCompressionOptionsSpec.provideDefault(), + null, new ConnectionInfo(localSocketAddress, DEFAULT_HOST_NAME, DEFAULT_HTTP_PORT, remoteSocketAddress, "http", true), ServerCookieDecoder.STRICT, ServerCookieEncoder.STRICT, From 99287f45906b75cf76ca0e7818d09b6edc40d8b2 Mon Sep 17 00:00:00 2001 From: raccoonback Date: Sat, 11 Jan 2025 00:17:16 +0900 Subject: [PATCH 8/8] Enhance compression configuration with Builder - Refactor compression settings to use a Builder pattern for easier configuration and improved flexibility. --- docs/modules/ROOT/pages/http-server.adoc | 22 ++- .../reactor/netty/http/server/HttpServer.java | 15 +- .../http/server/compression/BrotliOption.java | 8 +- .../server/compression/DeflateOption.java | 94 +++++++++++-- .../http/server/compression/GzipOption.java | 94 +++++++++++-- .../HttpCompressionOptionsSpec.java | 10 +- .../http/server/compression/SnappyOption.java | 8 +- .../StandardHttpCompressionOptions.java | 129 ------------------ .../http/server/compression/ZstdOption.java | 103 ++++++++++++-- .../HttpCompressionClientServerTests.java | 27 +++- 10 files changed, 324 insertions(+), 186 deletions(-) delete mode 100644 reactor-netty-http/src/main/java/reactor/netty/http/server/compression/StandardHttpCompressionOptions.java diff --git a/docs/modules/ROOT/pages/http-server.adoc b/docs/modules/ROOT/pages/http-server.adoc index 29bd92c696..c27fd9a040 100644 --- a/docs/modules/ROOT/pages/http-server.adoc +++ b/docs/modules/ROOT/pages/http-server.adoc @@ -162,15 +162,27 @@ You can configure the `HTTP` server to send a compressed response, depending on * `compress(int)`: The compression is performed once the response size exceeds the given value (in bytes). * `compress(BiPredicate)`: The compression is performed if the predicate returns `true`. -* `compressOptions(HttpCompressionOption... compressionOptions)`: Specifies the compression level for GZIP, DEFLATE, and ZSTD. +* `compressOptions(HttpCompressionOption... compressionOptions)`: Specifies the compression options for GZip, Deflate, and ZSTD. + [NOTE] ==== -Supported Compression Levels +GZip Compression Options -* gzip : only the range 0 to 9 is allowed. (default: 6) -* deflate : only the range 0 to 9 is allowed. (default: 6) -* zstd : only the range -131072 to 22 is allowed. (default: 3) +* compression level : only the range 0 to 9 is allowed. (default: 6) +* window bits : only the range 0 to 9 is allowed. (default: 15) +* memory level : only the range 1 to 9 is allowed. (default: 8) + +Deflate Compression Options + +* compression level : only the range 0 to 9 is allowed. (default: 6) +* window bits : only the range 0 to 9 is allowed. (default: 15) +* memory level : only the range 1 to 9 is allowed. (default: 8) + +ZSTD Compression Options + +* compression level : only the range -131072 to 9 is allowed. (default: 3) +* block size : only the positive number is allowed. (default: 65536, that is 64KB) +* max encode size : only the positive number is allowed. (default: 33554432, that is 32MB) ==== The following example uses the `compress` method (set to `true`) to enable compression: diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServer.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServer.java index 6d93e480b6..1d9d1bbd00 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServer.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServer.java @@ -340,15 +340,24 @@ public final HttpServer compress(int minResponseSize) { } /** - * Specifies GZIP, DEFLATE, ZSTD Compression Level. + * Specifies GZip, Deflate, ZSTD compression option + * with {@link reactor.netty.http.server.compression.GzipOption}, {@link reactor.netty.http.server.compression.DeflateOption}, , {@link reactor.netty.http.server.compression.ZstdOption}. * * @param compressionOptions configures {@link HttpCompressionOption} after enable compress. + * *
 	 *  {@code
 	 *      HttpServer.create()
-	 *                       .compress(true)
+	 *                      .compress(true)
 	 *  					.compressOptions(
-	 *                         StandardHttpCompressionOptions.gzip(6, 15, 8)
+	 *  				            	GzipOption.builder()
+	 *                                      .compressionLevel(6)
+	 *                                      .windowBits(15)
+	 *                                      .memoryLevel(8)
+	 *                                      .build(),
+	 *                                  ZstdOption.builder()
+	 *                                      .compressionLevel(3)
+	 *                                      .build()
 	 *  					 )
 	 *  					.bindNow();
 	 *  }
diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/BrotliOption.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/BrotliOption.java
index 0cdbee2f0a..5a36de8b09 100644
--- a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/BrotliOption.java
+++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/BrotliOption.java
@@ -25,13 +25,7 @@
  */
 final class BrotliOption implements HttpCompressionOption {
 
-	private final CompressionOptions option;
-
-	BrotliOption() {
-		this.option = StandardCompressionOptions.brotli();
-	}
-
 	CompressionOptions adapt() {
-		return option;
+		return StandardCompressionOptions.brotli();
 	}
 }
diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/DeflateOption.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/DeflateOption.java
index 87a57a903a..3ceb3a0abd 100644
--- a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/DeflateOption.java
+++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/DeflateOption.java
@@ -17,29 +17,107 @@
 
 import io.netty.handler.codec.compression.CompressionOptions;
 import io.netty.handler.codec.compression.StandardCompressionOptions;
+import io.netty.util.internal.ObjectUtil;
 
 /**
  * Deflate compression option configuration.
  *
  * @author raccoonback
  */
-final class DeflateOption implements HttpCompressionOption {
+public final class DeflateOption implements HttpCompressionOption {
 
-	private final CompressionOptions option;
+	private final int compressionLevel;
+	private final int windowBits;
+	private final int memoryLevel;
 
-	DeflateOption() {
-		this.option = StandardCompressionOptions.gzip();
+	private DeflateOption(Build build) {
+		this.compressionLevel = build.compressionLevel;
+		this.windowBits = build.windowBits;
+		this.memoryLevel = build.memoryLevel;
 	}
 
-	DeflateOption(int compressionLevel, int windowBits, int memoryLevel) {
-		this.option = StandardCompressionOptions.gzip(
+	static DeflateOption provideDefault() {
+		return builder().build();
+	}
+
+	CompressionOptions adapt() {
+		return StandardCompressionOptions.deflate(
 				compressionLevel,
 				windowBits,
 				memoryLevel
 		);
 	}
 
-	CompressionOptions adapt() {
-		return option;
+	/**
+	 * Creates a builder for {@link DeflateOption}.
+	 *
+	 * @return a new {@link DeflateOption.Builder}
+	 */
+	public static Builder builder() {
+		return new DeflateOption.Build();
+	}
+
+	public interface Builder {
+
+		/**
+		 * Build a new {@link DeflateOption}.
+		 *
+		 * @return a new {@link DeflateOption}
+		 */
+		DeflateOption build();
+
+		/**
+		 * Sets the deflate compression level.
+		 *
+		 * @return a new {@link DeflateOption.Builder}
+		 */
+		Builder compressionLevel(int compressionLevel);
+
+		/**
+		 * Sets the deflate window bits.
+		 *
+		 * @return a new {@link DeflateOption.Builder}
+		 */
+		Builder windowBits(int windowBits);
+
+		/**
+		 * Sets the deflate memory level.
+		 *
+		 * @return a new {@link DeflateOption.Builder}
+		 */
+		Builder memoryLevel(int memoryLevel);
+	}
+
+	private static final class Build implements Builder {
+
+		private int compressionLevel = 6;
+		private int windowBits = 12;
+		private int memoryLevel = 8;
+
+		@Override
+		public DeflateOption build() {
+			return new DeflateOption(this);
+		}
+
+		@Override
+		public Builder compressionLevel(int compressionLevel) {
+			ObjectUtil.checkInRange(compressionLevel, 0, 9, "compressionLevel");
+			this.compressionLevel = compressionLevel;
+			return this;
+		}
+
+		@Override
+		public Builder windowBits(int windowBits) {
+			ObjectUtil.checkInRange(windowBits, 9, 15, "windowBits");
+			this.windowBits = windowBits;
+			return this;
+		}
+
+		@Override
+		public Builder memoryLevel(int memoryLevel) {
+			ObjectUtil.checkInRange(memoryLevel, 1, 9, "memoryLevel");
+			this.memoryLevel = memoryLevel;
+			return this;
+		}
 	}
 }
diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/GzipOption.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/GzipOption.java
index b5f00abeb8..71fedb8215 100644
--- a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/GzipOption.java
+++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/GzipOption.java
@@ -17,29 +17,107 @@
 
 import io.netty.handler.codec.compression.CompressionOptions;
 import io.netty.handler.codec.compression.StandardCompressionOptions;
+import io.netty.util.internal.ObjectUtil;
 
 /**
  * GZIP compression option configuration.
  *
  * @author raccoonback
  */
-final class GzipOption implements HttpCompressionOption {
+public final class GzipOption implements HttpCompressionOption {
 
-	private final CompressionOptions option;
+	private final int compressionLevel;
+	private final int windowBits;
+	private final int memoryLevel;
 
-	GzipOption() {
-		this.option = StandardCompressionOptions.gzip();
+	private GzipOption(Build build) {
+		this.compressionLevel = build.compressionLevel;
+		this.windowBits = build.windowBits;
+		this.memoryLevel = build.memoryLevel;
 	}
 
-	GzipOption(int compressionLevel, int windowBits, int memoryLevel) {
-		this.option = StandardCompressionOptions.gzip(
+	static GzipOption provideDefault() {
+		return builder().build();
+	}
+
+	CompressionOptions adapt() {
+		return StandardCompressionOptions.gzip(
 				compressionLevel,
 				windowBits,
 				memoryLevel
 		);
 	}
 
-	CompressionOptions adapt() {
-		return option;
+	/**
+	 * Creates a builder for {@link GzipOption}.
+	 *
+	 * @return a new {@link GzipOption.Builder}
+	 */
+	public static Builder builder() {
+		return new Build();
+	}
+
+	public interface Builder {
+
+		/**
+		 * Build a new {@link GzipOption}.
+		 *
+		 * @return a new {@link GzipOption}
+		 */
+		GzipOption build();
+
+		/**
+		 * Sets the gzip compression level.
+		 *
+		 * @return a new {@link GzipOption.Builder}
+		 */
+		Builder compressionLevel(int compressionLevel);
+
+		/**
+		 * Sets the gzip window bits.
+		 *
+		 * @return a new {@link GzipOption.Builder}
+		 */
+		Builder windowBits(int windowBits);
+
+		/**
+		 * Sets the gzip memory level.
+		 *
+		 * @return a new {@link GzipOption.Builder}
+		 */
+		Builder memoryLevel(int memoryLevel);
+	}
+
+	private static final class Build implements Builder {
+
+		private int compressionLevel = 6;
+		private int windowBits = 12;
+		private int memoryLevel = 8;
+
+		@Override
+		public GzipOption build() {
+			return new GzipOption(this);
+		}
+
+		@Override
+		public Builder compressionLevel(int compressionLevel) {
+			ObjectUtil.checkInRange(compressionLevel, 0, 9, "compressionLevel");
+			this.compressionLevel = compressionLevel;
+			return this;
+		}
+
+		@Override
+		public Builder windowBits(int windowBits) {
+			ObjectUtil.checkInRange(windowBits, 9, 15, "windowBits");
+			this.windowBits = windowBits;
+			return this;
+		}
+
+		@Override
+		public Builder memoryLevel(int memoryLevel) {
+			ObjectUtil.checkInRange(memoryLevel, 1, 9, "memoryLevel");
+			this.memoryLevel = memoryLevel;
+			return this;
+		}
 	}
 }
diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOptionsSpec.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOptionsSpec.java
index dcf8f76d13..301fda3b61 100644
--- a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOptionsSpec.java
+++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/HttpCompressionOptionsSpec.java
@@ -37,16 +37,16 @@ public final class HttpCompressionOptionsSpec {
 	private ZstdOption zstd;
 
 	private HttpCompressionOptionsSpec() {
-		gzip = StandardHttpCompressionOptions.gzip();
-		deflate = StandardHttpCompressionOptions.deflate();
-		snappy = StandardHttpCompressionOptions.snappy();
+		gzip = GzipOption.provideDefault();
+		deflate = DeflateOption.provideDefault();
+		snappy = new SnappyOption();
 
 		if (Brotli.isAvailable()) {
-			brotli = StandardHttpCompressionOptions.brotli();
+			brotli = new BrotliOption();
 		}
 
 		if (Zstd.isAvailable()) {
-			zstd = StandardHttpCompressionOptions.zstd();
+			zstd = ZstdOption.provideDefault();
 		}
 	}
 
diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/SnappyOption.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/SnappyOption.java
index 33fecadab5..14a21a7cd3 100644
--- a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/SnappyOption.java
+++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/SnappyOption.java
@@ -25,13 +25,7 @@
  */
 final class SnappyOption implements HttpCompressionOption {
 
-	private final CompressionOptions option;
-
-	SnappyOption() {
-		this.option = StandardCompressionOptions.snappy();
-	}
-
 	CompressionOptions adapt() {
-		return option;
+		return StandardCompressionOptions.snappy();
 	}
 }
diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/StandardHttpCompressionOptions.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/StandardHttpCompressionOptions.java
deleted file mode 100644
index 29447f6d63..0000000000
--- a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/StandardHttpCompressionOptions.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (c) 2025 VMware, Inc. or its affiliates, All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package reactor.netty.http.server.compression;
-
-import io.netty.handler.codec.compression.Brotli;
-import io.netty.handler.codec.compression.Zstd;
-import io.netty.util.internal.ObjectUtil;
-
-/**
- * Standard Compression Options for {@link GzipOption}, {@link DeflateOption} and {@link ZstdOption}.
- *
- * @author raccoonback
- */
-public final class StandardHttpCompressionOptions {
-
-	private StandardHttpCompressionOptions() {
-	}
-
-	/**
-	 * Default GZIP options.
-	 * The default compression level is 6.
-	 *
-	 * @return a new {@link GzipOption}
-	 */
-	public static GzipOption gzip() {
-		return new GzipOption();
-	}
-
-	/**
-	 * Set GZIP options.
-	 *
-	 * @return a new {@link GzipOption}
-	 * @throws IllegalStateException If invalid parameters.
-	 */
-	public static GzipOption gzip(int compressionLevel, int windowBits, int memoryLevel) {
-		ObjectUtil.checkInRange(compressionLevel, 0, 9, "compressionLevel");
-		ObjectUtil.checkInRange(windowBits, 9, 15, "windowBits");
-		ObjectUtil.checkInRange(memoryLevel, 1, 9, "memLevel");
-
-		return new GzipOption(compressionLevel, windowBits, memoryLevel);
-	}
-
-	/**
-	 * Default Deflate options.
-	 * The default compression level is 6.
-	 *
-	 * @return a new {@link DeflateOption}
-	 */
-	public static DeflateOption deflate() {
-		return new DeflateOption();
-	}
-
-	/**
-	 * Set Deflate options.
-	 *
-	 * @return a new {@link DeflateOption}
-	 * @throws IllegalStateException If invalid parameters.
-	 */
-	public static DeflateOption deflate(int compressionLevel, int windowBits, int memoryLevel) {
-		ObjectUtil.checkInRange(compressionLevel, 0, 9, "compressionLevel");
-		ObjectUtil.checkInRange(windowBits, 9, 15, "windowBits");
-		ObjectUtil.checkInRange(memoryLevel, 1, 9, "memLevel");
-
-		return new DeflateOption(compressionLevel, windowBits, memoryLevel);
-	}
-
-	/**
-	 * Default ZSTD options.
-	 * The default compression level is 3.
-	 *
-	 * @return a new {@link ZstdOption}
-	 */
-	public static ZstdOption zstd() {
-		return new ZstdOption();
-	}
-
-	/**
-	 * Set ZSTD options.
-	 *
-	 * @return a new {@link ZstdOption}
-	 * @throws IllegalStateException If invalid parameters.
-	 */
-	public static ZstdOption zstd(int compressionLevel, int blockSize, int maxEncodeSize) {
-		ObjectUtil.checkInRange(compressionLevel, -(1 << 17), 22, "compressionLevel");
-		ObjectUtil.checkPositive(blockSize, "blockSize");
-		ObjectUtil.checkPositive(maxEncodeSize, "maxEncodeSize");
-
-		if (!Zstd.isAvailable()) {
-			throw new IllegalStateException("ZSTD is not available", Zstd.cause());
-		}
-
-		return new ZstdOption(compressionLevel, blockSize, maxEncodeSize);
-	}
-
-	/**
-	 * Default Brotli options.
-	 *
-	 * @return a new {@link BrotliOption}
-	 */
-	public static BrotliOption brotli() {
-		if (!Brotli.isAvailable()) {
-			throw new IllegalStateException("Brotli is not available", Brotli.cause());
-		}
-
-		return new BrotliOption();
-	}
-
-	/**
-	 * Default Snappy options.
-	 *
-	 * @return a new {@link SnappyOption}
-	 */
-	public static SnappyOption snappy() {
-		return new SnappyOption();
-	}
-}
diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/ZstdOption.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/ZstdOption.java
index b83e8cc880..70f0484db2 100644
--- a/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/ZstdOption.java
+++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/compression/ZstdOption.java
@@ -17,29 +17,112 @@
 
 import io.netty.handler.codec.compression.CompressionOptions;
 import io.netty.handler.codec.compression.StandardCompressionOptions;
+import io.netty.handler.codec.compression.Zstd;
+import io.netty.util.internal.ObjectUtil;
 
 /**
  * ZSTD compression option configuration.
  *
  * @author raccoonback
  */
-final class ZstdOption implements HttpCompressionOption {
+public final class ZstdOption implements HttpCompressionOption {
 
-	private final CompressionOptions option;
+	private final int compressionLevel;
+	private final int blockSize;
+	private final int maxEncodeSize;
 
-	ZstdOption() {
-		this.option = StandardCompressionOptions.zstd();
+	private ZstdOption(Build build) {
+		this.compressionLevel = build.compressionLevel;
+		this.blockSize = build.blockSize;
+		this.maxEncodeSize = build.maxEncodeSize;
 	}
 
-	ZstdOption(int compressionLevel, int windowBits, int memoryLevel) {
-		this.option = StandardCompressionOptions.zstd(
+	static ZstdOption provideDefault() {
+		return builder().build();
+	}
+
+	CompressionOptions adapt() {
+		return StandardCompressionOptions.zstd(
 				compressionLevel,
-				windowBits,
-				memoryLevel
+				blockSize,
+				maxEncodeSize
 		);
 	}
 
-	CompressionOptions adapt() {
-		return option;
+	/**
+	 * Creates a builder for {@link ZstdOption}.
+	 *
+	 * @return a new {@link ZstdOption.Builder}
+	 */
+	public static Builder builder() {
+		if (!Zstd.isAvailable()) {
+			throw new IllegalStateException("zstd is not available", Zstd.cause());
+		}
+
+		return new ZstdOption.Build();
+	}
+
+	public interface Builder {
+
+		/**
+		 * Build a new {@link ZstdOption}.
+		 *
+		 * @return a new {@link ZstdOption}
+		 */
+		ZstdOption build();
+
+		/**
+		 * Sets the zstd compression level.
+		 *
+		 * @return a new {@link ZstdOption.Builder}
+		 */
+		Builder compressionLevel(int compressionLevel);
+
+		/**
+		 * Sets the zstd block size.
+		 *
+		 * @return a new {@link ZstdOption.Builder}
+		 */
+		Builder blockSize(int blockSize);
+
+		/**
+		 * Sets the zstd memory level.
+		 *
+		 * @return a new {@link ZstdOption.Builder}
+		 */
+		Builder maxEncodeSize(int maxEncodeSize);
+	}
+
+	private static final class Build implements Builder {
+
+		private int compressionLevel = 3;
+		private int blockSize = 1 << 16;  // 64KB
+		private int maxEncodeSize = 1 << (compressionLevel + 7 + 0x0F);   //  32MB
+
+		@Override
+		public ZstdOption build() {
+			return new ZstdOption(this);
+		}
+
+		@Override
+		public Builder compressionLevel(int compressionLevel) {
+			ObjectUtil.checkInRange(compressionLevel, -(1 << 17), 22, "compressionLevel");
+			this.compressionLevel = compressionLevel;
+			return this;
+		}
+
+		@Override
+		public Builder blockSize(int blockSize) {
+			ObjectUtil.checkPositive(blockSize, "blockSize");
+			this.blockSize = blockSize;
+			return this;
+		}
+
+		@Override
+		public Builder maxEncodeSize(int maxEncodeSize) {
+			ObjectUtil.checkPositive(maxEncodeSize, "maxEncodeSize");
+			this.maxEncodeSize = maxEncodeSize;
+			return this;
+		}
 	}
 }
diff --git a/reactor-netty-http/src/test/java/reactor/netty/http/HttpCompressionClientServerTests.java b/reactor-netty-http/src/test/java/reactor/netty/http/HttpCompressionClientServerTests.java
index 059ef1a9dc..11737da3b5 100644
--- a/reactor-netty-http/src/test/java/reactor/netty/http/HttpCompressionClientServerTests.java
+++ b/reactor-netty-http/src/test/java/reactor/netty/http/HttpCompressionClientServerTests.java
@@ -59,7 +59,8 @@
 import reactor.netty.http.client.HttpClient;
 import reactor.netty.http.server.HttpServer;
 import reactor.netty.http.server.HttpServerResponse;
-import reactor.netty.http.server.compression.StandardHttpCompressionOptions;
+import reactor.netty.http.server.compression.GzipOption;
+import reactor.netty.http.server.compression.ZstdOption;
 import reactor.test.StepVerifier;
 import reactor.util.function.Tuple2;
 
@@ -727,7 +728,13 @@ void serverCompressionEnabledResponseCompressionDisabled(HttpServer server, Http
 	void serverCompressionWithCompressionLevelSettings(HttpServer server, HttpClient client) {
 		disposableServer =
 				server.compress(true)
-						.compressOptions(StandardHttpCompressionOptions.gzip(4, 15, 8))
+						.compressOptions(
+								GzipOption.builder()
+										.compressionLevel(4)
+										.windowBits(15)
+										.memoryLevel(8)
+										.build()
+						)
 						.handle((in, out) -> out.sendString(Mono.just("reply")))
 						.bindNow(Duration.ofSeconds(10));
 
@@ -766,7 +773,13 @@ void serverCompressionWithCompressionLevelSettings(HttpServer server, HttpClient
 	void serverCompressionEnabledWithGzipCompressionLevelSettings(HttpServer server, HttpClient client) throws Exception {
 		disposableServer =
 				server.compress(true)
-						.compressOptions(StandardHttpCompressionOptions.gzip(4, 15, 8))
+						.compressOptions(
+								GzipOption.builder()
+										.compressionLevel(4)
+										.windowBits(15)
+										.memoryLevel(8)
+										.build()
+						)
 						.handle((in, out) -> out.sendString(Mono.just("reply")))
 						.bindNow(Duration.ofSeconds(10));
 
@@ -801,7 +814,13 @@ void serverCompressionEnabledWithZstdCompressionLevel(HttpServer server, HttpCli
 		assertThat(Zstd.isAvailable()).isTrue();
 		disposableServer =
 				server.compress(true)
-						.compressOptions(StandardHttpCompressionOptions.zstd(12, 65536, 65536))
+						.compressOptions(
+								ZstdOption.builder()
+										.compressionLevel(12)
+										.blockSize(65536)
+										.maxEncodeSize(65536)
+										.build()
+						)
 						.handle((in, out) -> out.sendString(Mono.just("reply")))
 						.bindNow(Duration.ofSeconds(10));