Skip to content

Commit b52faa4

Browse files
authored
Merge pull request TooTallNate#914 from marci4/Issue900
Wrap IOException and include WebSocket
2 parents ec0fa03 + 72aebe1 commit b52faa4

File tree

4 files changed

+245
-12
lines changed

4 files changed

+245
-12
lines changed

src/main/java/org/java_websocket/WebSocketImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ public synchronized void closeConnection( int code, String message, boolean remo
510510
try {
511511
channel.close();
512512
} catch ( IOException e ) {
513-
if( e.getMessage().equals( "Broken pipe" ) ) {
513+
if( e.getMessage() != null && e.getMessage().equals( "Broken pipe" ) ) {
514514
log.trace( "Caught IOException: Broken pipe during closeConnection()", e );
515515
} else {
516516
log.error("Exception during channel.close()", e);
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright (c) 2010-2020 Nathan Rajlich
3+
*
4+
* Permission is hereby granted, free of charge, to any person
5+
* obtaining a copy of this software and associated documentation
6+
* files (the "Software"), to deal in the Software without
7+
* restriction, including without limitation the rights to use,
8+
* copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the
10+
* Software is furnished to do so, subject to the following
11+
* conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be
14+
* included in all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18+
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20+
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21+
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23+
* OTHER DEALINGS IN THE SOFTWARE.
24+
*
25+
*/
26+
27+
package org.java_websocket.exceptions;
28+
29+
import org.java_websocket.WebSocket;
30+
31+
import java.io.IOException;
32+
33+
/**
34+
* Exception to wrap an IOException and include information about the websocket which had the exception
35+
* @since 1.4.1
36+
*/
37+
public class WrappedIOException extends Exception {
38+
39+
/**
40+
* The websocket where the IOException happened
41+
*/
42+
private final WebSocket connection;
43+
44+
/**
45+
* The IOException
46+
*/
47+
private final IOException ioException;
48+
49+
/**
50+
* Wrapp an IOException and include the websocket
51+
* @param connection the websocket where the IOException happened
52+
* @param ioException the IOException
53+
*/
54+
public WrappedIOException(WebSocket connection, IOException ioException) {
55+
this.connection = connection;
56+
this.ioException = ioException;
57+
}
58+
59+
/**
60+
* The websocket where the IOException happened
61+
* @return the websocket for the wrapped IOException
62+
*/
63+
public WebSocket getConnection() {
64+
return connection;
65+
}
66+
67+
/**
68+
* The wrapped IOException
69+
* @return IOException which is wrapped
70+
*/
71+
public IOException getIOException() {
72+
return ioException;
73+
}
74+
}

src/main/java/org/java_websocket/server/WebSocketServer.java

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.java_websocket.*;
4848
import org.java_websocket.drafts.Draft;
4949
import org.java_websocket.exceptions.WebsocketNotConnectedException;
50+
import org.java_websocket.exceptions.WrappedIOException;
5051
import org.java_websocket.framing.CloseFrame;
5152
import org.java_websocket.framing.Framedata;
5253
import org.java_websocket.handshake.ClientHandshake;
@@ -320,7 +321,6 @@ public void run() {
320321
int selectTimeout = 0;
321322
while ( !selectorthread.isInterrupted() && iShutdownCount != 0) {
322323
SelectionKey key = null;
323-
WebSocketImpl conn = null;
324324
try {
325325
if (isclosed.get()) {
326326
selectTimeout = 5;
@@ -334,7 +334,6 @@ public void run() {
334334

335335
while ( i.hasNext() ) {
336336
key = i.next();
337-
conn = null;
338337

339338
if( !key.isValid() ) {
340339
continue;
@@ -358,10 +357,10 @@ public void run() {
358357
// an other thread may cancel the key
359358
} catch ( ClosedByInterruptException e ) {
360359
return; // do the same stuff as when InterruptedException is thrown
360+
} catch ( WrappedIOException ex) {
361+
handleIOException( key, ex.getConnection(), ex.getIOException());
361362
} catch ( IOException ex ) {
362-
if( key != null )
363-
key.cancel();
364-
handleIOException( key, conn, ex );
363+
handleIOException( key, null, ex );
365364
} catch ( InterruptedException e ) {
366365
// FIXME controlled shutdown (e.g. take care of buffermanagement)
367366
Thread.currentThread().interrupt();
@@ -445,7 +444,7 @@ private void doAccept(SelectionKey key, Iterator<SelectionKey> i) throws IOExcep
445444
* @throws InterruptedException thrown by taking a buffer
446445
* @throws IOException if an error happened during read
447446
*/
448-
private boolean doRead(SelectionKey key, Iterator<SelectionKey> i) throws InterruptedException, IOException {
447+
private boolean doRead(SelectionKey key, Iterator<SelectionKey> i) throws InterruptedException, WrappedIOException {
449448
WebSocketImpl conn = (WebSocketImpl) key.attachment();
450449
ByteBuffer buf = takeBuffer();
451450
if(conn.getChannel() == null){
@@ -471,7 +470,7 @@ private boolean doRead(SelectionKey key, Iterator<SelectionKey> i) throws Interr
471470
}
472471
} catch ( IOException e ) {
473472
pushBuffer( buf );
474-
throw e;
473+
throw new WrappedIOException(conn, e);
475474
}
476475
return true;
477476
}
@@ -481,12 +480,16 @@ private boolean doRead(SelectionKey key, Iterator<SelectionKey> i) throws Interr
481480
* @param key the selectionkey to write on
482481
* @throws IOException if an error happened during batch
483482
*/
484-
private void doWrite(SelectionKey key) throws IOException {
483+
private void doWrite(SelectionKey key) throws WrappedIOException {
485484
WebSocketImpl conn = (WebSocketImpl) key.attachment();
486-
if( SocketChannelIOHelper.batch( conn, conn.getChannel() ) ) {
487-
if( key.isValid() ) {
488-
key.interestOps(SelectionKey.OP_READ);
485+
try {
486+
if (SocketChannelIOHelper.batch(conn, conn.getChannel())) {
487+
if (key.isValid()) {
488+
key.interestOps(SelectionKey.OP_READ);
489+
}
489490
}
491+
} catch (IOException e) {
492+
throw new WrappedIOException(conn, e);
490493
}
491494
}
492495

@@ -598,6 +601,9 @@ private void pushBuffer( ByteBuffer buf ) throws InterruptedException {
598601

599602
private void handleIOException( SelectionKey key, WebSocket conn, IOException ex ) {
600603
// onWebsocketError( conn, ex );// conn may be null here
604+
if (key != null) {
605+
key.cancel();
606+
}
601607
if( conn != null ) {
602608
conn.closeConnection( CloseFrame.ABNORMAL_CLOSE, ex.getMessage() );
603609
} else if( key != null ) {
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/*
2+
* Copyright (c) 2010-2020 Nathan Rajlich
3+
*
4+
* Permission is hereby granted, free of charge, to any person
5+
* obtaining a copy of this software and associated documentation
6+
* files (the "Software"), to deal in the Software without
7+
* restriction, including without limitation the rights to use,
8+
* copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the
10+
* Software is furnished to do so, subject to the following
11+
* conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be
14+
* included in all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18+
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20+
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21+
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23+
* OTHER DEALINGS IN THE SOFTWARE.
24+
*
25+
*/
26+
27+
package org.java_websocket.issues;
28+
29+
import org.java_websocket.WebSocket;
30+
import org.java_websocket.WebSocketImpl;
31+
import org.java_websocket.WrappedByteChannel;
32+
import org.java_websocket.client.WebSocketClient;
33+
import org.java_websocket.handshake.ClientHandshake;
34+
import org.java_websocket.handshake.ServerHandshake;
35+
import org.java_websocket.server.WebSocketServer;
36+
import org.java_websocket.util.SocketUtil;
37+
import org.junit.Test;
38+
39+
import java.io.IOException;
40+
import java.net.InetSocketAddress;
41+
import java.net.URI;
42+
import java.nio.ByteBuffer;
43+
import java.util.ArrayList;
44+
import java.util.concurrent.CountDownLatch;
45+
46+
public class Issue900Test {
47+
48+
CountDownLatch serverStartLatch = new CountDownLatch(1);
49+
CountDownLatch closeCalledLatch = new CountDownLatch(1);
50+
51+
@Test(timeout = 2000)
52+
public void testIssue() throws Exception {
53+
int port = SocketUtil.getAvailablePort();
54+
final WebSocketClient client = new WebSocketClient(new URI("ws://localhost:" + port)) {
55+
@Override
56+
public void onOpen(ServerHandshake handshakedata) {
57+
58+
}
59+
60+
@Override
61+
public void onMessage(String message) {
62+
}
63+
64+
@Override
65+
public void onClose(int code, String reason, boolean remote) {
66+
}
67+
68+
@Override
69+
public void onError(Exception ex) {
70+
71+
}
72+
};
73+
WebSocketServer server = new WebSocketServer(new InetSocketAddress(port)) {
74+
@Override
75+
public void onOpen(WebSocket conn, ClientHandshake handshake) {
76+
}
77+
78+
@Override
79+
public void onClose(WebSocket conn, int code, String reason, boolean remote) {
80+
closeCalledLatch.countDown();
81+
}
82+
83+
@Override
84+
public void onMessage(WebSocket conn, String message) {
85+
86+
}
87+
88+
@Override
89+
public void onError(WebSocket conn, Exception ex) {
90+
91+
}
92+
93+
@Override
94+
public void onStart() {
95+
serverStartLatch.countDown();
96+
}
97+
};
98+
new Thread(server).start();
99+
serverStartLatch.await();
100+
client.connectBlocking();
101+
WebSocketImpl websocketImpl = (WebSocketImpl)new ArrayList<WebSocket>(server.getConnections()).get(0);
102+
websocketImpl.setChannel(new ExceptionThrowingByteChannel());
103+
server.broadcast("test");
104+
closeCalledLatch.await();
105+
}
106+
class ExceptionThrowingByteChannel implements WrappedByteChannel {
107+
108+
@Override
109+
public boolean isNeedWrite() {
110+
return true;
111+
}
112+
113+
@Override
114+
public void writeMore() throws IOException {
115+
throw new IOException();
116+
}
117+
118+
@Override
119+
public boolean isNeedRead() {
120+
return true;
121+
}
122+
123+
@Override
124+
public int readMore(ByteBuffer dst) throws IOException {
125+
throw new IOException();
126+
}
127+
128+
@Override
129+
public boolean isBlocking() {
130+
return false;
131+
}
132+
133+
@Override
134+
public int read(ByteBuffer dst) throws IOException {
135+
throw new IOException();
136+
}
137+
138+
@Override
139+
public int write(ByteBuffer src) throws IOException {
140+
throw new IOException();
141+
}
142+
143+
@Override
144+
public boolean isOpen() {
145+
return false;
146+
}
147+
148+
@Override
149+
public void close() throws IOException {
150+
throw new IOException();
151+
}
152+
}
153+
}

0 commit comments

Comments
 (0)