From c2e1271b3709427a2130fb2c70a853dc66241cfc Mon Sep 17 00:00:00 2001 From: Benjamin Guan <47118893+mumbler6@users.noreply.github.com> Date: Tue, 22 Oct 2024 13:07:53 -0500 Subject: [PATCH 1/9] Fixed flaky test in DisposableSupplierText.java with set comparison --- .../inject/cdi/se/DisposableSupplierTest.java | 18 ++++++++++++++---- .../inject/hk2/DisposableSupplierTest.java | 18 ++++++++++++++---- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/inject/cdi2-se/src/test/java/org/glassfish/jersey/inject/cdi/se/DisposableSupplierTest.java b/inject/cdi2-se/src/test/java/org/glassfish/jersey/inject/cdi/se/DisposableSupplierTest.java index 69d68eb247..5dc78bc373 100644 --- a/inject/cdi2-se/src/test/java/org/glassfish/jersey/inject/cdi/se/DisposableSupplierTest.java +++ b/inject/cdi2-se/src/test/java/org/glassfish/jersey/inject/cdi/se/DisposableSupplierTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -17,6 +17,8 @@ package org.glassfish.jersey.inject.cdi.se; import java.lang.reflect.Type; +import java.util.HashSet; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; @@ -368,9 +370,17 @@ public void testDisposeComposedObjectWithPerLookupFields() { // All instances should be the same because they are request scoped. ComposedObject instance = injectionManager.getInstance(ComposedObject.class); - assertEquals("1", instance.getFirst()); - assertEquals("2", instance.getSecond()); - assertEquals("3", instance.getThird()); + Set set1 = new HashSet() {{ + add("1"); + add("2"); + add("3"); + }}; + Set set2 = new HashSet() {{ + add(instance.getFirst().toString()); + add(instance.getSecond().toString()); + add(instance.getThird().toString()); + }}; + assertEquals(set1, set2); }); Supplier cleanedSupplier = atomicSupplier.get(); diff --git a/inject/hk2/src/test/java/org/glassfish/jersey/inject/hk2/DisposableSupplierTest.java b/inject/hk2/src/test/java/org/glassfish/jersey/inject/hk2/DisposableSupplierTest.java index 9eefe4889a..5a2d86968b 100644 --- a/inject/hk2/src/test/java/org/glassfish/jersey/inject/hk2/DisposableSupplierTest.java +++ b/inject/hk2/src/test/java/org/glassfish/jersey/inject/hk2/DisposableSupplierTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -17,6 +17,8 @@ package org.glassfish.jersey.inject.hk2; import java.lang.reflect.Type; +import java.util.HashSet; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; @@ -374,9 +376,17 @@ public void testDisposeComposedObjectWithPerLookupFields() { // All instances should be the same because they are request scoped. ComposedObject instance = injectionManager.getInstance(ComposedObject.class); - assertEquals("1", instance.first); - assertEquals("2", instance.second); - assertEquals("3", instance.third); + Set set1 = new HashSet() {{ + add("1"); + add("2"); + add("3"); + }}; + Set set2 = new HashSet() {{ + add(instance.first.toString()); + add(instance.second.toString()); + add(instance.third.toString()); + }}; + assertEquals(set1, set2); }); Supplier cleanedSupplier = atomicSupplier.get(); From 7903c976f0aec082c3c433c335b79c2c9f0ed094 Mon Sep 17 00:00:00 2001 From: Markus KARG Date: Thu, 31 Oct 2024 08:49:05 +0100 Subject: [PATCH 2/9] Fixed typo: Class>?< should be Class --- docs/src/main/docbook/media.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/docbook/media.xml b/docs/src/main/docbook/media.xml index 965aad10e3..d91fe66880 100644 --- a/docs/src/main/docbook/media.xml +++ b/docs/src/main/docbook/media.xml @@ -1136,7 +1136,7 @@ public JaxbBean getSimpleJSONP() { public class JsonbContextResolver implements ContextResolver<Jsonb> { @Override - public Jsonb getContext(Class>?< type) { + public Jsonb getContext(Class<?> type) { JsonbConfig config = new JsonbConfig(); // configure JsonbConfig ... From 39800bce3995d956f84c094770af287a5c9447df Mon Sep 17 00:00:00 2001 From: Jorge Bescos Gascon Date: Tue, 12 Nov 2024 08:20:15 +0100 Subject: [PATCH 3/9] Broken pipe Exception from Jersey layer while closing response in ServerRuntime.writeResponse() not handled or re-thrown #5783 (#5786) Signed-off-by: Jorge Bescos Gascon --- .../internal/OutboundJaxrsResponse.java | 11 ++- .../internal/OutboundMessageContext.java | 13 +-- .../jersey/server/ContainerResponse.java | 15 ++- .../tests/e2e/server/Issue5783Test.java | 97 +++++++++++++++++++ 4 files changed, 121 insertions(+), 15 deletions(-) create mode 100644 tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/Issue5783Test.java diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundJaxrsResponse.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundJaxrsResponse.java index f3fd445130..d63e0a90e7 100644 --- a/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundJaxrsResponse.java +++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundJaxrsResponse.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2019 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 @@ -26,6 +26,8 @@ import java.util.Date; import java.util.HashSet; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import java.util.Locale; import java.util.Map; import java.util.Set; @@ -200,7 +202,12 @@ public boolean bufferEntity() throws ProcessingException { @Override public void close() throws ProcessingException { closed = true; - context.close(); + try { + context.close(); + } catch (Exception e) { + // Just log the exception + Logger.getLogger(OutboundJaxrsResponse.class.getName()).log(Level.FINE, e.getMessage(), e); + } if (buffered) { // release buffer context.setEntity(null); diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java index 3265a96340..8e10cdffba 100644 --- a/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java +++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java @@ -18,6 +18,7 @@ import java.io.IOException; import java.io.OutputStream; +import java.io.UncheckedIOException; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.ArrayList; @@ -27,8 +28,6 @@ import java.util.Locale; import java.util.Set; import java.util.function.Function; -import java.util.logging.Level; -import java.util.logging.Logger; import java.util.stream.Collectors; import javax.ws.rs.core.Configuration; @@ -557,6 +556,7 @@ public boolean isCommitted() { /** * Closes the context. Flushes and closes the entity stream. + * @throws UncheckedIOException if IO errors */ public void close() { if (hasEntity()) { @@ -567,11 +567,7 @@ public void close() { } es.close(); } catch (IOException e) { - // Happens when the client closed connection before receiving the full response. - // This is OK and not interesting in the vast majority of the cases - // hence the log level set to FINE to make sure it does not flood the log unnecessarily - // (especially for clients disconnecting from SSE listening, which is very common). - Logger.getLogger(OutboundMessageContext.class.getName()).log(Level.FINE, e.getMessage(), e); + throw new UncheckedIOException(e); } finally { // In case some of the output stream wrapper does not delegate close() call we // close the root stream manually to make sure it commits the data. @@ -579,8 +575,7 @@ public void close() { try { committingOutputStream.close(); } catch (IOException e) { - // Just log the exception - Logger.getLogger(OutboundMessageContext.class.getName()).log(Level.FINE, e.getMessage(), e); + throw new UncheckedIOException(e); } } } diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ContainerResponse.java b/core-server/src/main/java/org/glassfish/jersey/server/ContainerResponse.java index cf6799ed25..99731d07c2 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/ContainerResponse.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/ContainerResponse.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 @@ -26,6 +26,8 @@ import java.net.URI; import java.util.Date; import java.util.Locale; +import java.util.logging.Level; +import java.util.logging.Logger; import java.util.Map; import java.util.Set; @@ -400,9 +402,14 @@ public boolean isCommitted() { public void close() { if (!closed) { closed = true; - messageContext.close(); - requestContext.getResponseWriter().commit(); - requestContext.setWorkers(null); + try { + messageContext.close(); + requestContext.setWorkers(null); + requestContext.getResponseWriter().commit(); + } catch (Exception e) { + Logger.getLogger(ContainerResponse.class.getName()).log(Level.FINE, e.getMessage(), e); + requestContext.getResponseWriter().failure(e); + } } } diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/Issue5783Test.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/Issue5783Test.java new file mode 100644 index 0000000000..cc1c873b13 --- /dev/null +++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/Issue5783Test.java @@ -0,0 +1,97 @@ +/* + * 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.server; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; +import javax.ws.rs.core.Application; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; + +import org.glassfish.jersey.server.ContainerRequest; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.server.spi.ContainerResponseWriter; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.jupiter.api.Test; + +public class Issue5783Test extends JerseyTest { + + private static final String ERROR = "Intentional issue5783 exception"; + private static volatile String exceptionMessage; + + @Override + protected Application configure() { + return new ResourceConfig(Resource.class, ResponseFilter.class); + } + + @Test + public void closeException() throws InterruptedException { + target("/test").request().get(); + assertEquals(ERROR, exceptionMessage); + } + + @Path("/test") + public static class Resource { + + @GET + public Response closeException(@Context ContainerRequest request) { + // Save the exception when response.getRequestContext().getResponseWriter().failure(e) + ContainerResponseWriter writer = request.getResponseWriter(); + ContainerResponseWriter proxy = (ContainerResponseWriter) Proxy.newProxyInstance( + ContainerResponseWriter.class.getClassLoader(), + new Class[]{ContainerResponseWriter.class}, new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if ("failure".equals(method.getName())) { + exceptionMessage = ((Throwable) args[0]).getCause().getMessage(); + } + return method.invoke(writer, args); + } + }); + request.setWriter(proxy); + return Response.ok().build(); + } + } + + @Provider + public static class ResponseFilter implements ContainerResponseFilter { + @Override + public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) + throws IOException { + // Hack it to make ContainerResponse#close throws one exception + responseContext.setEntity("something"); + responseContext.setEntityStream(new ByteArrayOutputStream() { + @Override + public void close() throws IOException { + throw new IOException(ERROR); + } + }); + } + } +} From 0f477df9e0c05a86a0f507f44c49cd4714d9eaac Mon Sep 17 00:00:00 2001 From: Dmitry Spikhalsky Date: Thu, 7 Nov 2024 17:40:18 -0500 Subject: [PATCH 4/9] ParamConverterProvider constructors are now public --- .../internal/inject/ParamConverters.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/inject/ParamConverters.java b/core-common/src/main/java/org/glassfish/jersey/internal/inject/ParamConverters.java index 4a1ca86401..ca6facab42 100644 --- a/core-common/src/main/java/org/glassfish/jersey/internal/inject/ParamConverters.java +++ b/core-common/src/main/java/org/glassfish/jersey/internal/inject/ParamConverters.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. * Copyright (c) 2018 Payara Foundation and/or its affiliates. * * This program and the accompanying materials are made available under the @@ -121,7 +121,7 @@ public String toString(final T value) throws IllegalArgumentException { @Singleton public static class StringConstructor extends ParamConverterCompliance implements ParamConverterProvider { - private StringConstructor(boolean canReturnNull) { + protected StringConstructor(boolean canReturnNull) { super(canReturnNull); } @@ -150,7 +150,7 @@ protected T _fromString(final String value) throws Exception { @Singleton public static class TypeValueOf extends ParamConverterCompliance implements ParamConverterProvider { - private TypeValueOf(boolean canReturnNull) { + protected TypeValueOf(boolean canReturnNull) { super(canReturnNull); } @@ -178,7 +178,7 @@ public T _fromString(final String value) throws Exception { @Singleton public static class TypeFromString extends ParamConverterCompliance implements ParamConverterProvider { - private TypeFromString(boolean canReturnNull) { + protected TypeFromString(boolean canReturnNull) { super(canReturnNull); } @@ -206,7 +206,7 @@ public T _fromString(final String value) throws Exception { @Singleton public static class TypeFromStringEnum extends TypeFromString { - private TypeFromStringEnum(boolean canReturnNull) { + protected TypeFromStringEnum(boolean canReturnNull) { super(canReturnNull); } @@ -221,7 +221,7 @@ public ParamConverter getConverter(final Class rawType, @Singleton public static class CharacterProvider extends ParamConverterCompliance implements ParamConverterProvider { - private CharacterProvider(boolean canReturnNull) { + protected CharacterProvider(boolean canReturnNull) { super(canReturnNull); } @@ -266,7 +266,7 @@ public String toString(T value) { @Singleton public static class DateProvider extends ParamConverterCompliance implements ParamConverterProvider { - private DateProvider(boolean canReturnNull) { + protected DateProvider(boolean canReturnNull) { super(canReturnNull); } @@ -309,7 +309,7 @@ public static class OptionalCustomProvider extends ParamConverterCompliance impl // Delegates to this provider when the type of Optional is extracted. private final InjectionManager manager; - public OptionalCustomProvider(InjectionManager manager, boolean canReturnNull) { + protected OptionalCustomProvider(InjectionManager manager, boolean canReturnNull) { super(canReturnNull); this.manager = manager; } @@ -365,6 +365,8 @@ public String toString(T value) throws IllegalArgumentException { @Singleton public static class OptionalProvider implements ParamConverterProvider { + protected OptionalProvider() {} + @Override public ParamConverter getConverter(Class rawType, Type genericType, Annotation[] annotations) { final Optionals optionals = Optionals.getOptional(rawType); From f21768534972a8af4c3f65c158637cce4567887f Mon Sep 17 00:00:00 2001 From: Maxim Nesen Date: Tue, 5 Nov 2024 14:14:45 +0100 Subject: [PATCH 5/9] Wrapping all methods of the EntityInputStream/InputStreamWrapper Signed-off-by: Maxim Nesen --- core-common/pom.xml | 6 +- .../message/internal/EntityInputStream.java | 44 ++------ .../jersey/innate/io/InputStreamWrapper.java | 103 ++++++++++++++++++ .../jersey/innate/io/InputStreamWrapper.java | 0 4 files changed, 114 insertions(+), 39 deletions(-) create mode 100644 core-common/src/main/java11/org/glassfish/jersey/innate/io/InputStreamWrapper.java rename core-common/src/main/{java => java8}/org/glassfish/jersey/innate/io/InputStreamWrapper.java (100%) diff --git a/core-common/pom.xml b/core-common/pom.xml index 71395592be..424315b1be 100644 --- a/core-common/pom.xml +++ b/core-common/pom.xml @@ -310,7 +310,7 @@ - + compile-2-java8 process-resources @@ -497,10 +497,10 @@ copy-resources - ${project.build.directory}/generated-sources/rsrc-gen/META-INF/versions/11/org/glassfish/jersey/internal/jsr166 + ${project.build.directory}/generated-sources/rsrc-gen/META-INF/versions/11/org/glassfish/jersey - ${java11.sourceDirectory}/org/glassfish/jersey/internal/jsr166 + ${java11.sourceDirectory}/org/glassfish/jersey diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/EntityInputStream.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/EntityInputStream.java index 06678b16fe..fe9fb0e160 100644 --- a/core-common/src/main/java/org/glassfish/jersey/message/internal/EntityInputStream.java +++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/EntityInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2019 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 @@ -22,6 +22,7 @@ import javax.ws.rs.ProcessingException; +import org.glassfish.jersey.innate.io.InputStreamWrapper; import org.glassfish.jersey.internal.LocalizationMessages; /** @@ -33,7 +34,7 @@ * * @author Marek Potociar */ -public class EntityInputStream extends InputStream { +public class EntityInputStream extends InputStreamWrapper { private InputStream input; private boolean closed = false; @@ -64,40 +65,6 @@ public EntityInputStream(InputStream input) { this.input = input; } - @Override - public int read() throws IOException { - return input.read(); - } - - @Override - public int read(byte[] b) throws IOException { - return input.read(b); - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - return input.read(b, off, len); - } - - @Override - public long skip(long n) throws IOException { - return input.skip(n); - } - - @Override - public int available() throws IOException { - return input.available(); - } - - @Override - public void mark(int readLimit) { - input.mark(readLimit); - } - - @Override - public boolean markSupported() { - return input.markSupported(); - } /** * {@inheritDoc} @@ -232,4 +199,9 @@ public final InputStream getWrappedStream() { public final void setWrappedStream(InputStream wrapped) { input = wrapped; } + + @Override + protected InputStream getWrapped() { + return input; + } } diff --git a/core-common/src/main/java11/org/glassfish/jersey/innate/io/InputStreamWrapper.java b/core-common/src/main/java11/org/glassfish/jersey/innate/io/InputStreamWrapper.java new file mode 100644 index 0000000000..abcee6b286 --- /dev/null +++ b/core-common/src/main/java11/org/glassfish/jersey/innate/io/InputStreamWrapper.java @@ -0,0 +1,103 @@ +/* + * 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.innate.io; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Generic wrapper template for InputStream. + */ +public abstract class InputStreamWrapper extends InputStream { + + /** + * Return the wrapped stream + * @return + */ + protected abstract InputStream getWrapped(); + + /** + * Get wrapped stream that can throw {@link IOException} + * @return the wrapped InputStream. + * @throws IOException + */ + protected InputStream getWrappedIOE() throws IOException { + return getWrapped(); + } + + @Override + public int read() throws IOException { + return getWrappedIOE().read(); + } + + @Override + public int read(byte[] b) throws IOException { + return getWrappedIOE().read(b); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + return getWrappedIOE().read(b, off, len); + } + + @Override + public byte[] readAllBytes() throws IOException { + return getWrappedIOE().readAllBytes(); + } + + @Override + public int readNBytes(byte[] b, int off, int len) throws IOException { + return getWrappedIOE().readNBytes(b, off, len); + } + + @Override + public long transferTo(OutputStream out) throws IOException { + return getWrappedIOE().transferTo(out); + } + + + @Override + public long skip(long n) throws IOException { + return getWrappedIOE().skip(n); + } + + @Override + public int available() throws IOException { + return getWrappedIOE().available(); + } + + @Override + public void close() throws IOException { + getWrappedIOE().close(); + } + + @Override + public void mark(int readlimit) { + getWrapped().mark(readlimit); + } + + @Override + public void reset() throws IOException { + getWrappedIOE().reset(); + } + + @Override + public boolean markSupported() { + return getWrapped().markSupported(); + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/innate/io/InputStreamWrapper.java b/core-common/src/main/java8/org/glassfish/jersey/innate/io/InputStreamWrapper.java similarity index 100% rename from core-common/src/main/java/org/glassfish/jersey/innate/io/InputStreamWrapper.java rename to core-common/src/main/java8/org/glassfish/jersey/innate/io/InputStreamWrapper.java From bd6edcdbf8457149d9947c10606b5bc874aad5e1 Mon Sep 17 00:00:00 2001 From: Jorge Bescos Gascon Date: Tue, 29 Oct 2024 08:20:21 +0100 Subject: [PATCH 6/9] Jersey 3.1.9: java.lang.NoSuchMethodException: jakarta.inject.Inject.value() #5782 Signed-off-by: Jorge Bescos Gascon --- .../org/glassfish/jersey/model/Parameter.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/core-common/src/main/java/org/glassfish/jersey/model/Parameter.java b/core-common/src/main/java/org/glassfish/jersey/model/Parameter.java index 126621aeb9..a0e34b706f 100644 --- a/core-common/src/main/java/org/glassfish/jersey/model/Parameter.java +++ b/core-common/src/main/java/org/glassfish/jersey/model/Parameter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 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 @@ -447,14 +447,20 @@ protected static List

+ String.format("Unable to get the %s annotation value property", a.getClass().getName())); } catch (Exception ex) { if (LOGGER.isLoggable(Level.FINER)) { LOGGER.log(Level.FINER, From b2316648590c368aeb5b3729b29af300ab5c0bea Mon Sep 17 00:00:00 2001 From: jansupol Date: Wed, 13 Nov 2024 22:44:37 +0100 Subject: [PATCH 7/9] NettyConnector - HOST header contains port & 307 works with buffered post Signed-off-by: jansupol --- .../netty/connector/JerseyClientHandler.java | 18 ++++- .../netty/connector/NettyConnector.java | 9 ++- .../netty/connector/FollowRedirectsTest.java | 28 ++++++- .../netty/connector/HostHeaderTest.java | 75 +++++++++++++++++++ 4 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/HostHeaderTest.java diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java index c2e47c6e22..9b24ea4366 100644 --- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java +++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java @@ -17,7 +17,6 @@ package org.glassfish.jersey.netty.connector; import java.io.IOException; -import java.io.InputStream; import java.net.URI; import java.util.Iterator; import java.util.List; @@ -47,7 +46,6 @@ import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpObject; import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpUtil; import io.netty.handler.codec.http.LastHttpContent; import io.netty.handler.timeout.IdleStateEvent; import org.glassfish.jersey.uri.internal.JerseyUriBuilder; @@ -145,7 +143,21 @@ protected void notifyResponse() { ClientRequest newReq = new ClientRequest(jerseyRequest); newReq.setUri(newUri); restrictRedirectRequest(newReq, cr); - connector.execute(newReq, redirectUriHistory, responseAvailable); + + final NettyConnector newConnector = new NettyConnector(newReq.getClient()); + newConnector.execute(newReq, redirectUriHistory, new CompletableFuture() { + @Override + public boolean complete(ClientResponse value) { + newConnector.close(); + return responseAvailable.complete(value); + } + + @Override + public boolean completeExceptionally(Throwable ex) { + newConnector.close(); + return responseAvailable.completeExceptionally(ex); + } + }); } } catch (IllegalArgumentException e) { responseAvailable.completeExceptionally( diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java index 4a4cb8603a..f242f10bc7 100644 --- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java +++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java @@ -418,7 +418,14 @@ protected void initChannel(SocketChannel ch) throws Exception { // host header - http 1.1 if (!nettyRequest.headers().contains(HttpHeaderNames.HOST)) { - nettyRequest.headers().add(HttpHeaderNames.HOST, jerseyRequest.getUri().getHost()); + int requestPort = jerseyRequest.getUri().getPort(); + final String hostHeader; + if (requestPort != 80 && requestPort != 443) { + hostHeader = jerseyRequest.getUri().getHost() + ":" + requestPort; + } else { + hostHeader = jerseyRequest.getUri().getHost(); + } + nettyRequest.headers().add(HttpHeaderNames.HOST, hostHeader); } } diff --git a/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/FollowRedirectsTest.java b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/FollowRedirectsTest.java index 127668bea8..c1b2c9216c 100644 --- a/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/FollowRedirectsTest.java +++ b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/FollowRedirectsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -24,16 +24,20 @@ import java.util.logging.Logger; import javax.ws.rs.GET; +import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.ProcessingException; import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.Entity; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.Application; +import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.glassfish.jersey.client.ClientConfig; import org.glassfish.jersey.client.ClientProperties; +import org.glassfish.jersey.client.RequestEntityProcessing; import org.glassfish.jersey.logging.LoggingFeature; import org.glassfish.jersey.netty.connector.internal.RedirectException; import org.glassfish.jersey.server.ResourceConfig; @@ -60,6 +64,11 @@ public String get() { return "GET"; } + @POST + public String post() { + return "POST"; + } + @GET @Path("redirect") public Response redirect() { @@ -77,6 +86,12 @@ public Response loop() { public Response redirect2() { return Response.seeOther(URI.create(TEST_URL_REF.get() + "/redirect")).build(); } + + @POST + @Path("status307") + public Response status307() { + return Response.temporaryRedirect(URI.create(TEST_URL_REF.get())).build(); + } } @Override @@ -169,4 +184,15 @@ public void testRedirectNoLimitReached() { assertEquals(200, r.getStatus()); assertEquals("GET", r.readEntity(String.class)); } + + @Test + public void testRedirect307PostBuffered() { + try (Response response = target("test/status307") + .property(ClientProperties.FOLLOW_REDIRECTS, true) + .property(ClientProperties.REQUEST_ENTITY_PROCESSING, RequestEntityProcessing.BUFFERED) + .request().post(Entity.entity("Something", MediaType.TEXT_PLAIN_TYPE))) { + assertEquals(200, response.getStatus()); + assertEquals("POST", response.readEntity(String.class)); + } + } } diff --git a/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/HostHeaderTest.java b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/HostHeaderTest.java new file mode 100644 index 0000000000..8d42c3699a --- /dev/null +++ b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/HostHeaderTest.java @@ -0,0 +1,75 @@ +/* + * 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.netty.connector; + +import org.glassfish.jersey.client.ClientConfig; +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.client.ClientBuilder; +import javax.ws.rs.core.Application; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response; + +public class HostHeaderTest extends JerseyTest { + + private static final String HTTP_HEADER_NAME = "HTTP_PORT_INT"; + + @Path("/") + public static class HostHeaderTestEchoResource { + @GET + public String get(@Context HttpHeaders headers) { + String sPort = headers.getHeaderString(HTTP_HEADER_NAME); + String hostPort = headers.getHeaderString(HttpHeaders.HOST); + int indexColon = hostPort.indexOf(':'); + if (indexColon != -1) { + hostPort = hostPort.substring(indexColon + 1); + } + if (sPort.equals(hostPort.trim())) { + return GET.class.getName(); + } else { + return "Expected port " + sPort + " but found " + hostPort; + } + } + } + + @Override + protected Application configure() { + return new ResourceConfig(HostHeaderTestEchoResource.class); + } + + @Test + public void testHostHeaderAndPort() { + int port = getPort(); + ClientConfig config = new ClientConfig(); + config.connectorProvider(new NettyConnectorProvider()); + try (Response response = ClientBuilder.newClient(config).target(target().getUri()) + .request() + .header(HTTP_HEADER_NAME, port) + .get()) { + MatcherAssert.assertThat(response.getStatus(), Matchers.is(200)); + MatcherAssert.assertThat(response.readEntity(String.class), Matchers.is(GET.class.getName())); + } + } + +} From 1b992379bed082b2a8ae42f8ec2994d5aa89d714 Mon Sep 17 00:00:00 2001 From: jansupol Date: Thu, 14 Nov 2024 22:37:43 +0100 Subject: [PATCH 8/9] Set correct HOST header for Netty & POST Signed-off-by: jansupol --- .../netty/connector/NettyConnector.java | 31 ++++++++++--------- .../netty/connector/HostHeaderTest.java | 24 ++++++++++++++ 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java index f242f10bc7..671e72a94f 100644 --- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java +++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java @@ -415,18 +415,7 @@ protected void initChannel(SocketChannel ch) throws Exception { // headers if (!jerseyRequest.hasEntity()) { setHeaders(jerseyRequest, nettyRequest.headers(), false); - - // host header - http 1.1 - if (!nettyRequest.headers().contains(HttpHeaderNames.HOST)) { - int requestPort = jerseyRequest.getUri().getPort(); - final String hostHeader; - if (requestPort != 80 && requestPort != 443) { - hostHeader = jerseyRequest.getUri().getHost() + ":" + requestPort; - } else { - hostHeader = jerseyRequest.getUri().getHost(); - } - nettyRequest.headers().add(HttpHeaderNames.HOST, hostHeader); - } + setHostHeader(jerseyRequest, nettyRequest); } if (jerseyRequest.hasEntity()) { @@ -474,9 +463,7 @@ public void operationComplete(io.netty.util.concurrent.Future futu @Override public OutputStream getOutputStream(int contentLength) throws IOException { replaceHeaders(jerseyRequest, nettyRequest.headers()); // WriterInterceptor changes - if (!nettyRequest.headers().contains(HttpHeaderNames.HOST)) { - nettyRequest.headers().add(HttpHeaderNames.HOST, jerseyRequest.getUri().getHost()); - } + setHostHeader(jerseyRequest, nettyRequest); headersSet.countDown(); return entityWriter.getOutputStream(); @@ -626,4 +613,18 @@ private static HttpHeaders replaceHeaders(ClientRequest jerseyRequest, HttpHeade private static boolean additionalProxyHeadersToKeep(String key) { return key.length() > 2 && (key.charAt(0) == 'x' || key.charAt(0) == 'X') && (key.charAt(1) == '-'); } + + private static void setHostHeader(ClientRequest jerseyRequest, HttpRequest nettyRequest) { + // host header - http 1.1 + if (!nettyRequest.headers().contains(HttpHeaderNames.HOST)) { + int requestPort = jerseyRequest.getUri().getPort(); + final String hostHeader; + if (requestPort != 80 && requestPort != 443) { + hostHeader = jerseyRequest.getUri().getHost() + ":" + requestPort; + } else { + hostHeader = jerseyRequest.getUri().getHost(); + } + nettyRequest.headers().add(HttpHeaderNames.HOST, hostHeader); + } + } } diff --git a/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/HostHeaderTest.java b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/HostHeaderTest.java index 8d42c3699a..e20eded3ec 100644 --- a/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/HostHeaderTest.java +++ b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/HostHeaderTest.java @@ -24,11 +24,14 @@ import org.junit.jupiter.api.Test; import javax.ws.rs.GET; +import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.Entity; import javax.ws.rs.core.Application; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; public class HostHeaderTest extends JerseyTest { @@ -37,6 +40,12 @@ public class HostHeaderTest extends JerseyTest { @Path("/") public static class HostHeaderTestEchoResource { + + @POST + public String post(@Context HttpHeaders headers) { + return get(headers); + } + @GET public String get(@Context HttpHeaders headers) { String sPort = headers.getHeaderString(HTTP_HEADER_NAME); @@ -72,4 +81,19 @@ public void testHostHeaderAndPort() { } } + @Test + public void testHostHeaderAndPortAfterRemovedFromFilter() { + int port = getPort(); + ClientConfig config = new ClientConfig(); + config.connectorProvider(new NettyConnectorProvider()); + try (Response response = ClientBuilder.newClient(config) + .target(target().getUri()) + .request() + .header(HTTP_HEADER_NAME, port) + .post(Entity.entity("xxx", MediaType.TEXT_PLAIN_TYPE))) { + MatcherAssert.assertThat(response.getStatus(), Matchers.is(200)); + MatcherAssert.assertThat(response.readEntity(String.class), Matchers.is(GET.class.getName())); + } + } + } From 47e37bd48edfadd9c4f558050b6abee600b0af1c Mon Sep 17 00:00:00 2001 From: mumbler6 Date: Wed, 16 Oct 2024 19:15:39 -0500 Subject: [PATCH 9/9] fixed flaky test in testDisabledModule() --- .../DefaultJsonJacksonProviderForBothModulesTest.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForBothModulesTest.java b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForBothModulesTest.java index 9d86b842b2..fe74a94dd3 100644 --- a/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForBothModulesTest.java +++ b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForBothModulesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved. + * 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 @@ -23,6 +23,9 @@ import javax.ws.rs.core.Application; +import java.util.List; +import java.util.Arrays; +import java.util.Collections; import static org.junit.jupiter.api.Assertions.assertEquals; public class DefaultJsonJacksonProviderForBothModulesTest extends JerseyTest { @@ -36,8 +39,12 @@ protected final Application configure() { public final void testDisabledModule() { final String response = target("entity/simple") .request().get(String.class); + String expected = "{\"name\":\"Hello\",\"value\":\"World\"}"; + List response_list = Arrays.asList(response.replaceAll("[{}]", "").split(",")); + List expected_list = Arrays.asList(expected.replaceAll("[{}]", "").split(",")); + Collections.sort(response_list); - assertEquals("{\"name\":\"Hello\",\"value\":\"World\"}", response); + assertEquals(expected_list, response_list); } }