diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ResponseEntityProxy.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ResponseEntityProxy.java index 23a720ecb..4c7612c71 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ResponseEntityProxy.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ResponseEntityProxy.java @@ -94,9 +94,7 @@ class ResponseEntityProxy extends HttpEntityWrapper implements EofSensorWatcher @Override public void writeTo(final OutputStream outStream) throws IOException { try { - if (outStream != null) { - super.writeTo(outStream); - } + super.writeTo(outStream != null ? outStream : NullOutputStream.INSTANCE); releaseConnection(); } catch (final IOException | RuntimeException ex) { discardConnection(); @@ -188,4 +186,43 @@ class ResponseEntityProxy extends HttpEntityWrapper implements EofSensorWatcher cleanup(); } } + + private static final class NullOutputStream extends OutputStream { + private static final NullOutputStream INSTANCE = new NullOutputStream(); + + private NullOutputStream() {} + + @Override + public void write(@SuppressWarnings("unused") final int byteValue) { + // no-op + } + + @Override + public void write(@SuppressWarnings("unused") final byte[] buffer) { + // no-op + } + + @Override + public void write( + @SuppressWarnings("unused") final byte[] buffer, + @SuppressWarnings("unused") final int off, + @SuppressWarnings("unused") final int len) { + // no-op + } + + @Override + public void flush() { + // no-op + } + + @Override + public void close() { + // no-op + } + + @Override + public String toString() { + return "NullOutputStream{}"; + } + } } diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestResponseEntityProxy.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestResponseEntityProxy.java index 7a5cd7c7e..7bd7c4a62 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestResponseEntityProxy.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestResponseEntityProxy.java @@ -39,6 +39,8 @@ import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.impl.io.ChunkedInputStream; import org.apache.hc.core5.http.impl.io.SessionInputBufferImpl; import org.apache.hc.core5.http.io.SessionInputBuffer; +import org.apache.hc.core5.http.io.entity.BasicHttpEntity; +import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -105,4 +107,32 @@ public class TestResponseEntityProxy { Assertions.assertEquals("X-Test-Trailer-Header", header.getName()); Assertions.assertEquals("test", header.getValue()); } + + @Test + public void testWriteToNullDrainsAndReleasesStream() throws Exception { + final SessionInputBuffer sessionInputBuffer = new SessionInputBufferImpl(100); + final ByteArrayInputStream inputStream = new ByteArrayInputStream("0\r\nX-Test-Trailer-Header: test\r\n".getBytes()); + final ChunkedInputStream chunkedInputStream = new ChunkedInputStream(sessionInputBuffer, inputStream); + final CloseableHttpResponse resp = new CloseableHttpResponse(new BasicClassicHttpResponse(200), execRuntime); + final HttpEntity entity = new BasicHttpEntity(chunkedInputStream, null, true); + Assertions.assertTrue(entity.isStreaming()); + resp.setEntity(entity); + + ResponseEntityProxy.enhance(resp, execRuntime); + + final HttpEntity wrappedEntity = resp.getEntity(); + + wrappedEntity.writeTo(null); + Mockito.verify(execRuntime).releaseEndpoint(); + + final Supplier> trailers = wrappedEntity.getTrailers(); + final List headers = trailers.get(); + + Assertions.assertEquals(1, headers.size()); + final Header header = headers.get(0); + Assertions.assertEquals("X-Test-Trailer-Header", header.getName()); + Assertions.assertEquals("test", header.getValue()); + + + } } \ No newline at end of file