From ff92b7e628bd398011aaae0f3c7b1aa148ea2bc4 Mon Sep 17 00:00:00 2001 From: jansupol Date: Wed, 24 Jul 2024 17:57:05 +0200 Subject: [PATCH] Allow ChunkedInput#close to close the underlying stream Signed-off-by: jansupol --- .../jersey/client/ChunkedInputReader.java | 5 +- .../e2e/entity/ChunkedInputReaderTest.java | 100 ++++++++++++++++++ 2 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/ChunkedInputReaderTest.java diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ChunkedInputReader.java b/core-client/src/main/java/org/glassfish/jersey/client/ChunkedInputReader.java index 4a27f23b75..1d2e2038fd 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/ChunkedInputReader.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/ChunkedInputReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -35,6 +35,7 @@ import org.glassfish.jersey.internal.PropertiesDelegate; import org.glassfish.jersey.internal.util.ReflectionHelper; import org.glassfish.jersey.message.MessageBodyWorkers; +import org.glassfish.jersey.message.internal.ReaderInterceptorExecutor; /** * {@link javax.ws.rs.ext.MessageBodyWriter} for {@link ChunkedInput}. @@ -71,7 +72,7 @@ public ChunkedInput readFrom(Class chunkedInputClass, return new ChunkedInput( chunkType, - inputStream, + ReaderInterceptorExecutor.closeableInputStream(inputStream), annotations, mediaType, headers, diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/ChunkedInputReaderTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/ChunkedInputReaderTest.java new file mode 100644 index 0000000000..b2b6936c50 --- /dev/null +++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/ChunkedInputReaderTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + + +package org.glassfish.jersey.tests.e2e.entity; + +import org.glassfish.jersey.client.ChunkedInput; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.ClientRequestContext; +import javax.ws.rs.client.ClientRequestFilter; +import javax.ws.rs.core.Application; +import javax.ws.rs.core.GenericType; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ReaderInterceptor; +import javax.ws.rs.ext.ReaderInterceptorContext; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.concurrent.atomic.AtomicBoolean; + +public class ChunkedInputReaderTest extends JerseyTest { + + @Path("/") + public static class ChunkedInputReaderTestResource { + @GET + public String get() { + return "To_be_replaced_by_client_reader"; + } + } + + @Override + protected Application configure() { + return new ResourceConfig(ChunkedInputReaderTestResource.class); + } + + @Test + public void testChunkedInputStreamIsClosed() { + AtomicBoolean closed = new AtomicBoolean(false); + InputStream inputStream = new ByteArrayInputStream("TEST".getBytes()) { + @Override + public void close() throws IOException { + closed.set(true); + super.close(); + } + }; + + final GenericType> chunkedInputGenericType = new GenericType(new ParameterizedType() { + @Override + public Type[] getActualTypeArguments() { + return new Type[]{String.class}; + } + + @Override + public Type getRawType() { + return ChunkedInput.class; + } + + @Override + public Type getOwnerType() { + return ChunkedInput.class; + } + }); + + + ChunkedInput response = target().register(new ReaderInterceptor() { + @Override + public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException { + context.setInputStream(inputStream); + return context.proceed(); + }; + }).request().get(chunkedInputGenericType); + MatcherAssert.assertThat(response.read(), Matchers.is("TEST")); + response.close(); + MatcherAssert.assertThat(closed.get(), Matchers.is(true)); + } +}