Skip to content

Commit

Permalink
Merge pull request #32684 from Sgitario/26981
Browse files Browse the repository at this point in the history
Support decoding of messages GZIP-compressed in REST Client Reactive
  • Loading branch information
Sgitario authored Apr 18, 2023
2 parents ea7b14d + d270e1f commit 0ea2dd2
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 0 deletions.
3 changes: 3 additions & 0 deletions docs/src/main/asciidoc/rest-client-reactive.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1125,6 +1125,9 @@ The code uses the following pieces:

As previously mentioned, the body parameter needs to be properly crafted by the application code to conform to the service's requirements.

=== Receiving compressed messages
REST Client Reactive also supports receiving compressed messages using GZIP. You can enable the HTTP compression support by adding the property `quarkus.http.enable-compression=true`.
When this feature is enabled and a server returns a response that includes the header `Content-Encoding: gzip`, REST Client Reactive will automatically decode the content and proceed with the message handling.

== Proxy support
REST Client Reactive supports sending requests through a proxy.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package io.quarkus.rest.client.reactive.jackson.test;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

import org.eclipse.microprofile.rest.client.annotation.ClientHeaderParam;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.vertx.http.Compressed;

public class ClientUsingGzipCompressionTest {
@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(MyResource.class, Message.class, MyClient.class))
.withConfigurationResource("client-using-gzip-application.properties");

@RestClient
MyClient client;

@Test
public void testClientSupportCompressedMessagesWithGzip() {
Message actual = client.receiveCompressed();
Assertions.assertEquals(1, actual.id);
}

@Test
public void testClientStillWorksWhenMessageIsUncompressed() {
Message actual = client.receiveUncompressed();
Assertions.assertEquals(1, actual.id);
}

@Path("/client")
@RegisterRestClient(configKey = "my-client")
public interface MyClient {

// This header is used to reproduce the issue: it will force the server to produce the payload with gzip compression
@ClientHeaderParam(name = "Accept-Encoding", value = "gzip")
@GET
@Path("/message")
Message receiveCompressed();

@GET
@Path("/message")
Message receiveUncompressed();

}

@Path("/client")
public static class MyResource {

@Compressed
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/message")
public String receive() {
return "{\"id\": 1}";
}

}

public static class Message {
public int id;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
quarkus.http.enable-compression=true

quarkus.rest-client.my-client.url=http://localhost:${quarkus.http.test-port:8081}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public class RestClientBuilderImpl implements RestClientBuilder {

private static final String DEFAULT_MAPPER_DISABLED = "microprofile.rest.client.disable.default.mapper";
private static final String TLS_TRUST_ALL = "quarkus.tls.trust-all";
private static final String ENABLE_COMPRESSION = "quarkus.http.enable-compression";

private final ClientBuilderImpl clientBuilder = (ClientBuilderImpl) new ClientBuilderImpl()
.withConfig(new ConfigurationImpl(RuntimeType.CLIENT));
Expand Down Expand Up @@ -341,6 +342,12 @@ public <T> T build(Class<T> aClass) throws IllegalStateException, RestClientDefi
clientBuilder.http2((Boolean) getConfiguration().getProperty(QuarkusRestClientProperties.HTTP2));
}

Boolean enableCompression = ConfigProvider.getConfig()
.getOptionalValue(ENABLE_COMPRESSION, Boolean.class).orElse(false);
if (enableCompression) {
clientBuilder.enableCompression();
}

if (proxyHost != null) {
configureProxy(proxyHost, proxyPort, proxyUser, proxyPassword, nonProxyHosts);
} else if (restClientsConfig.proxyAddress.isPresent()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.jboss.logging.Logger;
import org.jboss.resteasy.reactive.client.api.ClientLogger;
import org.jboss.resteasy.reactive.client.api.LoggingScope;
import org.jboss.resteasy.reactive.client.interceptors.ClientGZIPDecodingInterceptor;
import org.jboss.resteasy.reactive.client.logging.DefaultClientLogger;
import org.jboss.resteasy.reactive.client.spi.ClientContextResolver;
import org.jboss.resteasy.reactive.common.jaxrs.ConfigurationImpl;
Expand Down Expand Up @@ -75,6 +76,8 @@ public class ClientBuilderImpl extends ClientBuilder {
private ClientLogger clientLogger = new DefaultClientLogger();
private String userAgent = "Resteasy Reactive Client";

private boolean enableCompression;

public ClientBuilderImpl() {
configuration = new ConfigurationImpl(RuntimeType.CLIENT);
}
Expand Down Expand Up @@ -188,6 +191,11 @@ public ClientBuilder clientLogger(ClientLogger clientLogger) {
return this;
}

public ClientBuilder enableCompression() {
this.enableCompression = true;
return this;
}

@Override
public ClientImpl build() {
HttpClientOptions options = Optional.ofNullable(configuration.getFromContext(HttpClientOptions.class))
Expand Down Expand Up @@ -273,6 +281,10 @@ public ClientImpl build() {
}
}

if (enableCompression) {
configuration.register(ClientGZIPDecodingInterceptor.class);
}

clientLogger.setBodySize(loggingBodySize);

return new ClientImpl(options,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.jboss.resteasy.reactive.client.interceptors;

import static jakarta.ws.rs.core.HttpHeaders.CONTENT_ENCODING;

import java.io.IOException;
import java.io.InputStream;
import java.util.zip.GZIPInputStream;

import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.ext.ReaderInterceptor;
import jakarta.ws.rs.ext.ReaderInterceptorContext;

/**
* Implementation based on {@see org.jboss.resteasy.plugins.interceptors.GZIPDecodingInterceptor}.
*/
public class ClientGZIPDecodingInterceptor implements ReaderInterceptor {

private static final String GZIP = "gzip";

@Override
public Object aroundReadFrom(ReaderInterceptorContext context)
throws IOException, WebApplicationException {
Object encoding = context.getHeaders().getFirst(CONTENT_ENCODING);
if (encoding != null && encoding.toString().equalsIgnoreCase(GZIP)) {
InputStream old = context.getInputStream();
GZIPInputStream is = new GZIPInputStream(old);
context.setInputStream(is);

Object response;
try {
response = context.proceed();
} finally {
context.setInputStream(old);
}

return response;
} else {
return context.proceed();
}
}
}

0 comments on commit 0ea2dd2

Please sign in to comment.