@@ -74,13 +74,15 @@ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
7474import java .io .*;
7575import java .net .*;
7676import java .util .Calendar ;
77- import java .util .concurrent .CountDownLatch ;
77+ import java .util .concurrent .SynchronousQueue ;
7878import java .util .concurrent .TimeUnit ;
7979
8080public class TorOnionProxySmokeTest extends TorOnionProxyTestCase {
8181 private static final int CONNECT_TIMEOUT_MILLISECONDS = 60000 ;
8282 private static final int READ_TIMEOUT_MILLISECONDS = 60000 ;
83- private static final int TOTAL_MINUTES_FOR_TEST_TO_RUN = 3 ;
83+ private static final int TOTAL_SECONDS_PER_TOR_STARTUP = 4 * 60 ;
84+ private static final int TOTAL_TRIES_PER_TOR_STARTUP = 5 ;
85+ private static final int WAIT_FOR_HIDDEN_SERVICE_MINUTES = 3 ;
8486 private static final Logger LOG = LoggerFactory .getLogger (TorOnionProxySmokeTest .class );
8587
8688 /**
@@ -96,18 +98,24 @@ public void testHiddenServiceRecycleTime() throws IOException, InterruptedExcept
9698 try {
9799 hiddenServiceManager = getOnionProxyManager (hiddenServiceManagerDirectoryName );
98100 deleteTorWorkingDirectory (hiddenServiceManager .getWorkingDirectory ());
99- assertTrue (hiddenServiceManager .startWithRepeat (30 , 5 ));
101+ assertTrue (hiddenServiceManager .startWithRepeat (TOTAL_SECONDS_PER_TOR_STARTUP , TOTAL_TRIES_PER_TOR_STARTUP ));
102+
103+ LOG .warn ("Hidden Service Manager is running." );
100104
101105 clientManager = getOnionProxyManager (clientManagerDirectoryName );
102106 deleteTorWorkingDirectory (clientManager .getWorkingDirectory ());
103- assertTrue (clientManager .startWithRepeat (30 , 5 ));
107+ assertTrue (clientManager .startWithRepeat (TOTAL_SECONDS_PER_TOR_STARTUP , TOTAL_TRIES_PER_TOR_STARTUP ));
108+
109+ LOG .warn ("Client Manager is running." );
104110
105111 String onionAddress = runHiddenServiceTest (hiddenServiceManager , clientManager );
106112
113+ LOG .warn ("We successfully sent a message from client manager to hidden service manager!" );
114+
107115 // Now take down the hidden service manager and bring it back up with a new descriptor but the
108116 // same address
109117 hiddenServiceManager .stop ();
110- hiddenServiceManager .startWithRepeat (30 , 5 );
118+ hiddenServiceManager .startWithRepeat (TOTAL_SECONDS_PER_TOR_STARTUP , TOTAL_TRIES_PER_TOR_STARTUP );
111119 // It's possible that one of our deletes could have nuked the hidden service directory
112120 // in which case we would actually be testing against a new hidden service which would
113121 // remove the point of this test. So we check that they are the same.
@@ -122,6 +130,8 @@ public void testHiddenServiceRecycleTime() throws IOException, InterruptedExcept
122130 }
123131 }
124132
133+ public enum ServerState { success , timedout , othererror }
134+
125135 private String runHiddenServiceTest (OnionProxyManager hiddenServiceManager , OnionProxyManager clientManager )
126136 throws IOException , InterruptedException {
127137 int localPort = 9343 ;
@@ -131,16 +141,32 @@ private String runHiddenServiceTest(OnionProxyManager hiddenServiceManager, Onio
131141
132142 byte [] testBytes = new byte [] { 0x01 , 0x02 , 0x03 , 0x05 };
133143
134- CountDownLatch countDownLatch = receiveExpectedBytes (testBytes , localPort );
135-
136- Socket clientSocket =
137- getClientSocket (onionAddress , hiddenServicePort , clientManager .getIPv4LocalHostSocksPort ());
138-
139- DataOutputStream clientOutputStream = new DataOutputStream (clientSocket .getOutputStream ());
140- clientOutputStream .write (testBytes );
141- clientOutputStream .flush ();
142- assertTrue (countDownLatch .await (TOTAL_MINUTES_FOR_TEST_TO_RUN , TimeUnit .MINUTES ));
143- return onionAddress ;
144+ long timeToExit = Calendar .getInstance ().getTimeInMillis () + WAIT_FOR_HIDDEN_SERVICE_MINUTES * 60 * 1000 ;
145+ while (Calendar .getInstance ().getTimeInMillis () < timeToExit ) {
146+ SynchronousQueue <ServerState > serverQueue = new SynchronousQueue <ServerState >();
147+ Thread serverThread = receiveExpectedBytes (testBytes , localPort , serverQueue );
148+
149+ Socket clientSocket =
150+ getClientSocket (onionAddress , hiddenServicePort , clientManager .getIPv4LocalHostSocksPort ());
151+
152+ DataOutputStream clientOutputStream = new DataOutputStream (clientSocket .getOutputStream ());
153+ clientOutputStream .write (testBytes );
154+ clientOutputStream .flush ();
155+ ServerState serverState = serverQueue .poll (WAIT_FOR_HIDDEN_SERVICE_MINUTES , TimeUnit .MINUTES );
156+ if (serverState == ServerState .success ) {
157+ return onionAddress ;
158+ } else {
159+ long timeForThreadToExit = Calendar .getInstance ().getTimeInMillis () + 1000 ;
160+ while (Calendar .getInstance ().getTimeInMillis () < timeForThreadToExit &&
161+ serverThread .getState () != Thread .State .TERMINATED ) {
162+ Thread .sleep (1000 ,0 );
163+ }
164+ if (serverThread .getState () != Thread .State .TERMINATED ) {
165+ throw new RuntimeException ("Server thread doesn't want to terminate and free up our port!" );
166+ }
167+ }
168+ }
169+ throw new RuntimeException ("Test timed out!" );
144170 }
145171
146172 /**
@@ -152,11 +178,13 @@ private String runHiddenServiceTest(OnionProxyManager hiddenServiceManager, Onio
152178 */
153179 private Socket getClientSocket (String onionAddress , int hiddenServicePort , int socksPort )
154180 throws InterruptedException {
155- long timeToExit = Calendar .getInstance ().getTimeInMillis () + TOTAL_MINUTES_FOR_TEST_TO_RUN *60 *1000 ;
181+ long timeToExit = Calendar .getInstance ().getTimeInMillis () + WAIT_FOR_HIDDEN_SERVICE_MINUTES *60 *1000 ;
156182 Socket clientSocket = null ;
157183 while (Calendar .getInstance ().getTimeInMillis () < timeToExit && clientSocket == null ) {
158184 try {
159185 clientSocket = socks4aSocketConnection (onionAddress , hiddenServicePort , "127.0.0.1" , socksPort );
186+ clientSocket .setTcpNoDelay (true );
187+ LOG .info ("We connected via the clientSocket to try and talk to the hidden service." );
160188 } catch (IOException e ) {
161189 LOG .error ("attempt to set clientSocket failed, will retry" , e );
162190 Thread .sleep (5000 , 0 );
@@ -170,29 +198,46 @@ private Socket getClientSocket(String onionAddress, int hiddenServicePort, int s
170198 return clientSocket ;
171199 }
172200
173- private CountDownLatch receiveExpectedBytes (final byte [] expectedBytes , int localPort ) throws IOException {
201+ private Thread receiveExpectedBytes (final byte [] expectedBytes , int localPort ,
202+ final SynchronousQueue <ServerState > serverQueue ) throws IOException {
174203 final ServerSocket serverSocket = new ServerSocket (localPort );
175- final CountDownLatch countDownLatch = new CountDownLatch (1 );
176204
177- new Thread (new Runnable () {
205+ Thread thread = new Thread (new Runnable () {
178206 public void run () {
179207 Socket receivedSocket = null ;
180208 try {
181209 receivedSocket = serverSocket .accept ();
210+ // Yes, setTcpNoDelay is useless because we are just reading but I'm being paranoid
211+ receivedSocket .setTcpNoDelay (true );
212+ receivedSocket .setSoTimeout (10 *1000 );
182213 LOG .info ("Received incoming connection" );
183214 DataInputStream dataInputStream = new DataInputStream (receivedSocket .getInputStream ());
184215 for (byte nextByte : expectedBytes ) {
185216 byte receivedByte = dataInputStream .readByte ();
186217 if (nextByte != receivedByte ) {
187218 LOG .error ("Received " + receivedByte + ", but expected " + nextByte );
219+ serverQueue .put (ServerState .othererror );
188220 return ;
189221 } else {
190222 LOG .info ("Received " + receivedByte );
191223 }
192224 }
193- countDownLatch .countDown ();
225+ LOG .info ("All Bytes Successfully Received!" );
226+ serverQueue .put (ServerState .success );
194227 } catch (IOException e ) {
228+ LOG .warn ("We got an io exception waiting for the server bytes, this really shouldn't happen, but does." , e );
229+ try {
230+ serverQueue .put (ServerState .timedout );
231+ } catch (InterruptedException e1 ) {
232+ LOG .error ("We couldn't send notice that we had a server time out! EEEK!" );
233+ }
234+ } catch (InterruptedException e ) {
195235 LOG .error ("Test Failed" , e );
236+ try {
237+ serverQueue .put (ServerState .othererror );
238+ } catch (InterruptedException e1 ) {
239+ LOG .error ("We got an InterruptedException and couldn't tell the server queue about it!" , e1 );
240+ }
196241 } finally {
197242 // I suddenly am getting IncompatibleClassChangeError: interface no implemented when
198243 // calling these functions. I saw a random Internet claim (therefore it must be true!)
@@ -210,9 +255,9 @@ public void run() {
210255 }
211256 }
212257 }
213- }). start () ;
214-
215- return countDownLatch ;
258+ });
259+ thread . start ();
260+ return thread ;
216261 }
217262
218263 private void deleteTorWorkingDirectory (File torWorkingDirectory ) {
0 commit comments