-
Notifications
You must be signed in to change notification settings - Fork 657
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Supports configurable compression level #3567
base: main
Are you sure you want to change the base?
Changes from 3 commits
94a3d1d
a060024
38f3bcc
2c653c1
5373a2a
100d2da
7eee9c9
99287f4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Compression settings supported by Netty. |
||
|
||
private HttpCompressionSettingsSpec() { | ||
gzipOptions = StandardCompressionOptions.gzip(); | ||
deflateOptions = StandardCompressionOptions.deflate(); | ||
snappyOptions = StandardCompressionOptions.snappy(); | ||
|
||
if (Brotli.isAvailable()) { | ||
brotliOptions = StandardCompressionOptions.brotli(); | ||
} | ||
|
||
if (Zstd.isAvailable()) { | ||
zstdOptions = StandardCompressionOptions.zstd(); | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
|
||
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; | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Initializes to each compression setting entered by the Builder. |
||
|
||
/** | ||
* 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<CompressionOptions> 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]); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adapts to the type to be provided to the constructor argument of Netty's |
||
|
||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @raccoonback If in the future you need to extend the configuration API with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @violetagg The reason why For now, we’ve chosen to rely on default values without exposing these settings directly to users. However, we’ve considered the need for extensibility in future requirements. If it becomes necessary to make Builder gzip(int compressionLevel);
Builder gzip(int compressionLevel, int windowBits);
Builder gzip(int compressionLevel, int windowBits, int memoryLevel);
// ... Additionally, if required, we can extend the API to provide these as optional settings while maintaining the default values. Let me know your thoughts, and I’ll adjust the design accordingly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mmm ... method overloading is not something that I would like to see in the API. Can we use a similar approach to Netty's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @violetagg |
||
|
||
/** | ||
* 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; | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A builder that takes each compression setting as input from the user. |
||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Defines a spec for configuring HTTP compression. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -301,6 +301,7 @@ public final HttpServer compress(BiPredicate<HttpServerRequest, HttpServerRespon | |
/** | ||
* Specifies whether GZip response compression is enabled if the client request | ||
* presents accept encoding. | ||
* default compression level is 6. | ||
* | ||
* @param compressionEnabled if true GZip response compression | ||
* is enabled if the client request presents accept encoding, otherwise disabled. | ||
|
@@ -336,6 +337,35 @@ public final HttpServer compress(int minResponseSize) { | |
return dup; | ||
} | ||
|
||
/** | ||
* Specifies GZIP, DEFLATE, ZSTD Compression Level. | ||
* | ||
* @param compressionSettings configures {@link HttpCompressionSettingsSpec} after enable compress. | ||
* <pre> | ||
* {@code | ||
* HttpServer.create() | ||
* .compress(true) | ||
* .compressOptions( | ||
* builder -> builder.gzip(6) | ||
* .deflate(6) | ||
* .zstd(3) | ||
* ) | ||
* .bindNow(); | ||
* } | ||
* </pre> | ||
* @return a new {@link HttpServer} | ||
*/ | ||
public final HttpServer compressSettings(Consumer<HttpCompressionSettingsSpec.Builder> compressionSettings) { | ||
Objects.requireNonNull(compressionSettings, "compressionSettings"); | ||
|
||
HttpCompressionSettingsSpec.Builder builder = HttpCompressionSettingsSpec.builder(); | ||
compressionSettings.accept(builder); | ||
|
||
HttpServer dup = duplicate(); | ||
dup.configuration().compressionSettings = builder.build(); | ||
return dup; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method takes compression settings from the user. |
||
|
||
/** | ||
* Configure the | ||
* {@link ServerCookieEncoder}; {@link ServerCookieDecoder} will be | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apply the configured compression settings.