From 222a1c8d2794798742ee8b1e529ed4e840319402 Mon Sep 17 00:00:00 2001 From: Jorge Bescos Gascon Date: Fri, 18 Oct 2024 12:13:37 +0200 Subject: [PATCH] AsyncContext completion attempt after AsyncListener.onError is called #5675 Signed-off-by: Jorge Bescos Gascon --- .../jersey/server/ServerRuntime.java | 25 ++++++-- .../jersey/server/ServerRuntimeTest.java | 63 +++++++++++++++++++ 2 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 core-server/src/test/java/org/glassfish/jersey/server/ServerRuntimeTest.java diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ServerRuntime.java b/core-server/src/main/java/org/glassfish/jersey/server/ServerRuntime.java index bae3a0e12d..3ba8e01cb1 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/ServerRuntime.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/ServerRuntime.java @@ -277,6 +277,14 @@ public void run() { } } + static boolean isIOException(Throwable t) { + boolean ioException = false; + while (t != null && !(ioException = t instanceof IOException)) { + t = t.getCause(); + } + return ioException; + } + /** * Get the Jersey server runtime background scheduler. * @@ -669,11 +677,18 @@ public OutputStream getOutputStream(final int contentLength) throws IOException } catch (final Throwable ex) { if (response.isCommitted()) { - /** - * We're done with processing here. There's nothing we can do about the exception so - * let's just log it. - */ - LOGGER.log(Level.SEVERE, LocalizationMessages.ERROR_WRITING_RESPONSE_ENTITY(), ex); + if (isIOException(ex)) { + skipFinally = true; + LOGGER.log(Level.WARNING, "Response was sent, but there is IO issue", ex); + // Connection is broken. Re-throw to the web container to remove the connection. + throw new MappableException("Response was sent, but there is IO issue", ex); + } else { + /** + * We're done with processing here. There's nothing we can do about the exception so + * let's just log it. + */ + LOGGER.log(Level.SEVERE, LocalizationMessages.ERROR_WRITING_RESPONSE_ENTITY(), ex); + } } else { skipFinally = true; if (ex instanceof RuntimeException) { diff --git a/core-server/src/test/java/org/glassfish/jersey/server/ServerRuntimeTest.java b/core-server/src/test/java/org/glassfish/jersey/server/ServerRuntimeTest.java new file mode 100644 index 0000000000..33b33f7bbe --- /dev/null +++ b/core-server/src/test/java/org/glassfish/jersey/server/ServerRuntimeTest.java @@ -0,0 +1,63 @@ +/* + * 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.server; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.SocketException; + +import org.junit.jupiter.api.Test; + +public class ServerRuntimeTest { + + @Test + public void ioExceptionInRoot() { + assertTrue(ServerRuntime.isIOException(new IOException())); + assertTrue(ServerRuntime.isIOException(new IOException(new RuntimeException()))); + } + + @Test + public void ioExceptionInCause() { + assertTrue(ServerRuntime.isIOException(new RuntimeException(new IOException()))); + assertTrue(ServerRuntime.isIOException(new RuntimeException(new RuntimeException(new IOException())))); + } + + @Test + public void unckeckedIOExceptionInCause() { + assertTrue(ServerRuntime.isIOException(new UncheckedIOException(new SocketException()))); + assertTrue(ServerRuntime.isIOException(new RuntimeException(new UncheckedIOException(new SocketException())))); + } + + @Test + public void noIOException() { + assertFalse(ServerRuntime.isIOException(new RuntimeException())); + assertFalse(ServerRuntime.isIOException(new RuntimeException(new RuntimeException()))); + } + + @Test + public void nullException() { + assertFalse(ServerRuntime.isIOException(null)); + } + + @Test + public void nullCause() { + assertFalse(ServerRuntime.isIOException(new RuntimeException((Throwable) null))); + } +}