Skip to content

Commit

Permalink
Add selftest
Browse files Browse the repository at this point in the history
  • Loading branch information
RetGal committed Dec 25, 2024
1 parent eb6b0eb commit e2a5093
Show file tree
Hide file tree
Showing 19 changed files with 130 additions and 26 deletions.
37 changes: 26 additions & 11 deletions src/main/java/mpo/dayon/assistant/gui/Assistant.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import static java.lang.String.valueOf;
import static javax.swing.SwingConstants.HORIZONTAL;
import static mpo.dayon.common.babylon.Babylon.translate;
import static mpo.dayon.common.configuration.Configuration.DEFAULT_TOKEN_SERVER_URL;
import static mpo.dayon.common.gui.common.ImageUtilities.getOrCreateIcon;
import static mpo.dayon.common.utils.SystemUtilities.*;

Expand Down Expand Up @@ -250,17 +251,6 @@ public void actionPerformed(ActionEvent ev) {
final Point toolbarLocation = frame.getToolBar().getLocationOnScreen();
choices.setLocation(frameLocation.x + 20, toolbarLocation.y + frame.getToolBar().getHeight());
}

private void resolvePublicIp() throws IOException, InterruptedException {
// HttpClient doesn't implement AutoCloseable nor close before Java 21!
@java.lang.SuppressWarnings("squid:S2095")
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(WHATSMYIP_SERVER_URL))
.timeout(Duration.ofSeconds(5))
.build();
publicIp = client.send(request, HttpResponse.BodyHandlers.ofString()).body().trim();
}
};
ip.putValue("DISPLAY_NAME", publicIp); // always a selection
// ...
Expand All @@ -269,6 +259,17 @@ private void resolvePublicIp() throws IOException, InterruptedException {
return ip;
}

private void resolvePublicIp() throws IOException, InterruptedException {
// HttpClient doesn't implement AutoCloseable nor close before Java 21!
@java.lang.SuppressWarnings("squid:S2095")
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(WHATSMYIP_SERVER_URL))
.timeout(Duration.ofSeconds(5))
.build();
publicIp = client.send(request, HttpResponse.BodyHandlers.ofString()).body().trim();
}

