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:
parent
69582c0b6c
commit
06dcca1dbc
@ -32,6 +32,7 @@ public class HttpContentCompressor extends HttpContentEncoder {
|
|||||||
private final int compressionLevel;
|
private final int compressionLevel;
|
||||||
private final int windowBits;
|
private final int windowBits;
|
||||||
private final int memLevel;
|
private final int memLevel;
|
||||||
|
private final int contentSizeThreshold;
|
||||||
private ChannelHandlerContext ctx;
|
private ChannelHandlerContext ctx;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,7 +53,7 @@ public class HttpContentCompressor extends HttpContentEncoder {
|
|||||||
* compression level is {@code 6}.
|
* compression level is {@code 6}.
|
||||||
*/
|
*/
|
||||||
public HttpContentCompressor(int compressionLevel) {
|
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}
|
* at the expense of memory usage. The default value is {@code 8}
|
||||||
*/
|
*/
|
||||||
public HttpContentCompressor(int compressionLevel, int windowBits, int memLevel) {
|
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) {
|
if (compressionLevel < 0 || compressionLevel > 9) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"compressionLevel: " + compressionLevel +
|
"compressionLevel: " + compressionLevel +
|
||||||
@ -88,9 +116,14 @@ public class HttpContentCompressor extends HttpContentEncoder {
|
|||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"memLevel: " + memLevel + " (expected: 1-9)");
|
"memLevel: " + memLevel + " (expected: 1-9)");
|
||||||
}
|
}
|
||||||
|
if (contentSizeThreshold < 0) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"contentSizeThreshold: " + contentSizeThreshold + " (expected: non negative number)");
|
||||||
|
}
|
||||||
this.compressionLevel = compressionLevel;
|
this.compressionLevel = compressionLevel;
|
||||||
this.windowBits = windowBits;
|
this.windowBits = windowBits;
|
||||||
this.memLevel = memLevel;
|
this.memLevel = memLevel;
|
||||||
|
this.contentSizeThreshold = contentSizeThreshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -100,6 +133,13 @@ public class HttpContentCompressor extends HttpContentEncoder {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Result beginEncode(HttpResponse headers, String acceptEncoding) throws Exception {
|
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);
|
String contentEncoding = headers.headers().get(HttpHeaderNames.CONTENT_ENCODING);
|
||||||
if (contentEncoding != null) {
|
if (contentEncoding != null) {
|
||||||
// Content-Encoding was set, either as something specific or as the IDENTITY encoding
|
// Content-Encoding was set, either as something specific or as the IDENTITY encoding
|
||||||
|
@ -29,11 +29,7 @@ import static org.hamcrest.CoreMatchers.instanceOf;
|
|||||||
import static org.hamcrest.CoreMatchers.is;
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
import static org.hamcrest.CoreMatchers.not;
|
import static org.hamcrest.CoreMatchers.not;
|
||||||
import static org.hamcrest.CoreMatchers.nullValue;
|
import static org.hamcrest.CoreMatchers.nullValue;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.assertSame;
|
|
||||||
import static org.junit.Assert.assertThat;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
public class HttpContentCompressorTest {
|
public class HttpContentCompressorTest {
|
||||||
|
|
||||||
@ -454,6 +450,52 @@ public class HttpContentCompressorTest {
|
|||||||
assertTrue(ch.finishAndReleaseAll());
|
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() {
|
private static FullHttpRequest newRequest() {
|
||||||
FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/");
|
FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/");
|
||||||
req.headers().set(HttpHeaderNames.ACCEPT_ENCODING, "gzip");
|
req.headers().set(HttpHeaderNames.ACCEPT_ENCODING, "gzip");
|
||||||
|
Loading…
Reference in New Issue
Block a user