diff --git a/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/WebServerDTO.java b/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/WebServerDTO.java
index b118c708ad3..9a122e75125 100644
--- a/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/WebServerDTO.java
+++ b/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/WebServerDTO.java
@@ -113,6 +113,12 @@ public class WebServerDTO extends ComponentDTO {
@XmlAttribute
public Integer maxResponseHeaderSize;
+ @XmlAttribute
+ public Boolean compressionEnabled;
+
+ @XmlAttribute
+ public Integer compressionLevel;
+
public String getPath() {
return path;
}
diff --git a/artemis-web/pom.xml b/artemis-web/pom.xml
index 7459b950466..2f887519b97 100644
--- a/artemis-web/pom.xml
+++ b/artemis-web/pom.xml
@@ -102,6 +102,14 @@
org.eclipse.jetty
jetty-alpn-java-server
+
+ org.eclipse.jetty.compression
+ jetty-compression-server
+
+
+ org.eclipse.jetty.compression
+ jetty-compression-gzip
+
org.apache.artemis
artemis-server
diff --git a/artemis-web/src/main/java/org/apache/activemq/artemis/component/WebServerComponent.java b/artemis-web/src/main/java/org/apache/activemq/artemis/component/WebServerComponent.java
index 316b0e31205..c5af6026bd8 100644
--- a/artemis-web/src/main/java/org/apache/activemq/artemis/component/WebServerComponent.java
+++ b/artemis-web/src/main/java/org/apache/activemq/artemis/component/WebServerComponent.java
@@ -57,6 +57,11 @@
import org.apache.activemq.artemis.utils.PemConfigUtil;
import org.apache.activemq.artemis.utils.sm.SecurityManagerShim;
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
+import org.eclipse.jetty.compression.Compression;
+import org.eclipse.jetty.compression.EncoderConfig;
+import org.eclipse.jetty.compression.gzip.GzipCompression;
+import org.eclipse.jetty.compression.gzip.GzipEncoderConfig;
+import org.eclipse.jetty.compression.server.CompressionHandler;
import org.eclipse.jetty.ee9.security.DefaultAuthenticatorFactory;
import org.eclipse.jetty.ee9.servlet.FilterHolder;
import org.eclipse.jetty.ee9.webapp.WebAppContext;
@@ -149,6 +154,21 @@ public synchronized void start() throws Exception {
Scheduler scheduler = new ScheduledExecutorScheduler("activemq-web-scheduled", false);
server = new Server(threadPool, scheduler, null);
handlers = new Handler.Sequence();
+ if (webServerConfig.compressionEnabled != null && webServerConfig.compressionEnabled) {
+ int compressionLevel = Objects.requireNonNullElse(webServerConfig.compressionLevel, 6);
+ logger.debug("embedded web server is using GZIP compression level {}", compressionLevel);
+ EncoderConfig encoderConfig = new GzipEncoderConfig();
+ encoderConfig.setCompressionLevel(compressionLevel);
+ Compression compression = new GzipCompression();
+ compression.setDefaultEncoderConfig(encoderConfig);
+ CompressionHandler compressionHandler = new CompressionHandler();
+ compressionHandler.putCompression(compression);
+ compressionHandler.setHandler(handlers);
+ server.setHandler(compressionHandler);
+ } else {
+ server.setHandler(handlers);
+ }
+
HttpConfiguration httpConfiguration = new HttpConfiguration();
@@ -253,8 +273,6 @@ public void requestDestroyed(ServletRequestEvent sre) {
handlers.addHandler(defaultHandler); // this should be last
- server.setHandler(handlers);
-
server.start();
printStatus(bindings);
diff --git a/artemis-web/src/test/java/org/apache/activemq/cli/test/WebServerComponentTest.java b/artemis-web/src/test/java/org/apache/activemq/cli/test/WebServerComponentTest.java
index 1f56dad6bdb..a6134483cee 100644
--- a/artemis-web/src/test/java/org/apache/activemq/cli/test/WebServerComponentTest.java
+++ b/artemis-web/src/test/java/org/apache/activemq/cli/test/WebServerComponentTest.java
@@ -65,6 +65,7 @@
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaderNames;
+import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpObject;
import io.netty.handler.codec.http.HttpRequest;
@@ -202,6 +203,62 @@ private void internalSimpleServer(boolean useCustomizer) throws Exception {
assertFalse(webServerComponent.isStarted());
}
+ @Test
+ public void testCompressionEnabled() throws Exception {
+ testCompression(true);
+ }
+
+ @Test
+ public void testCompressionDisabled() throws Exception {
+ testCompression(false);
+ }
+
+ private void testCompression(boolean compressionEnabled) throws Exception {
+ final String encoding = "gzip";
+
+ BindingDTO bindingDTO = new BindingDTO();
+ bindingDTO.uri = "http://localhost:0";
+ WebServerDTO webServerDTO = new WebServerDTO();
+ webServerDTO.setBindings(Collections.singletonList(bindingDTO));
+ webServerDTO.path = "webapps";
+ webServerDTO.webContentEnabled = true;
+ webServerDTO.compressionEnabled = compressionEnabled;
+ WebServerComponent webServerComponent = new WebServerComponent();
+ assertFalse(webServerComponent.isStarted());
+ webServerComponent.configure(webServerDTO, "src/test/resources/", "src/test/resources/");
+ testedComponents.add(webServerComponent);
+ webServerComponent.start();
+ final int port = webServerComponent.getPort();
+ // Make the connection attempt.
+ CountDownLatch latch = new CountDownLatch(1);
+ final ClientHandler clientHandler = new ClientHandler(latch);
+ Channel ch = getChannel(port, clientHandler);
+
+ // this file is different from the other tests because it has to be above a certain size in order to be compressed
+ URI uri = new URI("http://localhost/WebServerComponentCompressionTest.txt");
+ // Prepare the HTTP request.
+ HttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri.getRawPath());
+ request.headers().set(HttpHeaderNames.HOST, "localhost");
+ request.headers().set(HttpHeaderNames.ACCEPT_ENCODING, encoding);
+
+ // Send the HTTP request.
+ ch.writeAndFlush(request);
+ assertTrue(latch.await(5, TimeUnit.SECONDS));
+ String contentEncoding = clientHandler.headers.get(HttpHeaderNames.CONTENT_ENCODING);
+ if (compressionEnabled) {
+ assertEquals(encoding, contentEncoding);
+ } else {
+ assertNull(contentEncoding);
+ }
+
+ // Wait for the server to close the connection.
+ ch.close();
+ ch.eventLoop().shutdownNow();
+ assertTrue(webServerComponent.isStarted());
+ webServerComponent.stop(true);
+ assertFalse(webServerComponent.isStarted());
+ }
+
@Test
public void testThreadPool() throws Exception {
BindingDTO bindingDTO = new BindingDTO();
@@ -1133,6 +1190,7 @@ class ClientHandler extends SimpleChannelInboundHandler {
private CountDownLatch latch;
private StringBuilder body = new StringBuilder();
private String serverHeader;
+ private HttpHeaders headers;
ClientHandler(CountDownLatch latch) {
this.latch = latch;
@@ -1141,6 +1199,7 @@ class ClientHandler extends SimpleChannelInboundHandler {
@Override
public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) {
if (msg instanceof HttpResponse response) {
+ headers = response.headers();
serverHeader = response.headers().get("Server");
} else if (msg instanceof HttpContent content) {
body.append(content.content().toString(CharsetUtil.UTF_8));
diff --git a/artemis-web/src/test/resources/webapps/WebServerComponentCompressionTest.txt b/artemis-web/src/test/resources/webapps/WebServerComponentCompressionTest.txt
new file mode 100644
index 00000000000..fd4f971ab3b
--- /dev/null
+++ b/artemis-web/src/test/resources/webapps/WebServerComponentCompressionTest.txt
@@ -0,0 +1 @@
+0123456789012345678901234567890123456789
\ No newline at end of file
diff --git a/docs/user-manual/web-server.adoc b/docs/user-manual/web-server.adoc
index 9f648e8fa2e..b71aba496ee 100644
--- a/docs/user-manual/web-server.adoc
+++ b/docs/user-manual/web-server.adoc
@@ -34,6 +34,15 @@ The location to redirect the requests with the root target.
webContentEnabled::
Whether or not the content included in the web folder of the home and the instance directories is accessible.
Default is `false`.
+compressionEnabled::
+Whether to compress HTTP responses.
+Uses `gzip` encoding for maximum compatibility.
+This will impact any client communicating with the embedded web server including the web console and consumers of xref:metrics.adoc#metrics[metrics] (e.g. Prometheus) assuming they set the `Accept-Encoding` header to `gzip` in their HTTP requests.
+Default is `false`.
+compressionLevel::
+The level of compression for HTTP responses.
+Only valid if `compressionEnabled` is `true`.
+Default is `6`. Must be between `0` and `9` inclusive.
maxThreads::
The maximum number of threads the embedded web server can create to service HTTP requests.
Default is `200`.