private JMenuItem getJMenuItemCopyIpAndPort(JButton button) {
final JMenuItem menuItem = new JMenuItem(translate("copy.msg"));
menuItem.addActionListener(ev12 -> {
Expand Down Expand Up @@ -563,6 +564,7 @@ public void actionPerformed(ActionEvent ev) {
return token;
}).thenAcceptAsync(tokenString -> {
if (tokenString != null) {
token = tokenString;
button.setText(format(" %s", tokenString));
button.setToolTipText(translate("token.copy.msg"));
}
Expand Down Expand Up @@ -684,6 +686,19 @@ protected String doInBackground() {

private void startNetwork() {
frame.onGettingReady();
if (publicIp == null) {
try {
resolvePublicIp();
} catch (IOException | InterruptedException ex) {
Log.error("Could not determine public IP", ex);
if (ex instanceof InterruptedException) {
Thread.currentThread().interrupt();
}
}
}
if (!networkEngine.selfTest(publicIp)) {
JOptionPane.showMessageDialog(frame, translate("port.error.msg1", networkConfiguration.getPort()), translate("port.error"), JOptionPane.WARNING_MESSAGE);
}
networkEngine.start(compatibilityModeActive.get());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.DataFlavor;
import java.io.*;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
Expand Down Expand Up @@ -66,14 +68,32 @@ public void addListener(NetworkAssistantEngineListener listener) {
listeners.add(listener);
}

public boolean selfTest(String publicIp) {
if (publicIp == null) {
return false;
}
if (!manageRouterPorts(0, configuration.getPort())) {
try (ServerSocket listener = new ServerSocket(configuration.getPort())) {
try (Socket socket = new Socket()) {
socket.connect(new InetSocketAddress(publicIp, configuration.getPort()), 1000);
}
Log.info("Port " + configuration.getPort() + " is reachable from the outside");
} catch (IOException e) {
Log.error("Port " + configuration.getPort() + " is not reachable from the outside");
return false;
}
}
return true;
}

/**
* Called from a GUI action => do not block the AWT thread (!)
*/
public void start(boolean compatibilityMode) {
if (cancelling.get() || receiver != null) {
return;
}
manageRouterPorts(0, configuration.getPort());

receiver = new Thread(new RunnableEx() {
@Override
protected void doRun() throws NoSuchAlgorithmException, KeyManagementException {
Expand All @@ -93,9 +113,9 @@ public void cancel() {
fireOnDisconnecting();
}

public static void manageRouterPorts(int oldPort, int newPort) {
public static boolean manageRouterPorts(int oldPort, int newPort) {
if (!UPnP.isUPnPAvailable()) {
return;
return false;
}
if (oldPort != 0 && UPnP.isMappedTCP(oldPort)) {
UPnP.closePortTCP(oldPort);
Expand All @@ -104,10 +124,12 @@ public static void manageRouterPorts(int oldPort, int newPort) {
if (!UPnP.isMappedTCP(newPort)) {
if (UPnP.openPortTCP(newPort, APP_NAME)) {
Log.info(format("Enabled forwarding for port %d", newPort));
return;
return true;
}
Log.warn(format("Failed to enable forwarding for port %d", newPort));
return false;
}
return true;
}

// right, keep streams open - forever!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@

import java.util.Objects;

import static mpo.dayon.common.utils.SystemUtilities.DEFAULT_TOKEN_SERVER_URL;

public class NetworkAssistantEngineConfiguration extends Configuration {
private static final String PREF_VERSION = "assistant.network.version";

Expand Down
12 changes: 8 additions & 4 deletions src/main/java/mpo/dayon/assisted/gui/Assisted.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import static java.lang.String.format;
import static java.lang.String.valueOf;
import static mpo.dayon.common.babylon.Babylon.translate;
import static mpo.dayon.common.configuration.Configuration.DEFAULT_TOKEN_SERVER_URL;
import static mpo.dayon.common.gui.common.ImageUtilities.getOrCreateIcon;
import static mpo.dayon.common.utils.SystemUtilities.*;

Expand All @@ -62,6 +63,8 @@ public class Assisted implements Subscriber, ClipboardOwner {

private final AtomicBoolean shareAllScreens = new AtomicBoolean(false);

private String token;

public Assisted(String tokenServerUrl) {
networkConfiguration = new NetworkAssistedEngineConfiguration();

Expand Down Expand Up @@ -144,7 +147,7 @@ private boolean configureConnection(String serverName, String portNumber, boolea

private boolean requestConnectionSettings() {
networkConfiguration = new NetworkAssistedEngineConfiguration();
ConnectionSettingsDialog connectionSettingsDialog = new ConnectionSettingsDialog(networkConfiguration);
ConnectionSettingsDialog connectionSettingsDialog = new ConnectionSettingsDialog(networkConfiguration, token);

final boolean ok = DialogFactory.showOkCancel(frame, translate("connection.settings"), connectionSettingsDialog.getTabbedPane(), false, () -> {
final String token = connectionSettingsDialog.getToken().trim();
Expand Down Expand Up @@ -186,8 +189,9 @@ private static String validatePortNumber(String portNumber) {
private void applyConnectionSettings(ConnectionSettingsDialog connectionSettingsDialog) {
CompletableFuture.supplyAsync(() -> {
final NetworkAssistedEngineConfiguration newConfiguration;
String token = connectionSettingsDialog.getToken().trim();
if (!token.isEmpty()) {
String tokenString = connectionSettingsDialog.getToken().trim();
if (!tokenString.isEmpty()) {
this.token = tokenString;
final Cursor cursor = frame.getCursor();
frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
String connectionParams = null;
Expand All @@ -212,6 +216,7 @@ private void applyConnectionSettings(ConnectionSettingsDialog connectionSettings
networkConfiguration = newConfiguration;
networkConfiguration.persist();
networkEngine.configure(networkConfiguration);
frame.onConnecting(networkConfiguration.getServerName(), networkConfiguration.getServerPort());
}
Log.info("NetworkConfiguration " + networkConfiguration);
});
Expand Down Expand Up @@ -265,7 +270,6 @@ private class NetWorker extends SwingWorker<String, String> {
@Override
protected String doInBackground() {
if (isConfigured() && !isCancelled()) {
frame.onConnecting(networkConfiguration.getServerName(), networkConfiguration.getServerPort());
networkEngine.configure(networkConfiguration);
networkEngine.connect();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class ConnectionSettingsDialog {

private final JTextField assistantTokenTextField;

ConnectionSettingsDialog(NetworkAssistedEngineConfiguration configuration) {
ConnectionSettingsDialog(NetworkAssistedEngineConfiguration configuration, String token) {

JPanel connectionSettingsDialog = new JPanel(new GridLayout(2, 2, 10, 10));
connectionSettingsDialog.setBorder(BorderFactory.createEmptyBorder(15, 0, 0, 0));
Expand All @@ -46,7 +46,7 @@ class ConnectionSettingsDialog {
connectionTokenDialog.setBorder(BorderFactory.createEmptyBorder(15, 0, 0, 0));

final JLabel assistantToken = new JLabel(getOrCreateIcon(ImageNames.KEY));
assistantTokenTextField = new JTextField("", 7);
assistantTokenTextField = new JTextField(token, 7);
assistantTokenTextField.setMargin(new Insets(2,2,2,2));
assistantTokenTextField.setFont(new Font("Sans Serif", Font.PLAIN, 26));
assistantTokenTextField.addMouseListener(clearTextOnDoubleClick(assistantTokenTextField));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@

import java.util.Objects;

import static mpo.dayon.common.utils.SystemUtilities.DEFAULT_TOKEN_SERVER_URL;

public class NetworkAssistedEngineConfiguration extends Configuration {
private static final String PREF_VERSION = "assisted.network.version";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
package mpo.dayon.common.configuration;

public abstract class Configuration {
public static final String DEFAULT_TOKEN_SERVER_URL = "https://fensterkitt.ch/dayon/";

public final String getDefaultTokenServerUrl() {
return DEFAULT_TOKEN_SERVER_URL;
}

public final void persist() {
persist(false);
}
Expand Down
1 change: 1 addition & 0 deletions src/main/java/mpo/dayon/common/gui/common/BaseFrame.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import static java.awt.GridBagConstraints.HORIZONTAL;
import static java.lang.String.format;
import static mpo.dayon.common.babylon.Babylon.translate;
import static mpo.dayon.common.configuration.Configuration.DEFAULT_TOKEN_SERVER_URL;
import static mpo.dayon.common.gui.common.ImageNames.FINGERPRINT;
import static mpo.dayon.common.gui.common.ImageUtilities.getOrCreateIcon;
import static mpo.dayon.common.gui.toolbar.ToolBar.*;
Expand Down
1 change: 0 additions & 1 deletion src/main/java/mpo/dayon/common/utils/SystemUtilities.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ public final class SystemUtilities {
public static final String JAVA_CLASS_PATH = "java.class.path";
public static final String FLATPAK_BROWSER = "/app/bin/dayon.browser";
private static final String JAVA_VENDOR = "java.vendor";
public static final String DEFAULT_TOKEN_SERVER_URL = "https://fensterkitt.ch/dayon/";
private static final Pattern FQ_HOSTNAME_REGEX = Pattern.compile("^([a-zA-Z\\d][a-zA-Z\\d\\-]{0,61}[a-zA-Z\\d]\\.)*[a-zA-Z]{2,}$");
private static final Pattern IPV4_REGEX = Pattern.compile("(\\d{1,3})");

Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/Babylon.properties
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ keyboard.error.msg3 = You should use the same input language on both computers.
comm.error = Communication error
comm.error.msg1 = The program has encountered a communication error [%s].

port.error = Warning
port.error.msg1 = Port %s is not reachable from the internet.\nPeers from outside your network will not be able to connect.

# Connection ...

connected = Connected - happy session!
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/Babylon_de.properties
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ keyboard.error.msg3 = Sie sollten auf beiden Rechnern dieselbe Eingabesprache nu
comm.error = Kommunikationsfehler
comm.error.msg1 = Es ist ein Kommunikationsfelher aufgetreten [%s].

port.error = Warnung
port.error.msg1 = Der Port %s ist vom Internet her nicht erreichbar.\nComputer ausserhalb ihres Netzwerks werden sich nicht verbinden k\u00F6nnen.

# Connection ...

connected = Verbunden - Happy Session!
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/Babylon_es.properties
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ keyboard.error.msg3 = Deber\u00EDas usar el mismo idioma de entrada en las dos c
comm.error = Error de comunicaci\u00F3n
comm.error.msg1 = El programa ha tenido un error de comunicaci\u00F3n [%s].

port.error = Aviso de advertencia
port.error.msg1 = El puerto %s no es accesible desde Internet.\nLos pares de fuera de su red no podr\u00E1n conectarse.

# Connection ...

connected = Conectado - \u00A1Feliz sesi\u00F3n!
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/Babylon_fr.properties
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ keyboard.error.msg3 = Vous devez utiliser la m\u00EAme langue de clavier sur les
comm.error = Erreur de Communication
comm.error.msg1 = Une erreur de communication est survenue dans l'application [%s].

port.error = Attention
port.error.msg1 = Le port X n'est pas accessible depuis l'internet.\nLes homologues ext\u00E9rieurs \u00E0 votre r\u00E9seau ne pourront pas se connecter.

# Connection ...

connected = Connect\u00E9 - bonne session!
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/Babylon_it.properties
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ keyboard.error.msg3 = Dovresti usare la stessa lingua di input su entrambi i com
comm.error = Errore di comunicazione
comm.error.msg1 = Il programma ha riscontrato un errore di comunicazione [%s].

port.error = Attenzione
port.error.msg1 = La porta %s non \u00E8 raggiungibile da Internet.\nI peer esterni alla rete non potranno connettersi.

# Connection ...

connected = Connesso - buona sessione!
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/Babylon_ru.properties
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ keyboard.error.msg3 = \u0412\u044B \u0434\u043E\u043B\u0436\u043D\u044B \u0438\u
comm.error = \u041E\u0448\u0438\u0431\u043A\u0430 \u0441\u0432\u044F\u0437\u0438
comm.error.msg1 = \u041F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u0430 \u043E\u0431\u043D\u0430\u0440\u0443\u0436\u0438\u043B\u0430 \u043E\u0448\u0438\u0431\u043A\u0443 \u0441\u0432\u044F\u0437\u0438 [%s].

port.error = \u041F\u0440\u0435\u0434\u0443\u043F\u0440\u0435\u0436\u0434\u0435\u043D\u0438\u0435
port.error.msg1 = \u041F\u043E\u0440\u0442 %s \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D \u0438\u0437 \u0418\u043D\u0442\u0435\u0440\u043D\u0435\u0442\u0430.\n\u041F\u0438\u0440\u0438\u043D\u0433\u0438, \u043D\u0430\u0445\u043E\u0434\u044F\u0449\u0438\u0435\u0441\u044F \u0437\u0430 \u043F\u0440\u0435\u0434\u0435\u043B\u0430\u043C\u0438 \u0432\u0430\u0448\u0435\u0439 \u0441\u0435\u0442\u0438, \u043D\u0435 \u0441\u043C\u043E\u0433\u0443\u0442 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0438\u0442\u044C\u0441\u044F.

# Connection ...

connected = \u041F\u043E\u0434\u043A\u043B\u044E\u0447\u0438\u043B\u0441\u044F - \u0443\u0434\u0430\u0447\u043D\u043E\u0439 \u0441\u0435\u0441\u0441\u0438\u0438!
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/Babylon_sv.properties
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ keyboard.error.msg3 = Undvik olika tangentbordsspr\u00E5k p\u00E5 datorerna.
comm.error = Kommunikationsfel
comm.error.msg1 = Programmet har st\u00F6tt p\u00E5 ett kommunikationsfel [%s].

port.error = Varning
port.error.msg1 = Port %s \u00E4r inte n\u00E5bar fr\u00E5n internet. Motparter utanf\u00F6r ditt n\u00E4tverk kommer inte att kunna ansluta.

# Connection ...

connected = Ansluten - Lycka till!
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/Babylon_tr.properties
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ keyboard.error.msg3 = \u0130ki bilgisayarda da ayn\u0131 giri\u015F dilini kulla
comm.error = Ileti\u015Fim hatas\u0131
comm.error.msg1 = Program ileti\u015Fim hatas\u0131yla kar\u015F\u0131la\u015Ft\u0131 [%s].

port.error = Uyar\u0131
port.error.msg1 = Port %s internetten eri\u015Filebilir de\u011Fil.\nA\u011F\u0131n\u0131z\u0131n d\u0131\u015F\u0131ndan gelen e\u015Fler ba\u011Flanamayacakt\u0131r.

# Connection ...

connected = Ba\u011Fland\u0131 - mutlu ba\u011Flant\u0131lar!
Expand Down
5 changes: 5 additions & 0 deletions src/main/resources/Babylon_zh.properties
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ comm.error = \u4F1A\u8BDD\u4E2D\u65AD
# The program has encountered a communication error [%s].
comm.error.msg1 = \u8FDE\u63A5\u88AB\u4E2D\u65AD\u4E86 [%s]

# Warning
port.error = \u8B66\u544A
# Port %s is not reachable from the internet.\nPeers from outside your network will not be able to connect.
port.error.msg1 = \u7AEF\u53E3 %s \u65E0\u6CD5\u4ECE\u4E92\u8054\u7F51\u8BBF\u95EE\u3002\u7F51\u7EDC\u5916\u90E8\u7684\u5BF9\u7B49\u65B9\u5C06\u65E0\u6CD5\u8FDE\u63A5\u3002

# Connection ...

# Connected - happy session!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.*;

class NetworkAssistantEngineTest {
Expand Down Expand Up @@ -41,4 +43,34 @@ void testCancel() {
// then
verify(listener).onDisconnecting();
}

@Test
void selfTestShouldFailIfPublicIpIsNull() {
// given
String publicIp = null;
engine.configure(new NetworkAssistantEngineConfiguration(12345, ""));

// when // then
assertFalse(engine.selfTest(publicIp));
}

@Test
void selfTestShouldFailForUnreachablePort() {
// given
String publicIp = "1.2.3.4";
engine.configure(new NetworkAssistantEngineConfiguration(5, ""));

// when // then
assertFalse(engine.selfTest(publicIp));
}

@Test
void selfTestShouldSucceedForReachablePort() {
// given
String publicIp = "127.0.0.1";
engine.configure(new NetworkAssistantEngineConfiguration(12345, ""));

// when // then
assertTrue(engine.selfTest(publicIp));
}
}

0 comments on commit e2a5093

Please sign in to comment.