From f9d5cfb7918688ce83c8dc09b537b21e82649d8d Mon Sep 17 00:00:00 2001 From: Gerald Unterrainer Date: Mon, 28 Jun 2021 10:25:37 +0200 Subject: [PATCH] add gzip interceptor --- pom.xml | 7 +- .../commons/restclient/BaseBuilder.java | 13 +++ .../commons/restclient/GzipInterceptor.java | 92 +++++++++++++++++++ .../commons/restclient/Information.java | 7 ++ .../commons/restclient/RestClient.java | 1 + 5 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 src/main/java/info/unterrainer/commons/restclient/GzipInterceptor.java create mode 100644 src/main/java/info/unterrainer/commons/restclient/Information.java diff --git a/pom.xml b/pom.xml index 3ab675c..179e3f5 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ 4.0.0 rest-client - 0.0.9 + 0.0.10 RestClient jar @@ -33,5 +33,10 @@ okhttp 3.14.9 + + com.squareup.okio + okio + 1.17.2 + \ No newline at end of file diff --git a/src/main/java/info/unterrainer/commons/restclient/BaseBuilder.java b/src/main/java/info/unterrainer/commons/restclient/BaseBuilder.java index f21713e..2b0d6fe 100644 --- a/src/main/java/info/unterrainer/commons/restclient/BaseBuilder.java +++ b/src/main/java/info/unterrainer/commons/restclient/BaseBuilder.java @@ -41,6 +41,19 @@ protected enum Retry { protected Map headers = new HashMap<>(); protected Map parameters = new HashMap<>(); protected Retry retry = Retry.ONCE; + protected boolean isGZipped; + + /** + * Adds the header {@code "Content-Encoding": "gzip"} which triggers + * GZIP-compression within our RestClient. + * + * @return a {@link BaseBuilder} to provide a fluent interface. + */ + @SuppressWarnings("unchecked") + public R gzip() { + addHeader("Content-Encoding", "gzip"); + return (R) this; + } /** * Add a string to an URL. diff --git a/src/main/java/info/unterrainer/commons/restclient/GzipInterceptor.java b/src/main/java/info/unterrainer/commons/restclient/GzipInterceptor.java new file mode 100644 index 0000000..ed53626 --- /dev/null +++ b/src/main/java/info/unterrainer/commons/restclient/GzipInterceptor.java @@ -0,0 +1,92 @@ +package info.unterrainer.commons.restclient; + +import java.io.IOException; + +import okhttp3.Headers; +import okhttp3.Interceptor; +import okhttp3.MediaType; +import okhttp3.Request; +import okhttp3.Request.Builder; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okio.BufferedSink; +import okio.GzipSink; +import okio.GzipSource; +import okio.Okio; + +public class GzipInterceptor implements Interceptor { + @Override + public Response intercept(final Interceptor.Chain chain) throws IOException { + + Request originalRequest = chain.request(); + + if (acceptGzipAlreadySetAndNoContentToZip(originalRequest)) + return chain.proceed(originalRequest); + + Builder newRequestBuilder = originalRequest.newBuilder().addHeader("Accept-Encoding", "gzip"); + if (contentToZip(originalRequest)) + newRequestBuilder.method(originalRequest.method(), gzip(originalRequest.body())); + + // Make the call. + Response response = chain.proceed(newRequestBuilder.build()); + + if (isGzipped(response)) + return unzip(response); + else + return response; + } + + private boolean acceptGzipAlreadySetAndNoContentToZip(final Request originalRequest) { + return originalRequest.header("Accept-Encoding") == "gzip" + && (originalRequest.body() == null || originalRequest.header("Content-Encoding") != "gzip"); + } + + private RequestBody gzip(final RequestBody body) { + return new RequestBody() { + @Override + public MediaType contentType() { + return body.contentType(); + } + + @Override + public long contentLength() { + return -1; // We don't know the compressed length in advance! + } + + @Override + public void writeTo(final BufferedSink sink) throws IOException { + BufferedSink gzipSink = Okio.buffer(new GzipSink(sink)); + body.writeTo(gzipSink); + gzipSink.close(); + } + }; + } + + private Response unzip(final Response response) throws IOException { + + if (response.body() == null) + return response; + + GzipSource gzipSource = new GzipSource(response.body().source()); + String bodyString = Okio.buffer(gzipSource).readUtf8(); + + ResponseBody responseBody = ResponseBody.create(response.body().contentType(), bodyString); + + Headers strippedHeaders = response.headers() + .newBuilder() + .removeAll("Content-Encoding") + .removeAll("Content-Length") + .build(); + return response.newBuilder().headers(strippedHeaders).body(responseBody).message(response.message()).build(); + + } + + private Boolean isGzipped(final Response response) { + return response.header("Content-Encoding") != null && response.header("Content-Encoding").equals("gzip"); + } + + private boolean contentToZip(final Request originalRequest) { + return originalRequest.body() != null && originalRequest.header("Content-Encoding") == "gzip"; + } +} diff --git a/src/main/java/info/unterrainer/commons/restclient/Information.java b/src/main/java/info/unterrainer/commons/restclient/Information.java new file mode 100644 index 0000000..bf6bc15 --- /dev/null +++ b/src/main/java/info/unterrainer/commons/restclient/Information.java @@ -0,0 +1,7 @@ +package info.unterrainer.commons.restclient; + +public class Information { + public static final String name = "REST-Client"; + public static final String buildTime = "2021-06-28T07:57:53Z"; + public static final String pomVersion = "0.0.10"; +} diff --git a/src/main/java/info/unterrainer/commons/restclient/RestClient.java b/src/main/java/info/unterrainer/commons/restclient/RestClient.java index bad13e8..7e1bbcc 100644 --- a/src/main/java/info/unterrainer/commons/restclient/RestClient.java +++ b/src/main/java/info/unterrainer/commons/restclient/RestClient.java @@ -48,6 +48,7 @@ public RestClient(final JsonMapper jsonMapper, final String userName, final Stri .connectTimeout(connectTimeoutInMillis, TimeUnit.MILLISECONDS) .readTimeout(readTimeoutInMillis, TimeUnit.MILLISECONDS) .writeTimeout(writeTimeoutInMillis, TimeUnit.MILLISECONDS) + .addInterceptor(new GzipInterceptor()) .followRedirects(true); if (userName != null || password != null) c.authenticator((route, response) -> {