When the response exceeds the threshold, it will be compressed

Motivation:

When the response is very small, compression will inflate the response.

Modifications:

Add filed io.netty.handler.codec.http.HttpContentCompressor#compressThreshold that control whether the HTTP response should be compressed.

Result:

Fixes #7660.
This commit is contained in:
teaey 2018-02-22 14:09:23 +08:00 committed by Norman Maurer
parent 69582c0b6c
commit 06dcca1dbc
2 changed files with 88 additions and 6 deletions

View File

@ -32,6 +32,7 @@ public class HttpContentCompressor extends HttpContentEncoder {
private final int compressionLevel;
private final int windowBits;
private final int memLevel;
private final int contentSizeThreshold;
private ChannelHandlerContext ctx;
/**
@ -52,7 +53,7 @@ public class HttpContentCompressor extends HttpContentEncoder {
* compression level is {@code 6}.
*/
public HttpContentCompressor(int compressionLevel) {
this(compressionLevel, 15, 8);
this(compressionLevel, 15, 8, 0);
}
/**
@ -75,6 +76,33 @@ public class HttpContentCompressor extends HttpContentEncoder {
* at the expense of memory usage. The default value is {@code 8}
*/
public HttpContentCompressor(int compressionLevel, int windowBits, int memLevel) {
this(compressionLevel, windowBits, memLevel, 0);
}
/**
* Creates a new handler with the specified compression level, window size,
* and memory level..
*
* @param compressionLevel
* {@code 1} yields the fastest compression and {@code 9} yields the
* best compression. {@code 0} means no compression. The default
* compression level is {@code 6}.
* @param windowBits
* The base two logarithm of the size of the history buffer. The
* value should be in the range {@code 9} to {@code 15} inclusive.
* Larger values result in better compression at the expense of
* memory usage. The default value is {@code 15}.
* @param memLevel
* How much memory should be allocated for the internal compression
* state. {@code 1} uses minimum memory and {@code 9} uses maximum
* memory. Larger values result in better and faster compression
* at the expense of memory usage. The default value is {@code 8}
* @param contentSizeThreshold
* The response body is compressed when the size of the response
* body exceeds the threshold. The value should be a non negative
* number. {@code 0} will enable compression for all responses.
*/
public HttpContentCompressor(int compressionLevel, int windowBits, int memLevel, int contentSizeThreshold) {
if (compressionLevel < 0 || compressionLevel > 9) {
throw new IllegalArgumentException(
"compressionLevel: " + compressionLevel +
@ -88,9 +116,14 @@ public class HttpContentCompressor extends HttpContentEncoder {
throw new IllegalArgumentException(
"memLevel: " + memLevel + " (expected: 1-9)");
}
if (contentSizeThreshold < 0) {
throw new IllegalArgumentException(
"contentSizeThreshold: " + contentSizeThreshold + " (expected: non negative number)");
}
this.compressionLevel = compressionLevel;
this.windowBits = windowBits;
this.memLevel = memLevel;
this.contentSizeThreshold = contentSizeThreshold;
}
@Override
@ -100,6 +133,13 @@ public class HttpContentCompressor extends HttpContentEncoder {
@Override
protected Result beginEncode(HttpResponse headers, String acceptEncoding) throws Exception {
if (this.contentSizeThreshold > 0) {
if (headers instanceof HttpContent &&
((HttpContent) headers).content().readableBytes() < contentSizeThreshold) {
return null;
}
}
String contentEncoding = headers.headers().get(HttpHeaderNames.CONTENT_ENCODING);
if (contentEncoding != null) {
// Content-Encoding was set, either as something specific or as the IDENTITY encoding

View File

@ -29,11 +29,7 @@ import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assert.*;
public class HttpContentCompressorTest {
@ -454,6 +450,52 @@ public class HttpContentCompressorTest {
assertTrue(ch.finishAndReleaseAll());
}
@Test
public void testCompressThresholdAllCompress() throws Exception {
EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor());
assertTrue(ch.writeInbound(newRequest()));
FullHttpResponse res1023 = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
Unpooled.wrappedBuffer(new byte[1023]));
assertTrue(ch.writeOutbound(res1023));
DefaultHttpResponse response1023 = ch.readOutbound();
assertThat(response1023.headers().get(HttpHeaderNames.CONTENT_ENCODING), is("gzip"));
ch.releaseOutbound();
assertTrue(ch.writeInbound(newRequest()));
FullHttpResponse res1024 = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
Unpooled.wrappedBuffer(new byte[1024]));
assertTrue(ch.writeOutbound(res1024));
DefaultHttpResponse response1024 = ch.readOutbound();
assertThat(response1024.headers().get(HttpHeaderNames.CONTENT_ENCODING), is("gzip"));
assertTrue(ch.finishAndReleaseAll());
}
@Test
public void testCompressThresholdNotCompress() throws Exception {
EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor(6, 15, 8, 1024));
assertTrue(ch.writeInbound(newRequest()));
FullHttpResponse res1023 = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
Unpooled.wrappedBuffer(new byte[1023]));
assertTrue(ch.writeOutbound(res1023));
DefaultHttpResponse response1023 = ch.readOutbound();
assertFalse(response1023.headers().contains(HttpHeaderNames.CONTENT_ENCODING));
ch.releaseOutbound();
assertTrue(ch.writeInbound(newRequest()));
FullHttpResponse res1024 = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
Unpooled.wrappedBuffer(new byte[1024]));
assertTrue(ch.writeOutbound(res1024));
DefaultHttpResponse response1024 = ch.readOutbound();
assertThat(response1024.headers().get(HttpHeaderNames.CONTENT_ENCODING), is("gzip"));
assertTrue(ch.finishAndReleaseAll());
}
private static FullHttpRequest newRequest() {
FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/");
req.headers().set(HttpHeaderNames.ACCEPT_ENCODING, "gzip");