Skip to content

Commit 2df59c5

Browse files
jbertrambrusdev
authored andcommitted
ARTEMIS-5833 support compression for HTTP responses
1 parent 3ac7948 commit 2df59c5

File tree

6 files changed

+103
-2
lines changed

6 files changed

+103
-2
lines changed

artemis-dto/src/main/java/org/apache/activemq/artemis/dto/WebServerDTO.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@ public class WebServerDTO extends ComponentDTO {
113113
@XmlAttribute
114114
public Integer maxResponseHeaderSize;
115115

116+
@XmlAttribute
117+
public Boolean compressionEnabled;
118+
119+
@XmlAttribute
120+
public Integer compressionLevel;
121+
116122
public String getPath() {
117123
return path;
118124
}

artemis-web/pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,14 @@
102102
<groupId>org.eclipse.jetty</groupId>
103103
<artifactId>jetty-alpn-java-server</artifactId>
104104
</dependency>
105+
<dependency>
106+
<groupId>org.eclipse.jetty.compression</groupId>
107+
<artifactId>jetty-compression-server</artifactId>
108+
</dependency>
109+
<dependency>
110+
<groupId>org.eclipse.jetty.compression</groupId>
111+
<artifactId>jetty-compression-gzip</artifactId>
112+
</dependency>
105113
<dependency>
106114
<groupId>org.apache.artemis</groupId>
107115
<artifactId>artemis-server</artifactId>

artemis-web/src/main/java/org/apache/activemq/artemis/component/WebServerComponent.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@
5757
import org.apache.activemq.artemis.utils.PemConfigUtil;
5858
import org.apache.activemq.artemis.utils.sm.SecurityManagerShim;
5959
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
60+
import org.eclipse.jetty.compression.Compression;
61+
import org.eclipse.jetty.compression.EncoderConfig;
62+
import org.eclipse.jetty.compression.gzip.GzipCompression;
63+
import org.eclipse.jetty.compression.gzip.GzipEncoderConfig;
64+
import org.eclipse.jetty.compression.server.CompressionHandler;
6065
import org.eclipse.jetty.ee9.security.DefaultAuthenticatorFactory;
6166
import org.eclipse.jetty.ee9.servlet.FilterHolder;
6267
import org.eclipse.jetty.ee9.webapp.WebAppContext;
@@ -149,6 +154,21 @@ public synchronized void start() throws Exception {
149154
Scheduler scheduler = new ScheduledExecutorScheduler("activemq-web-scheduled", false);
150155
server = new Server(threadPool, scheduler, null);
151156
handlers = new Handler.Sequence();
157+
if (webServerConfig.compressionEnabled != null && webServerConfig.compressionEnabled) {
158+
int compressionLevel = Objects.requireNonNullElse(webServerConfig.compressionLevel, 6);
159+
logger.debug("embedded web server is using GZIP compression level {}", compressionLevel);
160+
EncoderConfig encoderConfig = new GzipEncoderConfig();
161+
encoderConfig.setCompressionLevel(compressionLevel);
162+
Compression compression = new GzipCompression();
163+
compression.setDefaultEncoderConfig(encoderConfig);
164+
CompressionHandler compressionHandler = new CompressionHandler();
165+
compressionHandler.putCompression(compression);
166+
compressionHandler.setHandler(handlers);
167+
server.setHandler(compressionHandler);
168+
} else {
169+
server.setHandler(handlers);
170+
}
171+
152172

153173
HttpConfiguration httpConfiguration = new HttpConfiguration();
154174

@@ -253,8 +273,6 @@ public void requestDestroyed(ServletRequestEvent sre) {
253273

254274
handlers.addHandler(defaultHandler); // this should be last
255275

256-
server.setHandler(handlers);
257-
258276
server.start();
259277

260278
printStatus(bindings);

artemis-web/src/test/java/org/apache/activemq/cli/test/WebServerComponentTest.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
import io.netty.handler.codec.http.HttpClientCodec;
6666
import io.netty.handler.codec.http.HttpContent;
6767
import io.netty.handler.codec.http.HttpHeaderNames;
68+
import io.netty.handler.codec.http.HttpHeaders;
6869
import io.netty.handler.codec.http.HttpMethod;
6970
import io.netty.handler.codec.http.HttpObject;
7071
import io.netty.handler.codec.http.HttpRequest;
@@ -202,6 +203,62 @@ private void internalSimpleServer(boolean useCustomizer) throws Exception {
202203
assertFalse(webServerComponent.isStarted());
203204
}
204205

206+
@Test
207+
public void testCompressionEnabled() throws Exception {
208+
testCompression(true);
209+
}
210+
211+
@Test
212+
public void testCompressionDisabled() throws Exception {
213+
testCompression(false);
214+
}
215+
216+
private void testCompression(boolean compressionEnabled) throws Exception {
217+
final String encoding = "gzip";
218+
219+
BindingDTO bindingDTO = new BindingDTO();
220+
bindingDTO.uri = "http://localhost:0";
221+
WebServerDTO webServerDTO = new WebServerDTO();
222+
webServerDTO.setBindings(Collections.singletonList(bindingDTO));
223+
webServerDTO.path = "webapps";
224+
webServerDTO.webContentEnabled = true;
225+
webServerDTO.compressionEnabled = compressionEnabled;
226+
WebServerComponent webServerComponent = new WebServerComponent();
227+
assertFalse(webServerComponent.isStarted());
228+
webServerComponent.configure(webServerDTO, "src/test/resources/", "src/test/resources/");
229+
testedComponents.add(webServerComponent);
230+
webServerComponent.start();
231+
final int port = webServerComponent.getPort();
232+
// Make the connection attempt.
233+
CountDownLatch latch = new CountDownLatch(1);
234+
final ClientHandler clientHandler = new ClientHandler(latch);
235+
Channel ch = getChannel(port, clientHandler);
236+
237+
// this file is different from the other tests because it has to be above a certain size in order to be compressed
238+
URI uri = new URI("http://localhost/WebServerComponentCompressionTest.txt");
239+
// Prepare the HTTP request.
240+
HttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri.getRawPath());
241+
request.headers().set(HttpHeaderNames.HOST, "localhost");
242+
request.headers().set(HttpHeaderNames.ACCEPT_ENCODING, encoding);
243+
244+
// Send the HTTP request.
245+
ch.writeAndFlush(request);
246+
assertTrue(latch.await(5, TimeUnit.SECONDS));
247+
String contentEncoding = clientHandler.headers.get(HttpHeaderNames.CONTENT_ENCODING);
248+
if (compressionEnabled) {
249+
assertEquals(encoding, contentEncoding);
250+
} else {
251+
assertNull(contentEncoding);
252+
}
253+
254+
// Wait for the server to close the connection.
255+
ch.close();
256+
ch.eventLoop().shutdownNow();
257+
assertTrue(webServerComponent.isStarted());
258+
webServerComponent.stop(true);
259+
assertFalse(webServerComponent.isStarted());
260+
}
261+
205262
@Test
206263
public void testThreadPool() throws Exception {
207264
BindingDTO bindingDTO = new BindingDTO();
@@ -1133,6 +1190,7 @@ class ClientHandler extends SimpleChannelInboundHandler<HttpObject> {
11331190
private CountDownLatch latch;
11341191
private StringBuilder body = new StringBuilder();
11351192
private String serverHeader;
1193+
private HttpHeaders headers;
11361194

11371195
ClientHandler(CountDownLatch latch) {
11381196
this.latch = latch;
@@ -1141,6 +1199,7 @@ class ClientHandler extends SimpleChannelInboundHandler<HttpObject> {
11411199
@Override
11421200
public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) {
11431201
if (msg instanceof HttpResponse response) {
1202+
headers = response.headers();
11441203
serverHeader = response.headers().get("Server");
11451204
} else if (msg instanceof HttpContent content) {
11461205
body.append(content.content().toString(CharsetUtil.UTF_8));
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0123456789012345678901234567890123456789

docs/user-manual/web-server.adoc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,15 @@ The location to redirect the requests with the root target.
3434
webContentEnabled::
3535
Whether or not the content included in the web folder of the home and the instance directories is accessible.
3636
Default is `false`.
37+
compressionEnabled::
38+
Whether to compress HTTP responses.
39+
Uses `gzip` encoding for maximum compatibility.
40+
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.
41+
Default is `false`.
42+
compressionLevel::
43+
The level of compression for HTTP responses.
44+
Only valid if `compressionEnabled` is `true`.
45+
Default is `6`. Must be between `0` and `9` inclusive.
3746
maxThreads::
3847
The maximum number of threads the embedded web server can create to service HTTP requests.
3948
Default is `200`.

0 commit comments

Comments
 (0)