From 0260a12663134de50833385d476ce153501af7db Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Wed, 16 Nov 2022 08:16:56 +0200 Subject: [PATCH] close connecting sockets and interrupt read thread on shutdown --- .../com/sparrowwallet/sparrow/AppController.java | 2 ++ .../com/sparrowwallet/sparrow/AppServices.java | 10 ++++++---- .../sparrowwallet/sparrow/net/ElectrumServer.java | 14 ++++++++++++-- .../sparrow/net/ProxyTcpOverTlsTransport.java | 12 +++++------- .../sparrow/net/TcpOverTlsTransport.java | 10 +++++----- .../sparrowwallet/sparrow/net/TcpTransport.java | 10 ++++++---- .../sparrow/net/TorTcpOverTlsTransport.java | 11 +++++------ .../sparrowwallet/sparrow/net/TorTcpTransport.java | 4 ++-- .../preferences/ServerPreferencesController.java | 12 ++++++++++-- .../terminal/preferences/ServerTestDialog.java | 8 ++++++++ 10 files changed, 61 insertions(+), 32 deletions(-) diff --git a/src/main/java/com/sparrowwallet/sparrow/AppController.java b/src/main/java/com/sparrowwallet/sparrow/AppController.java index 59050cb0..5739a4e9 100644 --- a/src/main/java/com/sparrowwallet/sparrow/AppController.java +++ b/src/main/java/com/sparrowwallet/sparrow/AppController.java @@ -944,6 +944,8 @@ public class AppController implements Initializable { if(AppServices.isConnected()) { return "Connected to " + Config.get().getServerDisplayName() + (currentBlockHeight != null ? " at height " + currentBlockHeight : "") + (Config.get().getServerType() == ServerType.PUBLIC_ELECTRUM_SERVER ? "\nWarning! You are connected to a public server and sharing your transaction data with it.\nFor better privacy, consider using your own Bitcoin Core node or private Electrum server." : ""); + } else if(AppServices.isConnecting()) { + return "Connecting..."; } return "Disconnected"; diff --git a/src/main/java/com/sparrowwallet/sparrow/AppServices.java b/src/main/java/com/sparrowwallet/sparrow/AppServices.java index 3585c389..9a58d3be 100644 --- a/src/main/java/com/sparrowwallet/sparrow/AppServices.java +++ b/src/main/java/com/sparrowwallet/sparrow/AppServices.java @@ -1139,10 +1139,12 @@ public class AppServices { @Subscribe public void requestDisconnect(RequestDisconnectEvent event) { onlineProperty.set(false); - //Ensure services don't try to reconnect - connectionService.cancel(); - ratesService.cancel(); - versionCheckService.cancel(); + //Ensure services don't try to reconnect later + Platform.runLater(() -> { + connectionService.cancel(); + ratesService.cancel(); + versionCheckService.cancel(); + }); } @Subscribe diff --git a/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java b/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java index 9532c1f6..dddfeb6c 100644 --- a/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java +++ b/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java @@ -1063,6 +1063,7 @@ public class ElectrumServer { private final ReentrantLock bwtStartLock = new ReentrantLock(); private final Condition bwtStartCondition = bwtStartLock.newCondition(); private Throwable bwtStartException; + private boolean shutdown; public ConnectionService() { this(true); @@ -1195,7 +1196,7 @@ public class ElectrumServer { } public boolean isConnecting() { - return isRunning() && Config.get().getServerType() == ServerType.BITCOIN_CORE && bwt.isRunning() && !bwt.isReady(); + return isRunning() && firstCall && (Config.get().getServerType() != ServerType.BITCOIN_CORE || (bwt.isRunning() && !bwt.isReady())); } public boolean isConnectionRunning() { @@ -1203,7 +1204,11 @@ public class ElectrumServer { } public boolean isConnected() { - return isRunning() && (Config.get().getServerType() != ServerType.BITCOIN_CORE || (bwt.isRunning() && bwt.isReady())); + return isRunning() && !firstCall && (Config.get().getServerType() != ServerType.BITCOIN_CORE || (bwt.isRunning() && bwt.isReady())); + } + + public boolean isShutdown() { + return shutdown; } @Override @@ -1219,6 +1224,11 @@ public class ElectrumServer { } private void shutdown() { + shutdown = true; + if(reader != null && reader.isAlive()) { + reader.interrupt(); + } + if(Config.get().getServerType() == ServerType.BITCOIN_CORE && bwt.isRunning()) { Bwt.DisconnectionService disconnectionService = bwt.getDisconnectionService(); disconnectionService.setOnSucceeded(workerStateEvent -> { diff --git a/src/main/java/com/sparrowwallet/sparrow/net/ProxyTcpOverTlsTransport.java b/src/main/java/com/sparrowwallet/sparrow/net/ProxyTcpOverTlsTransport.java index 781b8753..f828232f 100644 --- a/src/main/java/com/sparrowwallet/sparrow/net/ProxyTcpOverTlsTransport.java +++ b/src/main/java/com/sparrowwallet/sparrow/net/ProxyTcpOverTlsTransport.java @@ -29,13 +29,11 @@ public class ProxyTcpOverTlsTransport extends TcpOverTlsTransport { } @Override - protected Socket createSocket() throws IOException { + protected void createSocket() throws IOException { InetSocketAddress proxyAddr = new InetSocketAddress(proxy.getHost(), proxy.getPortOrDefault(DEFAULT_PROXY_PORT)); - Socket underlying = new Socket(new Proxy(Proxy.Type.SOCKS, proxyAddr)); - underlying.connect(new InetSocketAddress(server.getHost(), server.getPortOrDefault(DEFAULT_PORT))); - SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(underlying, proxy.getHost(), proxy.getPortOrDefault(DEFAULT_PROXY_PORT), true); - startHandshake(sslSocket); - - return sslSocket; + socket = new Socket(new Proxy(Proxy.Type.SOCKS, proxyAddr)); + socket.connect(new InetSocketAddress(server.getHost(), server.getPortOrDefault(DEFAULT_PORT))); + socket = sslSocketFactory.createSocket(socket, proxy.getHost(), proxy.getPortOrDefault(DEFAULT_PROXY_PORT), true); + startHandshake((SSLSocket)socket); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/net/TcpOverTlsTransport.java b/src/main/java/com/sparrowwallet/sparrow/net/TcpOverTlsTransport.java index e79fce82..a531576a 100644 --- a/src/main/java/com/sparrowwallet/sparrow/net/TcpOverTlsTransport.java +++ b/src/main/java/com/sparrowwallet/sparrow/net/TcpOverTlsTransport.java @@ -9,7 +9,7 @@ import javax.net.ssl.*; import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.net.Socket; +import java.net.InetSocketAddress; import java.security.*; import java.security.cert.*; import java.security.cert.Certificate; @@ -86,10 +86,10 @@ public class TcpOverTlsTransport extends TcpTransport { return trustManagerFactory.getTrustManagers(); } - protected Socket createSocket() throws IOException { - SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(server.getHost(), server.getPortOrDefault(DEFAULT_PORT)); - startHandshake(sslSocket); - return sslSocket; + protected void createSocket() throws IOException { + socket = sslSocketFactory.createSocket(); + socket.connect(new InetSocketAddress(server.getHost(), server.getPortOrDefault(DEFAULT_PORT))); + startHandshake((SSLSocket)socket); } protected void startHandshake(SSLSocket sslSocket) throws IOException { diff --git a/src/main/java/com/sparrowwallet/sparrow/net/TcpTransport.java b/src/main/java/com/sparrowwallet/sparrow/net/TcpTransport.java index 4394bd00..8bc91be8 100644 --- a/src/main/java/com/sparrowwallet/sparrow/net/TcpTransport.java +++ b/src/main/java/com/sparrowwallet/sparrow/net/TcpTransport.java @@ -13,6 +13,7 @@ import org.slf4j.LoggerFactory; import javax.net.SocketFactory; import javax.net.ssl.SSLHandshakeException; import java.io.*; +import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketTimeoutException; import java.nio.charset.StandardCharsets; @@ -36,7 +37,7 @@ public class TcpTransport implements CloseableTransport, TimeoutCounter { protected final SocketFactory socketFactory; protected final int[] readTimeouts; - private Socket socket; + protected Socket socket; private String response; @@ -231,7 +232,7 @@ public class TcpTransport implements CloseableTransport, TimeoutCounter { public void connect() throws ServerException { try { - socket = createSocket(); + createSocket(); log.debug("Created " + socket); socket.setSoTimeout(SOCKET_READ_TIMEOUT_MILLIS); running = true; @@ -250,8 +251,9 @@ public class TcpTransport implements CloseableTransport, TimeoutCounter { return socket != null && running && !closed; } - protected Socket createSocket() throws IOException { - return socketFactory.createSocket(server.getHost(), server.getPortOrDefault(DEFAULT_PORT)); + protected void createSocket() throws IOException { + socket = socketFactory.createSocket(); + socket.connect(new InetSocketAddress(server.getHost(), server.getPortOrDefault(DEFAULT_PORT))); } public boolean isClosed() { diff --git a/src/main/java/com/sparrowwallet/sparrow/net/TorTcpOverTlsTransport.java b/src/main/java/com/sparrowwallet/sparrow/net/TorTcpOverTlsTransport.java index c16cf03a..1b289201 100644 --- a/src/main/java/com/sparrowwallet/sparrow/net/TorTcpOverTlsTransport.java +++ b/src/main/java/com/sparrowwallet/sparrow/net/TorTcpOverTlsTransport.java @@ -26,9 +26,10 @@ public class TorTcpOverTlsTransport extends TcpOverTlsTransport { } @Override - protected Socket createSocket() throws IOException { + protected void createSocket() throws IOException { TorTcpTransport torTcpTransport = new TorTcpTransport(server); - Socket socket = torTcpTransport.createSocket(); + torTcpTransport.createSocket(); + socket = torTcpTransport.socket; try { Field socketField = socket.getClass().getDeclaredField("socket"); @@ -41,9 +42,7 @@ public class TorTcpOverTlsTransport extends TcpOverTlsTransport { log.error("Could not set socket connected status", e); } - SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(socket, server.getHost(), server.getPortOrDefault(DEFAULT_PORT), true); - startHandshake(sslSocket); - - return sslSocket; + socket = sslSocketFactory.createSocket(socket, server.getHost(), server.getPortOrDefault(DEFAULT_PORT), true); + startHandshake((SSLSocket)socket); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/net/TorTcpTransport.java b/src/main/java/com/sparrowwallet/sparrow/net/TorTcpTransport.java index 3669864f..8fe61adc 100644 --- a/src/main/java/com/sparrowwallet/sparrow/net/TorTcpTransport.java +++ b/src/main/java/com/sparrowwallet/sparrow/net/TorTcpTransport.java @@ -19,11 +19,11 @@ public class TorTcpTransport extends TcpTransport { } @Override - protected Socket createSocket() throws IOException { + protected void createSocket() throws IOException { if(!AppServices.isTorRunning()) { throw new IllegalStateException("Can't create Tor socket, Tor is not running"); } - return new TorSocket(server.getHost(), server.getPort(), "sparrow"); + socket = new TorSocket(server.getHost(), server.getPort(), "sparrow"); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/preferences/ServerPreferencesController.java b/src/main/java/com/sparrowwallet/sparrow/preferences/ServerPreferencesController.java index 407852da..cb7ff26f 100644 --- a/src/main/java/com/sparrowwallet/sparrow/preferences/ServerPreferencesController.java +++ b/src/main/java/com/sparrowwallet/sparrow/preferences/ServerPreferencesController.java @@ -175,6 +175,9 @@ public class ServerPreferencesController extends PreferencesDetailController { EventManager.get().register(this); getMasterController().closingProperty().addListener((observable, oldValue, newValue) -> { EventManager.get().unregister(this); + if(connectionService != null && connectionService.isRunning()) { + connectionService.cancel(); + } }); Platform.runLater(this::setupValidation); @@ -360,7 +363,7 @@ public class ServerPreferencesController extends PreferencesDetailController { boolean isConnected = AppServices.isConnecting() || AppServices.isConnected(); - if(AppServices.isConnecting()) { + if(Config.get().getServerType() == ServerType.BITCOIN_CORE && AppServices.isConnecting()) { testResults.appendText("Connecting to server, please wait..."); } @@ -380,7 +383,7 @@ public class ServerPreferencesController extends PreferencesDetailController { editConnection.managedProperty().bind(editConnection.visibleProperty()); editConnection.setVisible(isConnected); - editConnection.setDisable(AppServices.isConnecting()); + editConnection.setDisable(Config.get().getServerType() == ServerType.BITCOIN_CORE && AppServices.isConnecting()); editConnection.setOnAction(event -> { EventManager.get().post(new RequestDisconnectEvent()); setFieldsEditable(true); @@ -536,6 +539,11 @@ public class ServerPreferencesController extends PreferencesDetailController { }); connectionService.setOnFailed(workerStateEvent -> { EventManager.get().unregister(connectionService); + if(connectionService.isShutdown()) { + connectionService.cancel(); + return; + } + showConnectionFailure(workerStateEvent.getSource().getException()); connectionService.cancel(); diff --git a/src/main/java/com/sparrowwallet/sparrow/terminal/preferences/ServerTestDialog.java b/src/main/java/com/sparrowwallet/sparrow/terminal/preferences/ServerTestDialog.java index 8c7ad419..61324249 100644 --- a/src/main/java/com/sparrowwallet/sparrow/terminal/preferences/ServerTestDialog.java +++ b/src/main/java/com/sparrowwallet/sparrow/terminal/preferences/ServerTestDialog.java @@ -95,6 +95,9 @@ public class ServerTestDialog extends DialogWindow { close(); Platform.runLater(() -> { + if(connectionService != null && connectionService.isRunning()) { + connectionService.cancel(); + } if(Config.get().getMode() == Mode.ONLINE && !(AppServices.isConnecting() || AppServices.isConnected())) { EventManager.get().post(new RequestConnectEvent()); } @@ -145,6 +148,11 @@ public class ServerTestDialog extends DialogWindow { }); connectionService.setOnFailed(workerStateEvent -> { EventManager.get().unregister(connectionService); + if(connectionService.isShutdown()) { + connectionService.cancel(); + return; + } + showConnectionFailure(workerStateEvent.getSource().getException()); connectionService.cancel(); });