From 8ed271fea75bc3ca1ada7623c974d0ea5a3516fd Mon Sep 17 00:00:00 2001 From: jansupol Date: Thu, 4 May 2023 11:54:24 +0200 Subject: [PATCH] Workaround JNH InputStream.available() == 1 for no entity Signed-off-by: jansupol --- .../jnh/connector/JavaNetHttpConnector.java | 92 ++++++++++++++++++- .../connector/FirstByteCachingStreamTest.java | 81 ++++++++++++++++ .../jersey/jnh/connector/NoEntityTest.java | 34 +++++++ 3 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/FirstByteCachingStreamTest.java diff --git a/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnector.java b/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnector.java index 975f5d81b0..fb0f6131b8 100644 --- a/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnector.java +++ b/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnector.java @@ -55,6 +55,8 @@ import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Logger; /** @@ -253,7 +255,12 @@ private ClientResponse buildClientResponse(ClientRequest request, HttpResponse clazz : classes) { + if (clazz.getName().contains("FirstByteCachingStream")) { + Constructor constructor = clazz.getDeclaredConstructor(InputStream.class); + constructor.setAccessible(true); + return (InputStream) constructor.newInstance(inner); + } + } + throw new IllegalArgumentException("JavaNetHttpConnector inner class FirstByteCachingStream not found"); + } + + @Test + void testNoByte() throws Exception { + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(new byte[0]); + InputStream testIs = createFirstByteCachingStream(byteArrayInputStream); + Assertions.assertEquals(0, testIs.available()); + } + + @Test + void testOneByte() throws Exception { + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(new byte[]{'A'}); + InputStream testIs = createFirstByteCachingStream(byteArrayInputStream); + Assertions.assertEquals(1, testIs.available()); + Assertions.assertEquals(1, testIs.available()); // idempotency + Assertions.assertEquals('A', testIs.read()); + Assertions.assertEquals(0, testIs.available()); + } + + @Test + void testTwoBytes() throws Exception { + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(new byte[]{'A', 'B'}); + InputStream testIs = createFirstByteCachingStream(byteArrayInputStream); + Assertions.assertEquals(2, testIs.available()); + Assertions.assertEquals(2, testIs.available()); // idempotency + Assertions.assertEquals('A', testIs.read()); + Assertions.assertEquals(1, testIs.available()); + Assertions.assertEquals(1, testIs.available()); // idempotency + Assertions.assertEquals('B', testIs.read()); + Assertions.assertEquals(0, testIs.available()); + } + + @Test + void testTwoBytesReadAtOnce() throws Exception { + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(new byte[]{'A', 'B'}); + InputStream testIs = createFirstByteCachingStream(byteArrayInputStream); + Assertions.assertEquals(2, testIs.available()); + + byte[] bytes = new byte[2]; + testIs.read(bytes); + Assertions.assertEquals('A', bytes[0]); + Assertions.assertEquals('B', bytes[1]); + Assertions.assertEquals(0, testIs.available()); + } +} diff --git a/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/NoEntityTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/NoEntityTest.java index 0954072d8a..6635a615da 100644 --- a/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/NoEntityTest.java +++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/NoEntityTest.java @@ -16,6 +16,7 @@ package org.glassfish.jersey.jnh.connector; +import jakarta.ws.rs.core.GenericType; import org.glassfish.jersey.client.ClientConfig; import org.glassfish.jersey.logging.LoggingFeature; import org.glassfish.jersey.server.ResourceConfig; @@ -44,6 +45,13 @@ public Response get() { @POST public void post(String entity) { } + + @GET + @Path("/success") + public Response getSuccessfully() { + return Response.status(Response.Status.NO_CONTENT).build(); + } + } @Override @@ -77,6 +85,32 @@ public void testGetWithClose() { } } + @Test + public void testGetVoidWithClose() { + WebTarget r = target("test"); + for (int i = 0; i < 5; i++) { + Response cr = r.request().get(); + cr.close(); + } + } + + @Test + public void testGetVoid() { + WebTarget r = target("test/success"); + for (int i = 0; i < 5; i++) { + r.request().get(void.class); + } + } + + @Test + public void testGetGenericVoid() { + WebTarget r = target("test/success"); + for (int i = 0; i < 5; i++) { + r.request().get(new GenericType() { + }); + } + } + @Test public void testPost() { WebTarget r = target("test");