66import java .io .IOException ;
77import java .util .Timer ;
88import java .util .TimerTask ;
9+ import java .util .UUID ;
910import java .util .logging .Level ;
1011import java .util .logging .Logger ;
1112
@@ -17,7 +18,8 @@ public class SSHTunnel {
1718 private final App app ;
1819 private final Connection conn ;
1920 private final String server ;
20- private Timer timer ;
21+ private final String connectionId ;
22+ private Timer keepAliveTimer ;
2123 private boolean authenticated = false ;
2224 private boolean shuttingDown = false ;
2325 private LocalPortForwarder lpf1 ;
@@ -26,6 +28,7 @@ public SSHTunnel(App app, String server) throws Exception {
2628 /* Create a connection instance */
2729 this .app = app ;
2830 this .server = server ;
31+ this .connectionId = UUID .randomUUID ().toString ().substring (0 , 8 );
2932
3033 this .conn = new Connection (server , 443 );
3134 this .conn .addConnectionMonitor (new CustomConnectionMonitor (this , this .app ));
@@ -35,27 +38,36 @@ public SSHTunnel(App app, String server) throws Exception {
3538 public final void connect () throws Exception {
3639 try {
3740 /* Now connect */
41+ long startTime = System .currentTimeMillis ();
3842 conn .connect ();
43+ long connectTime = System .currentTimeMillis () - startTime ;
44+
45+ Logger .getLogger (SSHTunnel .class .getName ()).log (Level .INFO ,
46+ String .format ("[%s] Secure connection established in %dms" , connectionId , connectTime ));
3947 } catch (IOException ex ) {
40- Logger .getLogger (SSHTunnel .class .getName ()).log (Level .SEVERE , ex .getMessage (), ex );
48+ Logger .getLogger (SSHTunnel .class .getName ()).log (Level .SEVERE ,
49+ String .format ("[%s] Connection failed: %s" , connectionId , ex .getMessage ()), ex );
50+ throw ex ;
4151 }
4252
4353 try {
4454 // authenticate
4555 this .authenticated = conn .authenticateWithPassword (app .getClientKey (), app .getClientSecret ());
4656 } catch (IOException ex ) {
47- Logger .getLogger (SSHTunnel .class .getName ()).log (Level .SEVERE , "Failed authenticating to the tunnel. Please make sure you are supplying correct login credentials." );
57+ Logger .getLogger (SSHTunnel .class .getName ()).log (Level .SEVERE ,
58+ String .format ("[%s] Failed authenticating to the tunnel. Please make sure you are supplying correct login credentials." , connectionId ));
4859 throw new Exception ("Authentication failed: " + ex .getMessage ());
4960 }
5061
51-
5262 if (!this .authenticated ) {
53- Logger .getLogger (SSHTunnel .class .getName ()).log (Level .SEVERE , "Failed authenticating to the tunnel. Please make sure you are supplying correct login credentials." );
63+ Logger .getLogger (SSHTunnel .class .getName ()).log (Level .SEVERE ,
64+ String .format ("[%s] Failed authenticating to the tunnel. Please make sure you are supplying correct login credentials." , connectionId ));
5465 throw new Exception ("Authentication failed" );
5566 }
5667
57- timer = new Timer ();
58- timer .schedule (new PollTask (), 60000 , 60000 );
68+ // Start keep-alive timer with configurable interval
69+ keepAliveTimer = new Timer ("KeepAlive-" + connectionId );
70+ keepAliveTimer .schedule (new KeepAliveTask (), 30000 , 30000 );
5971 }
6072
6173 public void stop (boolean quitting ) {
@@ -64,14 +76,19 @@ public void stop(boolean quitting) {
6476 }
6577
6678 public void stop () {
67- timer .cancel ();
79+ Logger .getLogger (SSHTunnel .class .getName ()).log (Level .INFO , String .format ("[%s] Stopping secure tunnel" ,connectionId ));
80+ if (keepAliveTimer != null ) {
81+ keepAliveTimer .cancel ();
82+ }
83+
6884 conn .close ();
6985 try {
7086 if (lpf1 != null ) {
7187 lpf1 .close ();
7288 }
7389 } catch (IOException ex ) {
74- Logger .getLogger (SSHTunnel .class .getName ()).log (Level .SEVERE , null , ex );
90+ Logger .getLogger (SSHTunnel .class .getName ()).log (Level .SEVERE ,
91+ String .format ("[%s] Error closing port forwarder: %s" , connectionId , ex .getMessage ()), ex );
7592 }
7693 }
7794
@@ -81,30 +98,44 @@ public void createPortForwarding() {
8198 conn .requestRemotePortForwarding (server , 2010 , "0.0.0.0" , app .getJettyPort ());
8299 String hubHost = "hub.testingbot.com" ;
83100 lpf1 = conn .createLocalPortForwarder (app .getSSHPort (), hubHost , app .getHubPort ());
101+
102+ Logger .getLogger (SSHTunnel .class .getName ()).log (Level .INFO ,
103+ String .format ("[%s] Port forwarding established: %s:2010 -> localhost:%d, localhost:%d -> %s:%d" ,
104+ connectionId , server , app .getJettyPort (), app .getSSHPort (), hubHost , app .getHubPort ()));
84105 } catch (IOException ex ) {
85- Logger .getLogger (SSHTunnel .class .getName ()).log (Level .SEVERE , "Could not setup port forwarding. Please make sure we can make an outbound connection to port 2010." );
106+ Logger .getLogger (SSHTunnel .class .getName ()).log (Level .SEVERE ,
107+ String .format ("[%s] Could not setup port forwarding. Please make sure we can make an outbound connection to port 2010." , connectionId ), ex );
86108 }
87109 }
88110
89111 public boolean isShuttingDown () {
90112 return shuttingDown ;
91113 }
92114
115+ public String getConnectionId () {
116+ return connectionId ;
117+ }
118+
93119 /**
94120 * @return the authenticated
95121 */
96122 public boolean isAuthenticated () {
97123 return authenticated ;
98124 }
99125
100- class PollTask extends TimerTask {
126+ class KeepAliveTask extends TimerTask {
101127 @ Override
102128 public void run () {
103129 try {
104- // keep-alive attempt
130+ long startTime = System . currentTimeMillis ();
105131 conn .sendIgnorePacket ();
132+ long roundTripTime = System .currentTimeMillis () - startTime ;
133+
134+ Logger .getLogger (SSHTunnel .class .getName ()).log (Level .FINE ,
135+ String .format ("[%s] Keep-alive sent, RTT: %dms" , connectionId , roundTripTime ));
106136 } catch (IOException ex ) {
107- // ignore?
137+ Logger .getLogger (SSHTunnel .class .getName ()).log (Level .WARNING ,
138+ String .format ("[%s] Keep-alive failed: %s" , connectionId , ex .getMessage ()));
108139 }
109140 }
110141 }
0 commit comments