diff --git a/docs/zipper.jpg b/docs/zipper.jpg
new file mode 100644
index 000000000..e83bfa447
Binary files /dev/null and b/docs/zipper.jpg differ
diff --git a/pom.xml b/pom.xml
index fed4992d9..cb4cbae02 100644
--- a/pom.xml
+++ b/pom.xml
@@ -54,6 +54,7 @@
riptide-compatibility
riptide-core
riptide-chaos
+ riptide-compression
riptide-failsafe
riptide-faults
riptide-httpclient
@@ -104,6 +105,11 @@
riptide-compatibility
${project.version}
+
+ org.zalando
+ riptide-compression
+ ${project.version}
+
org.zalando
riptide-core
@@ -607,7 +613,6 @@
-Xlint:unchecked
- -Werror
-parameters
diff --git a/riptide-compression/README.md b/riptide-compression/README.md
new file mode 100644
index 000000000..c94bd81a5
--- /dev/null
+++ b/riptide-compression/README.md
@@ -0,0 +1,81 @@
+# Riptide: Compression
+
+[![Zipper](../docs/zipper.jpg)](https://pixabay.com/photos/zipper-metal-gold-color-brass-201684/)
+
+[![Build Status](https://img.shields.io/travis/zalando/riptide.svg)](https://travis-ci.org/zalando/riptide)
+[![Coverage Status](https://img.shields.io/coveralls/zalando/riptide.svg)](https://coveralls.io/r/zalando/riptide)
+[![Code Quality](https://img.shields.io/codacy/grade/1fbe3d16ca544c0c8589692632d114de/master.svg)](https://www.codacy.com/app/whiskeysierra/riptide)
+[![Javadoc](https://www.javadoc.io/badge/org.zalando/riptide-compression.svg)](http://www.javadoc.io/doc/org.zalando/riptide-compression)
+[![Release](https://img.shields.io/github/release/zalando/riptide.svg)](https://github.com/zalando/riptide/releases)
+[![Maven Central](https://img.shields.io/maven-central/v/org.zalando/riptide-compression.svg)](https://maven-badges.herokuapp.com/maven-central/org.zalando/riptide-compression)
+[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/zalando/riptide/master/LICENSE)
+
+*Riptide: Compression* adds support to compress request bodies.
+
+## Features
+
+- pluggable compression mechanism
+- out of the box GZIP support
+
+## Dependencies
+
+- Java 8
+- Riptide: Core
+
+## Installation
+
+Add the following dependency to your project:
+
+```xml
+
+ org.zalando
+ riptide-compression
+ ${riptide.version}
+
+```
+
+## Configuration
+
+```java
+Http.builder()
+ .plugin(new RequestCompressionPlugin())
+ .build();
+```
+
+By default request bodies are compressed using [GZIP](https://docs.oracle.com/javase/8/docs/api/java/util/zip/GZIPOutputStream.html).
+
+In order to specify the compression algorithm you can pass in a custom `Compression`:
+
+```java
+new RequestCompressionPlugin(Compression.of("br", BrotliOutputStream::new));
+```
+
+## Usage
+
+```java
+http.post("/events")
+ .contentType(MediaType.APPLICATION_JSON)
+ .body(asList(events))
+ .call(pass())
+ .join();
+```
+
+All request bodies will be compressed using the configured compression method using `chunked` transfer-encoding.
+
+If there is already a `Content-Encoding` specified on the request, the plugin does nothing.
+
+### Limitations
+
+* You must only configure a single `RequestCompressionPlugin` as only a single encoding is applied currently.
+* Starting with Spring 4.3 the `Netty4ClientHttpRequestFactory` unconditionally adds a `Content-Length` header,
+which breaks if used together with `RequestCompressionPlugin`. Use `riptide-httpclient` instead.
+
+
+## Getting Help
+
+If you have questions, concerns, bug reports, etc., please file an issue in this repository's [Issue Tracker](../../../../issues).
+
+## Getting Involved/Contributing
+
+To contribute, simply make a pull request and add a brief description (1-2 sentences) of your addition or change. For
+more details, check the [contribution guidelines](../.github/CONTRIBUTING.md).
diff --git a/riptide-compression/pom.xml b/riptide-compression/pom.xml
new file mode 100644
index 000000000..a71fecbfa
--- /dev/null
+++ b/riptide-compression/pom.xml
@@ -0,0 +1,44 @@
+
+
+ 4.0.0
+
+
+ org.zalando
+ riptide-parent
+ 3.0.0-SNAPSHOT
+
+
+ riptide-compression
+
+ Riptide: Request Compression
+ Client side response routing with stream support
+
+
+
+ org.zalando
+ riptide-core
+
+
+ org.zalando
+ riptide-httpclient
+ test
+
+
+ io.netty
+ netty-all
+ 4.1.43.Final
+ test
+
+
+ org.springframework
+ spring-test
+
+
+ com.github.rest-driver
+ rest-client-driver
+
+
+
+
diff --git a/riptide-compression/src/main/java/org/zalando/riptide/compression/Compression.java b/riptide-compression/src/main/java/org/zalando/riptide/compression/Compression.java
new file mode 100644
index 000000000..01fda3ade
--- /dev/null
+++ b/riptide-compression/src/main/java/org/zalando/riptide/compression/Compression.java
@@ -0,0 +1,19 @@
+package org.zalando.riptide.compression;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.apiguardian.api.API;
+import org.zalando.fauxpas.ThrowingUnaryOperator;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import static org.apiguardian.api.API.Status.EXPERIMENTAL;
+
+@API(status = EXPERIMENTAL)
+@AllArgsConstructor(staticName = "of")
+@Getter
+public final class Compression {
+ private final String contentEncoding;
+ private final ThrowingUnaryOperator outputStreamDecorator;
+}
diff --git a/riptide-compression/src/main/java/org/zalando/riptide/compression/RequestCompressionPlugin.java b/riptide-compression/src/main/java/org/zalando/riptide/compression/RequestCompressionPlugin.java
new file mode 100644
index 000000000..31b50bc2a
--- /dev/null
+++ b/riptide-compression/src/main/java/org/zalando/riptide/compression/RequestCompressionPlugin.java
@@ -0,0 +1,88 @@
+package org.zalando.riptide.compression;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.apiguardian.api.API;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpOutputMessage;
+import org.springframework.http.StreamingHttpOutputMessage;
+import org.zalando.riptide.Plugin;
+import org.zalando.riptide.RequestArguments.Entity;
+import org.zalando.riptide.RequestExecution;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.zip.GZIPOutputStream;
+
+import static org.apiguardian.api.API.Status.EXPERIMENTAL;
+import static org.springframework.http.HttpHeaders.CONTENT_ENCODING;
+import static org.springframework.http.HttpHeaders.TRANSFER_ENCODING;
+
+@API(status = EXPERIMENTAL)
+public final class RequestCompressionPlugin implements Plugin {
+
+ private final Compression compression;
+
+ public RequestCompressionPlugin(final Compression compression) {
+ this.compression = compression;
+ }
+
+ public RequestCompressionPlugin() {
+ this(Compression.of("gzip", GZIPOutputStream::new));
+ }
+
+ @Override
+ public RequestExecution aroundNetwork(final RequestExecution execution) {
+ return arguments -> {
+ final Entity entity = arguments.getEntity();
+
+ if (entity.isEmpty() || arguments.getHeaders().containsKey(CONTENT_ENCODING)) {
+ return execution.execute(arguments);
+ }
+
+ return execution.execute(
+ arguments.withEntity(new CompressingEntity(compression, entity)));
+ };
+ }
+
+ @AllArgsConstructor
+ private static class CompressingEntity implements Entity {
+
+ private final Compression compression;
+ private final Entity entity;
+
+ @Override
+ public void writeTo(final HttpOutputMessage message) throws IOException {
+ update(message.getHeaders());
+
+ if (message instanceof StreamingHttpOutputMessage) {
+ final StreamingHttpOutputMessage streaming = (StreamingHttpOutputMessage) message;
+ streaming.setBody(stream ->
+ writeToCompressing(new DelegatingHttpOutputMessage(message.getHeaders(), stream)));
+ } else {
+ writeToCompressing(message);
+ }
+ }
+
+ private void writeToCompressing(HttpOutputMessage message) throws IOException {
+ try (final WrappingHttpOutputMessage compressingMessage =
+ new WrappingHttpOutputMessage(message, compression.getOutputStreamDecorator())) {
+ entity.writeTo(compressingMessage);
+ }
+ }
+
+ private void update(final HttpHeaders headers) {
+ headers.set(CONTENT_ENCODING, compression.getContentEncoding());
+ headers.set(TRANSFER_ENCODING, "chunked");
+ }
+
+ }
+
+ @AllArgsConstructor
+ @Getter
+ private static final class DelegatingHttpOutputMessage implements HttpOutputMessage {
+ private final HttpHeaders headers;
+ private final OutputStream body;
+ }
+
+}
diff --git a/riptide-compression/src/main/java/org/zalando/riptide/compression/WrappingHttpOutputMessage.java b/riptide-compression/src/main/java/org/zalando/riptide/compression/WrappingHttpOutputMessage.java
new file mode 100644
index 000000000..c3295e23b
--- /dev/null
+++ b/riptide-compression/src/main/java/org/zalando/riptide/compression/WrappingHttpOutputMessage.java
@@ -0,0 +1,44 @@
+package org.zalando.riptide.compression;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpOutputMessage;
+import org.zalando.fauxpas.ThrowingUnaryOperator;
+
+import javax.annotation.Nonnull;
+import java.io.IOException;
+import java.io.OutputStream;
+
+final class WrappingHttpOutputMessage implements HttpOutputMessage, AutoCloseable {
+
+ private final HttpOutputMessage message;
+ private final ThrowingUnaryOperator wrapper;
+ private OutputStream stream;
+
+ WrappingHttpOutputMessage(HttpOutputMessage message, ThrowingUnaryOperator wrapper) {
+ this.message = message;
+ this.wrapper = wrapper;
+ }
+
+ @Nonnull
+ @Override
+ public OutputStream getBody() throws IOException {
+ if (stream == null) {
+ stream = wrapper.apply(message.getBody());
+ }
+ return stream;
+ }
+
+ @Nonnull
+ @Override
+ public HttpHeaders getHeaders() {
+ return message.getHeaders();
+ }
+
+ @Override
+ public void close() throws IOException {
+ // make sure any underlying compressor gets flushed
+ if (stream != null) {
+ stream.close();
+ }
+ }
+}
diff --git a/riptide-compression/src/main/java/org/zalando/riptide/compression/package-info.java b/riptide-compression/src/main/java/org/zalando/riptide/compression/package-info.java
new file mode 100644
index 000000000..7870a37f8
--- /dev/null
+++ b/riptide-compression/src/main/java/org/zalando/riptide/compression/package-info.java
@@ -0,0 +1,4 @@
+@ParametersAreNonnullByDefault
+package org.zalando.riptide.compression;
+
+import javax.annotation.ParametersAreNonnullByDefault;
\ No newline at end of file
diff --git a/riptide-compression/src/test/java/org/zalando/riptide/compression/GzipClientDriver.java b/riptide-compression/src/test/java/org/zalando/riptide/compression/GzipClientDriver.java
new file mode 100644
index 000000000..0d19cfa87
--- /dev/null
+++ b/riptide-compression/src/test/java/org/zalando/riptide/compression/GzipClientDriver.java
@@ -0,0 +1,58 @@
+package org.zalando.riptide.compression;
+
+import com.github.restdriver.clientdriver.ClientDriver;
+import com.github.restdriver.clientdriver.DefaultRequestMatcher;
+import com.github.restdriver.clientdriver.exception.ClientDriverSetupException;
+import com.github.restdriver.clientdriver.jetty.ClientDriverJettyHandler;
+import com.github.restdriver.clientdriver.jetty.DefaultClientDriverJettyHandler;
+import com.google.gag.annotation.remark.Hack;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.gzip.GzipHandler;
+
+@Hack
+final class GzipClientDriver extends ClientDriver {
+
+ private int port;
+
+ static ClientDriver create() {
+ return new GzipClientDriver(new DefaultClientDriverJettyHandler(new DefaultRequestMatcher()));
+ }
+
+ private GzipClientDriver(ClientDriverJettyHandler handler) {
+ super(handler);
+ }
+
+ @Override
+ protected Server createAndStartJetty(int port) {
+ final Server jetty = new Server();
+ jetty.setHandler(handler);
+ final GzipHandler gzip = new GzipHandler();
+ gzip.setInflateBufferSize(1024);
+ jetty.insertHandler(gzip);
+ final ServerConnector connector = createConnector(jetty, port);
+ jetty.addConnector(connector);
+ try {
+ jetty.start();
+ } catch (Exception e) {
+ throw new ClientDriverSetupException("Error starting jetty on port " + port, e);
+ }
+ this.port = connector.getLocalPort();
+ return jetty;
+ }
+
+ @Override
+ public int getPort() {
+ return port;
+ }
+
+ @Override
+ public String getBaseUrl() {
+ return "http://localhost:" + port;
+ }
+
+ @Override
+ protected void replaceConnector(ServerConnector newConnector, Server jetty) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/riptide-compression/src/test/java/org/zalando/riptide/compression/RequestCompressionPluginTest.java b/riptide-compression/src/test/java/org/zalando/riptide/compression/RequestCompressionPluginTest.java
new file mode 100644
index 000000000..d91ac2e29
--- /dev/null
+++ b/riptide-compression/src/test/java/org/zalando/riptide/compression/RequestCompressionPluginTest.java
@@ -0,0 +1,140 @@
+package org.zalando.riptide.compression;
+
+import com.github.restdriver.clientdriver.ClientDriver;
+import org.apache.http.impl.client.HttpClients;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.ArgumentsProvider;
+import org.junit.jupiter.params.provider.ArgumentsSource;
+import org.springframework.http.MediaType;
+import org.springframework.http.client.BufferingClientHttpRequestFactory;
+import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.http.client.SimpleClientHttpRequestFactory;
+import org.zalando.riptide.Http;
+import org.zalando.riptide.Plugin;
+import org.zalando.riptide.httpclient.ApacheClientHttpRequestFactory;
+import org.zalando.riptide.httpclient.ApacheClientHttpRequestFactory.Mode;
+
+import java.util.HashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
+
+import static com.github.restdriver.clientdriver.ClientDriverRequest.Method.POST;
+import static com.github.restdriver.clientdriver.RestClientDriver.giveResponse;
+import static com.github.restdriver.clientdriver.RestClientDriver.onRequestTo;
+import static java.util.Arrays.asList;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.hamcrest.Matchers.emptyString;
+import static org.hamcrest.Matchers.equalTo;
+import static org.springframework.http.HttpHeaders.CONTENT_ENCODING;
+import static org.zalando.riptide.PassRoute.pass;
+
+class RequestCompressionPluginTest {
+
+ private final ClientDriver driver = GzipClientDriver.create();
+ private final ExecutorService executor = newSingleThreadExecutor();
+
+ @AfterEach
+ void tearDown() throws Exception {
+ executor.shutdown();
+ executor.awaitTermination(10, TimeUnit.SECONDS);
+ driver.verify();
+ }
+
+ @ParameterizedTest
+ @ArgumentsSource(RequestFactorySource.class)
+ void shouldCompressRequestBody(final ClientHttpRequestFactory factory) {
+ driver.addExpectation(onRequestTo("/")
+ .withMethod(POST)
+ .withHeader("X-Content-Encoding", "gzip") // written by Jetty's GzipHandler
+ .withBody(equalTo("{}"), "application/json"),
+ giveResponse("", "text/plain"));
+
+ final Http http = buildHttp(factory, new RequestCompressionPlugin());
+ http.post("/")
+ .contentType(MediaType.APPLICATION_JSON)
+ .body(new HashMap<>())
+ .call(pass())
+ .join();
+ }
+
+ @ParameterizedTest
+ @ArgumentsSource(RequestFactorySource.class)
+ void shouldNotCompressEmptyRequestBody(final ClientHttpRequestFactory factory) {
+ driver.addExpectation(onRequestTo("/")
+ .withMethod(POST)
+ .withBody(emptyString(), "application/json")
+ .withoutHeader("Content-Encoding")
+ .withoutHeader("X-Content-Encoding"),
+ giveResponse("", "text/plain"));
+
+ final Http http = buildHttp(factory, new RequestCompressionPlugin());
+ http.post("/")
+ .contentType(MediaType.APPLICATION_JSON)
+ .call(pass())
+ .join();
+ }
+
+ @ParameterizedTest
+ @ArgumentsSource(RequestFactorySource.class)
+ void shouldCompressWithGivenAlgorithm(final ClientHttpRequestFactory factory) {
+ driver.addExpectation(onRequestTo("/")
+ .withMethod(POST)
+ .withHeader("Content-Encoding", "identity") // not handled by Jetty
+ .withoutHeader("X-Content-Encoding")
+ .withBody(equalTo("{}"), "application/json"),
+ giveResponse("", "text/plain"));
+
+ final Http http = buildHttp(factory, new RequestCompressionPlugin(Compression.of("identity", it -> it)));
+ http.post("/")
+ .contentType(MediaType.APPLICATION_JSON)
+ .body(new HashMap<>())
+ .call(pass())
+ .join();
+ }
+
+ @ParameterizedTest
+ @ArgumentsSource(RequestFactorySource.class)
+ void shouldBackOffIfAlreadyEncoded(final ClientHttpRequestFactory factory) {
+ driver.addExpectation(onRequestTo("/")
+ .withMethod(POST)
+ .withHeader("Content-Encoding", "custom") // not handled by Jetty
+ .withoutHeader("X-Content-Encoding")
+ .withBody(equalTo("{}"), "application/json"),
+ giveResponse("", "text/plain"));
+
+ final Http http = buildHttp(factory, new RequestCompressionPlugin());
+ http.post("/")
+ .header(CONTENT_ENCODING, "custom")
+ .contentType(MediaType.APPLICATION_JSON)
+ .body(new HashMap<>())
+ .call(pass())
+ .join();
+ }
+
+ private Http buildHttp(final ClientHttpRequestFactory factory, final Plugin... plugins) {
+ return Http.builder()
+ .executor(executor)
+ .requestFactory(factory)
+ .baseUrl(driver.getBaseUrl())
+ .plugins(asList(plugins))
+ .build();
+ }
+
+ static class RequestFactorySource implements ArgumentsProvider {
+ @Override
+ public Stream extends Arguments> provideArguments(ExtensionContext extensionContext) {
+ return Stream.of(
+ new SimpleClientHttpRequestFactory(),
+ // new Netty4ClientHttpRequestFactory(), # broken, see #823
+ new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()),
+ new ApacheClientHttpRequestFactory(HttpClients.createDefault(), Mode.BUFFERING),
+ new ApacheClientHttpRequestFactory(HttpClients.createDefault(), Mode.STREAMING)
+ ).map(Arguments::of);
+ }
+ }
+
+}
diff --git a/riptide-compression/src/test/java/org/zalando/riptide/compression/WrappingHttpOutputMessageTest.java b/riptide-compression/src/test/java/org/zalando/riptide/compression/WrappingHttpOutputMessageTest.java
new file mode 100644
index 000000000..4c42e6d98
--- /dev/null
+++ b/riptide-compression/src/test/java/org/zalando/riptide/compression/WrappingHttpOutputMessageTest.java
@@ -0,0 +1,72 @@
+package org.zalando.riptide.compression;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpOutputMessage;
+import org.zalando.fauxpas.ThrowingUnaryOperator;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+class WrappingHttpOutputMessageTest {
+
+ private final HttpOutputMessage message = mock(HttpOutputMessage.class);
+
+ @SuppressWarnings("unchecked")
+ private final ThrowingUnaryOperator wrapper = mock(ThrowingUnaryOperator.class);
+
+ private final WrappingHttpOutputMessage unit = new WrappingHttpOutputMessage(message, wrapper);
+
+ @BeforeEach
+ void setUp() throws IOException {
+ final OutputStream body = mock(OutputStream.class);
+ when(message.getBody()).thenReturn(body);
+ when(message.getHeaders()).thenReturn(new HttpHeaders());
+ }
+
+ @Test
+ void shouldDelegateHeaders() {
+ assertThat(unit.getHeaders(), equalTo(message.getHeaders()));
+ }
+
+ @Test
+ void shouldReturnWrapped() throws IOException {
+ final OutputStream wrappedStream = mock(OutputStream.class);
+ when(wrapper.apply(any())).thenReturn(wrappedStream);
+
+ assertThat(unit.getBody(), equalTo(wrappedStream));
+ assertThat(unit.getBody(), equalTo(wrappedStream));
+ }
+
+ @Test
+ void shouldWrapOnFirstAccessOnly() throws IOException {
+ final OutputStream wrappedStream = mock(OutputStream.class);
+ when(wrapper.apply(any())).thenReturn(wrappedStream);
+
+ unit.getBody();
+ unit.getBody();
+
+ verify(wrapper).apply(message.getBody());
+ verifyNoMoreInteractions(wrapper);
+ }
+
+ @Test
+ void shouldNotCloseStreamIfNeverAccessed() throws IOException {
+ final OutputStream wrappedStream = mock(OutputStream.class);
+ when(wrapper.apply(any())).thenReturn(wrappedStream);
+
+ unit.close();
+
+ verifyNoInteractions(wrappedStream);
+ }
+}
diff --git a/riptide-core/src/main/java/org/zalando/riptide/RequestCompressionPlugin.java b/riptide-core/src/main/java/org/zalando/riptide/RequestCompressionPlugin.java
deleted file mode 100644
index fdc6026fb..000000000
--- a/riptide-core/src/main/java/org/zalando/riptide/RequestCompressionPlugin.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package org.zalando.riptide;
-
-import lombok.AllArgsConstructor;
-import org.apiguardian.api.API;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpOutputMessage;
-import org.zalando.riptide.RequestArguments.Entity;
-
-import javax.annotation.Nonnull;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.zip.GZIPOutputStream;
-
-import static org.apiguardian.api.API.Status.EXPERIMENTAL;
-
-@API(status = EXPERIMENTAL)
-public final class RequestCompressionPlugin implements Plugin {
-
- @Override
- public RequestExecution aroundNetwork(final RequestExecution execution) {
- return arguments -> {
- final Entity entity = arguments.getEntity();
-
- if (entity.isEmpty()) {
- return execution.execute(arguments);
- }
-
- return execution.execute(
- arguments.withEntity(new GzipEntity(entity)));
- };
- }
-
- @AllArgsConstructor
- private static class GzipEntity implements Entity {
-
- private final Entity entity;
-
- @Override
- public void writeTo(final HttpOutputMessage message) throws IOException {
- entity.writeTo(new GzipHttpOutputMessage(message));
- update(message.getHeaders());
- }
-
- private void update(final HttpHeaders headers) {
- headers.set("Content-Encoding", "gzip");
- headers.set("Transfer-Encoding", "chunked");
- }
-
- }
-
- @AllArgsConstructor
- private static final class GzipHttpOutputMessage implements HttpOutputMessage {
-
- private final HttpOutputMessage message;
-
- @Nonnull
- @Override
- public OutputStream getBody() throws IOException {
- return new GZIPOutputStream(message.getBody());
- }
-
- @Nonnull
- @Override
- public HttpHeaders getHeaders() {
- return message.getHeaders();
- }
-
- }
-
-}
diff --git a/riptide-core/src/test/java/org/zalando/riptide/RequestCompressionPluginTest.java b/riptide-core/src/test/java/org/zalando/riptide/RequestCompressionPluginTest.java
deleted file mode 100644
index f06330433..000000000
--- a/riptide-core/src/test/java/org/zalando/riptide/RequestCompressionPluginTest.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package org.zalando.riptide;
-
-import com.github.restdriver.clientdriver.ClientDriver;
-import com.github.restdriver.clientdriver.ClientDriverFactory;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Test;
-import org.springframework.http.MediaType;
-import org.springframework.http.client.SimpleClientHttpRequestFactory;
-
-import java.util.HashMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.TimeUnit;
-
-import static com.github.restdriver.clientdriver.ClientDriverRequest.Method.POST;
-import static com.github.restdriver.clientdriver.RestClientDriver.giveResponse;
-import static com.github.restdriver.clientdriver.RestClientDriver.onRequestTo;
-import static java.util.concurrent.Executors.newSingleThreadExecutor;
-import static org.hamcrest.Matchers.emptyString;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.not;
-import static org.zalando.riptide.PassRoute.pass;
-
-final class RequestCompressionPluginTest {
-
- private final ClientDriver driver = new ClientDriverFactory().createClientDriver();
- private final ExecutorService executor = newSingleThreadExecutor();
-
- private final Http unit = Http.builder()
- .executor(executor)
- .requestFactory(new SimpleClientHttpRequestFactory())
- .baseUrl(driver.getBaseUrl())
- .plugin(new RequestCompressionPlugin())
- .build();
-
- @AfterEach
- void tearDown() throws Exception {
- executor.shutdown();
- executor.awaitTermination(10, TimeUnit.SECONDS);
- driver.verify();
- }
-
- @Test
- void shouldNotCompressEmptyBody() {
- driver.addExpectation(onRequestTo("/")
- .withBody(emptyString(), "text/plain")
- .withoutHeader("Content-Encoding"),
- giveResponse("", "text/plain"));
-
- unit.get("/")
- .contentType(MediaType.TEXT_PLAIN)
- .call(pass())
- .join();
- }
-
- @Test
- void shouldCompressNonEmptyBody() {
- driver.addExpectation(onRequestTo("/")
- .withMethod(POST)
- .withBody(not(equalTo("{}")), "application/json")
- .withHeader("Content-Encoding", "gzip"),
- giveResponse("", "text/plain"));
-
- unit.post("/")
- .body(new HashMap<>())
- .call(pass())
- .join();
- }
-
-}
diff --git a/riptide-logbook/pom.xml b/riptide-logbook/pom.xml
index 6644538e1..b9cfd1191 100644
--- a/riptide-logbook/pom.xml
+++ b/riptide-logbook/pom.xml
@@ -42,6 +42,11 @@
riptide-httpclient
test
+
+ org.zalando
+ riptide-compression
+ test
+
org.zalando
logbook-json
diff --git a/riptide-logbook/src/test/java/org/zalando/riptide/logbook/LogbookPluginTest.java b/riptide-logbook/src/test/java/org/zalando/riptide/logbook/LogbookPluginTest.java
index 8c8d3169f..d24144961 100644
--- a/riptide-logbook/src/test/java/org/zalando/riptide/logbook/LogbookPluginTest.java
+++ b/riptide-logbook/src/test/java/org/zalando/riptide/logbook/LogbookPluginTest.java
@@ -16,7 +16,7 @@
import org.zalando.logbook.Precorrelation;
import org.zalando.logbook.json.JsonHttpLogFormatter;
import org.zalando.riptide.Http;
-import org.zalando.riptide.RequestCompressionPlugin;
+import org.zalando.riptide.compression.RequestCompressionPlugin;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;
diff --git a/riptide-spring-boot-autoconfigure/README.md b/riptide-spring-boot-autoconfigure/README.md
index 8af18efe0..1c8036d71 100644
--- a/riptide-spring-boot-autoconfigure/README.md
+++ b/riptide-spring-boot-autoconfigure/README.md
@@ -80,6 +80,7 @@ private Http example;
- Riptide
- Core
- (Apache) HTTP Client
+ - Compression
- Backup (optional)
- Failsafe (optional)
- Faults (optional)
diff --git a/riptide-spring-boot-autoconfigure/pom.xml b/riptide-spring-boot-autoconfigure/pom.xml
index 6e6f53281..80cd54524 100644
--- a/riptide-spring-boot-autoconfigure/pom.xml
+++ b/riptide-spring-boot-autoconfigure/pom.xml
@@ -65,6 +65,10 @@
org.zalando
riptide-compatibility
+
+ org.zalando
+ riptide-compression
+
org.zalando
riptide-core
diff --git a/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/DefaultRiptideRegistrar.java b/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/DefaultRiptideRegistrar.java
index 7b25a5a0a..515a7ba2d 100644
--- a/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/DefaultRiptideRegistrar.java
+++ b/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/DefaultRiptideRegistrar.java
@@ -27,7 +27,6 @@
import org.zalando.riptide.Http;
import org.zalando.riptide.OriginalStackTracePlugin;
import org.zalando.riptide.Plugin;
-import org.zalando.riptide.RequestCompressionPlugin;
import org.zalando.riptide.auth.AuthorizationPlugin;
import org.zalando.riptide.auth.AuthorizationProvider;
import org.zalando.riptide.auth.PlatformCredentialsAuthorizationProvider;
@@ -41,6 +40,7 @@
import org.zalando.riptide.chaos.Probability;
import org.zalando.riptide.compatibility.AsyncHttpOperations;
import org.zalando.riptide.compatibility.HttpOperations;
+import org.zalando.riptide.compression.RequestCompressionPlugin;
import org.zalando.riptide.failsafe.BackupRequest;
import org.zalando.riptide.failsafe.CircuitBreakerListener;
import org.zalando.riptide.failsafe.FailsafePlugin;
diff --git a/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/ManualConfiguration.java b/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/ManualConfiguration.java
index 4da0c4cd2..11ccc6669 100644
--- a/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/ManualConfiguration.java
+++ b/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/ManualConfiguration.java
@@ -32,7 +32,6 @@
import org.zalando.riptide.Http;
import org.zalando.riptide.OriginalStackTracePlugin;
import org.zalando.riptide.Plugin;
-import org.zalando.riptide.RequestCompressionPlugin;
import org.zalando.riptide.UrlResolution;
import org.zalando.riptide.auth.AuthorizationPlugin;
import org.zalando.riptide.auth.PlatformCredentialsAuthorizationProvider;
@@ -44,6 +43,7 @@
import org.zalando.riptide.chaos.Probability;
import org.zalando.riptide.compatibility.AsyncHttpOperations;
import org.zalando.riptide.compatibility.HttpOperations;
+import org.zalando.riptide.compression.RequestCompressionPlugin;
import org.zalando.riptide.failsafe.BackupRequest;
import org.zalando.riptide.failsafe.CircuitBreakerListener;
import org.zalando.riptide.failsafe.CompositeDelayFunction;