Skip to content

Commit 7866b94

Browse files
Stephane Maldinibclozel
authored andcommitted
Add Netty Compression support
Reactor Netty 0.7.5 now supports all Compression options. Unlike previous Reactor Netty versions, the minimum compression threshold is now based on the content-length instead of accumulating for a while the data. See gh-12268
1 parent 0abe62e commit 7866b94

File tree

3 files changed

+124
-6
lines changed

3 files changed

+124
-6
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
* Copyright 2012-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.web.embedded.netty;
18+
19+
import java.util.function.BiPredicate;
20+
21+
import io.netty.handler.codec.http.HttpHeaderNames;
22+
import reactor.ipc.netty.http.server.HttpServerOptions;
23+
import reactor.ipc.netty.http.server.HttpServerRequest;
24+
import reactor.ipc.netty.http.server.HttpServerResponse;
25+
26+
import org.springframework.boot.web.server.Compression;
27+
import org.springframework.util.MimeType;
28+
import org.springframework.util.MimeTypeUtils;
29+
30+
/**
31+
* Configure the HTTP compression on an Reactor Netty request/response handler.
32+
*
33+
* @author Stephane Maldini
34+
*/
35+
final class CompressionCustomizer implements NettyServerCustomizer {
36+
37+
private final Compression compression;
38+
39+
CompressionCustomizer(Compression compression) {
40+
this.compression = compression;
41+
}
42+
43+
@Override
44+
public void customize(HttpServerOptions.Builder builder) {
45+
if (compression.getMinResponseSize() >= 0) {
46+
builder.compression(compression.getMinResponseSize());
47+
}
48+
49+
BiPredicate<HttpServerRequest, HttpServerResponse> compressPredicate = null;
50+
51+
if (compression.getMimeTypes() != null &&
52+
compression.getMimeTypes().length > 0) {
53+
compressPredicate = new CompressibleMimeTypePredicate(compression.getMimeTypes());
54+
}
55+
56+
if (compression.getExcludedUserAgents() != null &&
57+
compression.getExcludedUserAgents().length > 0 ) {
58+
BiPredicate<HttpServerRequest, HttpServerResponse> agentCompressPredicate =
59+
new CompressibleAgentPredicate(compression.getExcludedUserAgents());
60+
61+
compressPredicate = compressPredicate == null ?
62+
agentCompressPredicate :
63+
compressPredicate.and(agentCompressPredicate);
64+
}
65+
66+
if (compressPredicate != null) {
67+
builder.compression(compressPredicate);
68+
}
69+
}
70+
71+
private static class CompressibleAgentPredicate
72+
implements BiPredicate<HttpServerRequest, HttpServerResponse> {
73+
74+
private final String[] excludedAgents;
75+
76+
CompressibleAgentPredicate(String[] excludedAgents) {
77+
this.excludedAgents = new String[excludedAgents.length];
78+
System.arraycopy(excludedAgents, 0, this.excludedAgents, 0, excludedAgents.length);
79+
}
80+
81+
@Override
82+
public boolean test(HttpServerRequest request, HttpServerResponse response) {
83+
for(String excludedAgent : excludedAgents) {
84+
if (request.requestHeaders()
85+
.contains(HttpHeaderNames.USER_AGENT, excludedAgent, true)) {
86+
return false;
87+
}
88+
}
89+
return true;
90+
}
91+
}
92+
93+
private static class CompressibleMimeTypePredicate
94+
implements BiPredicate<HttpServerRequest, HttpServerResponse> {
95+
96+
private final MimeType[] mimeTypes;
97+
98+
CompressibleMimeTypePredicate(String[] mimeTypes) {
99+
this.mimeTypes = new MimeType[mimeTypes.length];
100+
for (int i = 0; i < mimeTypes.length; i++) {
101+
this.mimeTypes[i] = MimeTypeUtils.parseMimeType(mimeTypes[i]);
102+
}
103+
}
104+
105+
@Override
106+
public boolean test(HttpServerRequest request, HttpServerResponse response) {
107+
String contentType = response.responseHeaders()
108+
.get(HttpHeaderNames.CONTENT_TYPE);
109+
if (contentType != null) {
110+
for (MimeType mimeType : this.mimeTypes) {
111+
if (mimeType.isCompatibleWith(MimeTypeUtils.parseMimeType(contentType))) {
112+
return true;
113+
}
114+
}
115+
}
116+
return false;
117+
}
118+
119+
}
120+
}

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/netty/NettyReactiveWebServerFactory.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,10 @@ private HttpServer createHttpServer() {
106106
getSsl(), getSslStoreProvider());
107107
sslServerCustomizer.customize(options);
108108
}
109-
if (getCompression() != null) {
110-
options.compression(getCompression().getEnabled());
109+
if (getCompression() != null && getCompression().getEnabled()) {
110+
CompressionCustomizer compressionCustomizer = new CompressionCustomizer(
111+
getCompression());
112+
compressionCustomizer.customize(options);
111113
}
112114
applyCustomizers(options);
113115
}).build();

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/reactive/server/AbstractReactiveWebServerFactoryTests.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -275,8 +275,6 @@ public void compressionOfResponseToPostRequest() throws Exception {
275275

276276
@Test
277277
public void noCompressionForMimeType() throws Exception {
278-
Assumptions.assumeThat(getFactory())
279-
.isNotInstanceOf(NettyReactiveWebServerFactory.class);
280278
Compression compression = new Compression();
281279
compression.setMimeTypes(new String[] { "application/json" });
282280
WebClient client = prepareCompressionTest(compression);
@@ -287,8 +285,6 @@ public void noCompressionForMimeType() throws Exception {
287285

288286
@Test
289287
public void noCompressionForUserAgent() throws Exception {
290-
Assumptions.assumeThat(getFactory())
291-
.isNotInstanceOf(NettyReactiveWebServerFactory.class);
292288
Compression compression = new Compression();
293289
compression.setEnabled(true);
294290
compression.setExcludedUserAgents(new String[] { "testUserAgent" });

0 commit comments

Comments
 (0)