diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLConnection.java b/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLConnection.java index 13e105b7b0d..875923029cf 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLConnection.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -111,6 +111,11 @@ class AsyncSSLConnection extends AbstractAsyncSSLConnection { plainConnection.close(); } + @Override + void close(Throwable cause) { + plainConnection.close(cause); + } + @Override SSLTube getConnectionFlow() { return flow; diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLTunnelConnection.java b/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLTunnelConnection.java index 9f2129fef42..dc9e1bfb795 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLTunnelConnection.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLTunnelConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -113,6 +113,11 @@ class AsyncSSLTunnelConnection extends AbstractAsyncSSLConnection { plainConnection.close(); } + @Override + void close(Throwable cause) { + plainConnection.close(cause); + } + @Override SocketChannel channel() { return plainConnection.channel(); diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java b/src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java index ba6e668241e..e643b05422a 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java @@ -142,6 +142,7 @@ final class Exchange { static final class ConnectionAborter { private volatile HttpConnection connection; private volatile boolean closeRequested; + private volatile Throwable cause; void connection(HttpConnection connection) { boolean closeRequested; @@ -156,20 +157,27 @@ final class Exchange { this.closeRequested = false; } } - if (closeRequested) closeConnection(connection); + if (closeRequested) closeConnection(connection, cause); } - void closeConnection() { + void closeConnection(Throwable error) { HttpConnection connection; + Throwable cause; synchronized (this) { + cause = this.cause; + if (cause == null) { + cause = error; + } connection = this.connection; if (connection == null) { closeRequested = true; + this.cause = cause; } else { this.connection = null; + this.cause = null; } } - closeConnection(connection); + closeConnection(connection, cause); } HttpConnection disable() { @@ -178,14 +186,15 @@ final class Exchange { connection = this.connection; this.connection = null; this.closeRequested = false; + this.cause = null; } return connection; } - private static void closeConnection(HttpConnection connection) { + private static void closeConnection(HttpConnection connection, Throwable cause) { if (connection != null) { try { - connection.close(); + connection.close(cause); } catch (Throwable t) { // ignore } @@ -264,11 +273,19 @@ final class Exchange { impl.cancel(cause); } else { // no impl yet. record the exception - failed = cause; + IOException failed = this.failed; + if (failed == null) { + synchronized (this) { + failed = this.failed; + if (failed == null) { + failed = this.failed = cause; + } + } + } // abort/close the connection if setting up the exchange. This can // be important when setting up HTTP/2 - connectionAborter.closeConnection(); + connectionAborter.closeConnection(failed); // now call checkCancelled to recheck the impl. // if the failed state is set and the impl is not null, reset diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/HttpConnection.java b/src/java.net.http/share/classes/jdk/internal/net/http/HttpConnection.java index f300d3ec93e..be33469c052 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/HttpConnection.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/HttpConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -427,11 +427,19 @@ abstract class HttpConnection implements Closeable { abstract ConnectionPool.CacheKey cacheKey(); /** - * Closes this connection, by returning the socket to its connection pool. + * Closes this connection. */ @Override public abstract void close(); + /** + * Closes this connection due to the given cause. + * @param cause the cause for which the connection is closed, may be null + */ + void close(Throwable cause) { + close(); + } + abstract FlowTube getConnectionFlow(); /** diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/PlainHttpConnection.java b/src/java.net.http/share/classes/jdk/internal/net/http/PlainHttpConnection.java index 1d125e60e98..d9ac4dc05f2 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/PlainHttpConnection.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/PlainHttpConnection.java @@ -401,11 +401,13 @@ class PlainHttpConnection extends HttpConnection { return "PlainHttpConnection: " + super.toString(); } - /** - * Closes this connection - */ @Override public void close() { + close(null); + } + + @Override + void close(Throwable cause) { var closed = this.closed; if (closed) return; stateLock.lock(); @@ -423,7 +425,7 @@ class PlainHttpConnection extends HttpConnection { } try { chan.close(); - tube.signalClosed(); + tube.signalClosed(cause); } finally { client().connectionClosed(this); } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/PlainTunnelingConnection.java b/src/java.net.http/share/classes/jdk/internal/net/http/PlainTunnelingConnection.java index b82e764f09e..4565514f577 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/PlainTunnelingConnection.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/PlainTunnelingConnection.java @@ -157,7 +157,12 @@ final class PlainTunnelingConnection extends HttpConnection { @Override public void close() { - delegate.close(); + close(null); + } + + @Override + void close(Throwable cause) { + delegate.close(cause); connected = false; } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/SocketTube.java b/src/java.net.http/share/classes/jdk/internal/net/http/SocketTube.java index 388384a379e..1421e461656 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/SocketTube.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/SocketTube.java @@ -149,7 +149,7 @@ final class SocketTube implements FlowTube { // Events // // ======================================================================// - void signalClosed() { + void signalClosed(Throwable cause) { // Ensures that the subscriber will be terminated and that future // subscribers will be notified when the connection is closed. if (Log.channel()) { @@ -157,7 +157,7 @@ final class SocketTube implements FlowTube { channelDescr()); } readPublisher.subscriptionImpl.signalError( - new IOException("connection closed locally")); + new IOException("connection closed locally", cause)); } /**