From 0de8eda792d4e578c12883efb1459cec10eba98a Mon Sep 17 00:00:00 2001 From: sullis Date: Wed, 31 May 2023 02:22:26 -0700 Subject: [PATCH] Add Brotli compression test (#2815) --- build.gradle | 1 + reactor-netty-http/build.gradle | 9 +++++ .../http/server/SimpleCompressionHandler.java | 7 +++- .../HttpCompressionClientServerTests.java | 36 +++++++++++++++++++ 4 files changed, 52 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d2040ed1f2..931a858537 100644 --- a/build.gradle +++ b/build.gradle @@ -109,6 +109,7 @@ ext { nettyQuicVersion = '0.0.47.Final' // Testing + brotli4jVersion = '1.12.0' jacksonDatabindVersion = '2.15.2' testAddonVersion = reactorCoreVersion assertJVersion = '3.24.2' diff --git a/reactor-netty-http/build.gradle b/reactor-netty-http/build.gradle index 8d6c992c2b..501210b1bf 100644 --- a/reactor-netty-http/build.gradle +++ b/reactor-netty-http/build.gradle @@ -145,6 +145,15 @@ dependencies { testRuntimeOnly "org.slf4j:jcl-over-slf4j:$slf4jVersion" testRuntimeOnly "ch.qos.logback:logback-classic:$logbackVersion" + // Needed for Brotli compression + testImplementation "com.aayushatharva.brotli4j:brotli4j:$brotli4jVersion" + if (osdetector.classifier == "linux-aarch_64" || osdetector.classifier == "osx-aarch_64") { + testRuntimeOnly "com.aayushatharva.brotli4j:native-${osdetector.os}-aarch64:$brotli4jVersion" + } + else { + testRuntimeOnly "com.aayushatharva.brotli4j:native-${osdetector.classifier}:$brotli4jVersion" + } + // Needed for proxy testing testRuntimeOnly "io.netty:netty-handler-proxy:$nettyVersion" testRuntimeOnly "io.netty:netty-codec-haproxy:$nettyVersion" 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 6158ac0385..2ecc4760eb 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-2021 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2018-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. @@ -19,6 +19,7 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; import io.netty.handler.codec.DecoderException; +import io.netty.handler.codec.compression.CompressionOptions; import io.netty.handler.codec.http.DefaultHttpContent; import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; @@ -34,6 +35,10 @@ */ final class SimpleCompressionHandler extends HttpContentCompressor { + SimpleCompressionHandler() { + super((CompressionOptions[]) null); + } + @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { 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 435d24931c..de16691778 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 @@ -25,7 +25,10 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.zip.GZIPInputStream; +import com.aayushatharva.brotli4j.decoder.DecoderJNI; +import com.aayushatharva.brotli4j.decoder.DirectDecompress; import io.netty.buffer.Unpooled; +import io.netty.handler.codec.compression.Brotli; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.ssl.util.SelfSignedCertificate; @@ -187,6 +190,39 @@ void serverCompressionAlwaysEnabled(HttpServer server, HttpClient client) throws assertThat(deflated).isEqualTo("reply"); } + @ParameterizedCompressionTest + void brotliServerCompressionEnabled(HttpServer server, HttpClient client) throws Exception { + assertThat(Brotli.isAvailable()).isTrue(); + disposableServer = + server.compress(true) + .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()) + .compress(false) + .headers(h -> h.add("Accept-Encoding", "br, 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("br"); + + final byte[] compressedData = resp.getT1(); + assertThat(new String(compressedData, Charset.defaultCharset())).isNotEqualTo("reply"); + + DirectDecompress directDecompress = DirectDecompress.decompress(compressedData); + assertThat(directDecompress.getResultStatus()).isEqualTo(DecoderJNI.Status.DONE); + final byte[] decompressedData = directDecompress.getDecompressedData(); + assertThat(decompressedData).isNotEmpty(); + assertThat(new String(decompressedData, Charset.defaultCharset())).isEqualTo("reply"); + } + @ParameterizedCompressionTest void serverCompressionEnabledSmallResponse(HttpServer server, HttpClient client) { disposableServer =