diff --git a/src/main/java/org/java_websocket/client/WebSocketClient.java b/src/main/java/org/java_websocket/client/WebSocketClient.java index 52dbe61e3..cd9e16fc0 100644 --- a/src/main/java/org/java_websocket/client/WebSocketClient.java +++ b/src/main/java/org/java_websocket/client/WebSocketClient.java @@ -93,6 +93,11 @@ public abstract class WebSocketClient extends AbstractWebSocket implements Runna */ private Thread writeThread; + /** + * The thread to connect and read message + */ + private Thread connectReadThread; + /** * The draft to use */ @@ -239,12 +244,20 @@ public boolean reconnectBlocking() throws InterruptedException { * @since 1.3.8 */ private void reset() { + Thread current = Thread.currentThread(); + if (current == writeThread || current == connectReadThread) { + throw new IllegalStateException("You cannot initialize a reconnect out of the websocket thread. Use reconnect in another thread to insure a successful cleanup."); + } try { closeBlocking(); if( writeThread != null ) { this.writeThread.interrupt(); this.writeThread = null; } + if( connectReadThread != null ) { + this.connectReadThread.interrupt(); + this.connectReadThread = null; + } this.draft.reset(); if( this.socket != null ) { this.socket.close(); @@ -264,11 +277,11 @@ private void reset() { * Initiates the websocket connection. This method does not block. */ public void connect() { - if( writeThread != null ) + if( connectReadThread != null ) throw new IllegalStateException( "WebSocketClient objects are not reuseable" ); - writeThread = new Thread( this ); - writeThread.setName( "WebSocketConnectReadThread-" + writeThread.getId() ); - writeThread.start(); + connectReadThread = new Thread( this ); + connectReadThread.setName( "WebSocketConnectReadThread-" + connectReadThread.getId() ); + connectReadThread.start(); } /** @@ -316,7 +329,7 @@ public void closeBlocking() throws InterruptedException { /** * Sends text to the connected websocket server. - * + * * @param text * The string which will be transmitted. */ @@ -326,7 +339,7 @@ public void send( String text ) throws NotYetConnectedException { /** * Sends binary data to the connected webSocket server. - * + * * @param data * The byte-Array of data to send to the WebSocket server. */ @@ -411,8 +424,7 @@ public void run() { onError( e ); engine.closeConnection( CloseFrame.ABNORMAL_CLOSE, e.getMessage() ); } - //I have no idea why this was added. - //assert ( socket.isClosed() ); + connectReadThread = null; } /** diff --git a/src/test/java/org/java_websocket/issues/AllIssueTests.java b/src/test/java/org/java_websocket/issues/AllIssueTests.java index 5759d06b5..617262991 100644 --- a/src/test/java/org/java_websocket/issues/AllIssueTests.java +++ b/src/test/java/org/java_websocket/issues/AllIssueTests.java @@ -36,7 +36,8 @@ org.java_websocket.issues.Issue256Test.class, org.java_websocket.issues.Issue661Test.class, org.java_websocket.issues.Issue666Test.class, - org.java_websocket.issues.Issue677Test.class + org.java_websocket.issues.Issue677Test.class, + org.java_websocket.issues.Issue732Test.class }) /** * Start all tests for issues diff --git a/src/test/java/org/java_websocket/issues/Issue732Test.java b/src/test/java/org/java_websocket/issues/Issue732Test.java new file mode 100644 index 000000000..59266c621 --- /dev/null +++ b/src/test/java/org/java_websocket/issues/Issue732Test.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2010-2018 Nathan Rajlich + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.java_websocket.issues; + +import org.java_websocket.WebSocket; +import org.java_websocket.WebSocketImpl; +import org.java_websocket.client.WebSocketClient; +import org.java_websocket.handshake.ClientHandshake; +import org.java_websocket.handshake.ServerHandshake; +import org.java_websocket.server.WebSocketServer; +import org.java_websocket.util.SocketUtil; +import org.java_websocket.util.ThreadCheck; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; + +import java.net.InetSocketAddress; +import java.net.URI; +import java.util.concurrent.CountDownLatch; + +import static org.junit.Assert.fail; + +public class Issue732Test { + + @Rule + public ThreadCheck zombies = new ThreadCheck(); + + private CountDownLatch countServerDownLatch = new CountDownLatch(1); + + @Test(timeout = 2000) + public void testIssue() throws Exception { + int port = SocketUtil.getAvailablePort(); + final WebSocketClient webSocket = new WebSocketClient(new URI("ws://localhost:" + port)) { + @Override + public void onOpen(ServerHandshake handshakedata) { + try { + this.reconnect(); + Assert.fail("Exception should be thrown"); + } catch (IllegalStateException e) { + // + } + } + + @Override + public void onMessage(String message) { + try { + this.reconnect(); + Assert.fail("Exception should be thrown"); + } catch (IllegalStateException e) { + send("hi"); + } + } + + @Override + public void onClose(int code, String reason, boolean remote) { + try { + this.reconnect(); + Assert.fail("Exception should be thrown"); + } catch (IllegalStateException e) { + // + } + } + + @Override + public void onError(Exception ex) { + try { + this.reconnect(); + Assert.fail("Exception should be thrown"); + } catch (IllegalStateException e) { + // + } + } + }; + WebSocketServer server = new WebSocketServer(new InetSocketAddress(port)) { + @Override + public void onOpen(WebSocket conn, ClientHandshake handshake) { + conn.send("hi"); + } + + @Override + public void onClose(WebSocket conn, int code, String reason, boolean remote) { + countServerDownLatch.countDown(); + } + + @Override + public void onMessage(WebSocket conn, String message) { + conn.close(); + } + + @Override + public void onError(WebSocket conn, Exception ex) { + fail("There should be no onError!"); + } + + @Override + public void onStart() { + webSocket.connect(); + } + }; + server.start(); + countServerDownLatch.await(); + server.stop(); + } +}