Skip to content

Commit fa6a335

Browse files
committed
worked on breaking onWebsocketClose up into onWebsocketCloseInitiated, onWebsocketClosing, onWebsocketClose to give the user more insights/control over the different steps during the close handshake. This is especially necessary because of the asynchronous nature of the implementation.
1 parent 3a1f2b6 commit fa6a335

File tree

7 files changed

+152
-55
lines changed

7 files changed

+152
-55
lines changed

src/main/java/org/java_websocket/SocketChannelIOHelper.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ public static boolean batch( WebSocketImpl ws, ByteChannel sockchannel ) throws
5353
} while ( buffer != null );
5454
}
5555

56-
if( ws.isClosed() ) {
56+
if( ws.outQueue.isEmpty() && ws.isFlushAndClose() ) {
5757
synchronized ( ws ) {
58-
sockchannel.close();
58+
ws.closeConnection();
5959
}
6060
}
6161
return sockchannel instanceof WrappedByteChannel == true ? !( (WrappedByteChannel) sockchannel ).isNeedWrite() : true;

src/main/java/org/java_websocket/WebSocket.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ public enum Role {
4040

4141
public abstract void close( int code );
4242

43+
/**
44+
* This will close the connection immediately without a proper close handshake.
45+
* The code and the message therefore won't be transfered over the wire also they will be forwarded to onClose/onWebsocketClose.
46+
**/
47+
public abstract void closeConnection( int code, String message );
48+
4349
protected abstract void close( InvalidDataException e );
4450

4551
/**
@@ -76,8 +82,10 @@ public enum Role {
7682

7783
public abstract boolean isClosing();
7884

85+
public abstract boolean isFlushAndClose();
86+
7987
public abstract boolean isClosed();
80-
88+
8189
public abstract Draft getDraft();
8290

8391
/**

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

Lines changed: 93 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,6 @@
4141
* Takes care of the "handshake" phase, then allows for easy sending of
4242
* text frames, and receiving frames through an event-based model.
4343
*
44-
* This is an inner class, used by <tt>WebSocketClient</tt> and <tt>WebSocketServer</tt>, and should never need to be instantiated directly
45-
* by your code. However, instances are exposed in <tt>WebSocketServer</tt> through the <i>onClientOpen</i>, <i>onClientClose</i>,
46-
* <i>onClientMessage</i> callbacks.
47-
*
4844
*/
4945
public class WebSocketImpl extends WebSocket {
5046

@@ -76,11 +72,13 @@ public class WebSocketImpl extends WebSocket {
7672
/**
7773
* Determines whether we sent already a request to Close the connection or not.
7874
*/
79-
private volatile boolean closeHandshakeSent = false;
80-
/**
81-
* Determines whether the connection is open or not
82-
*/
83-
private volatile boolean connectionClosed = false;
75+
private volatile boolean closeHandshakeSubmitted = false;
76+
77+
/** When true no further frames may be submitted to be sent */
78+
private volatile boolean flushandclosestate = false;
79+
80+
/** When true the socket has been closed */
81+
private volatile boolean isclosed = false;
8482

8583
/**
8684
* The listener to notify of WebSocket events.
@@ -102,6 +100,10 @@ public class WebSocketImpl extends WebSocket {
102100
/** stores the handshake sent by this websocket ( Role.CLIENT only ) */
103101
private ClientHandshake handshakerequest = null;
104102

103+
private String closemessage = null;
104+
private Integer closecode = null;
105+
private Boolean closedremotely = null;
106+
105107
/**
106108
* crates a websocket with server role
107109
*/
@@ -139,7 +141,7 @@ public WebSocketImpl( WebSocketListener listener , Draft draft , Socket sock ) {
139141
* When socket related I/O errors occur.
140142
*/
141143
public void decode( ByteBuffer socketBuffer ) throws IOException {
142-
if( !socketBuffer.hasRemaining() || connectionClosed )
144+
if( !socketBuffer.hasRemaining() || flushandclosestate )
143145
return;
144146

145147
if( DEBUG )
@@ -152,7 +154,7 @@ public void decode( ByteBuffer socketBuffer ) throws IOException {
152154
decodeFrames( socketBuffer );
153155
}
154156
}
155-
assert ( isClosing() || isClosed() || !socketBuffer.hasRemaining() );
157+
assert ( isClosing() || isFlushAndClose() || !socketBuffer.hasRemaining() );
156158
}
157159

158160
/**
@@ -196,7 +198,7 @@ private boolean decodeHandshake( ByteBuffer socketBufferNew ) throws IOException
196198
socketBuffer.reset();
197199
Handshakedata tmphandshake = d.translateHandshake( socketBuffer );
198200
if( tmphandshake instanceof ClientHandshake == false ) {
199-
closeConnection( CloseFrame.PROTOCOL_ERROR, "wrong http function", false );
201+
flushAndClose( CloseFrame.PROTOCOL_ERROR, "wrong http function", false );
200202
return false;
201203
}
202204
ClientHandshake handshake = (ClientHandshake) tmphandshake;
@@ -206,7 +208,7 @@ private boolean decodeHandshake( ByteBuffer socketBufferNew ) throws IOException
206208
try {
207209
response = wsl.onWebsocketHandshakeReceivedAsServer( this, d, handshake );
208210
} catch ( InvalidDataException e ) {
209-
closeConnection( e.getCloseCode(), e.getMessage(), false );
211+
flushAndClose( e.getCloseCode(), e.getMessage(), false );
210212
return false;
211213
}
212214
write( d.createHandshake( d.postProcessHandshakeResponseAsServer( handshake, response ), role ) );
@@ -226,7 +228,7 @@ private boolean decodeHandshake( ByteBuffer socketBufferNew ) throws IOException
226228
// special case for multiple step handshakes
227229
Handshakedata tmphandshake = draft.translateHandshake( socketBuffer );
228230
if( tmphandshake instanceof ClientHandshake == false ) {
229-
closeConnection( CloseFrame.PROTOCOL_ERROR, "wrong http function", false );
231+
flushAndClose( CloseFrame.PROTOCOL_ERROR, "wrong http function", false );
230232
return false;
231233
}
232234
ClientHandshake handshake = (ClientHandshake) tmphandshake;
@@ -244,7 +246,7 @@ private boolean decodeHandshake( ByteBuffer socketBufferNew ) throws IOException
244246
draft.setParseMode( role );
245247
Handshakedata tmphandshake = draft.translateHandshake( socketBuffer );
246248
if( tmphandshake instanceof ServerHandshake == false ) {
247-
closeConnection( CloseFrame.PROTOCOL_ERROR, "Wwrong http function", false );
249+
flushAndClose( CloseFrame.PROTOCOL_ERROR, "Wwrong http function", false );
248250
return false;
249251
}
250252
ServerHandshake handshake = (ServerHandshake) tmphandshake;
@@ -253,7 +255,7 @@ private boolean decodeHandshake( ByteBuffer socketBufferNew ) throws IOException
253255
try {
254256
wsl.onWebsocketHandshakeReceivedAsClient( this, handshakerequest, handshake );
255257
} catch ( InvalidDataException e ) {
256-
closeConnection( e.getCloseCode(), e.getMessage(), false );
258+
flushAndClose( e.getCloseCode(), e.getMessage(), false );
257259
return false;
258260
}
259261
open( handshake );
@@ -287,12 +289,17 @@ private boolean decodeHandshake( ByteBuffer socketBufferNew ) throws IOException
287289
}
288290

289291
private void decodeFrames( ByteBuffer socketBuffer ) {
292+
if( flushandclosestate )
293+
return;
294+
assert ( isOpen() );
290295
List<Framedata> frames;
291296
try {
292297
frames = draft.translateFrame( socketBuffer );
293298
for( Framedata f : frames ) {
294299
if( DEBUG )
295300
System.out.println( "matched frame: " + f );
301+
if( flushandclosestate )
302+
return;
296303
Opcode curop = f.getOpcode();
297304
if( curop == Opcode.CLOSING ) {
298305
int code = CloseFrame.NOCODE;
@@ -302,14 +309,14 @@ private void decodeFrames( ByteBuffer socketBuffer ) {
302309
code = cf.getCloseCode();
303310
reason = cf.getMessage();
304311
}
305-
if( closeHandshakeSent ) {
312+
if( closeHandshakeSubmitted ) {
306313
// complete the close handshake by disconnecting
307314
closeConnection( code, reason, true );
308315
} else {
309316
// echo close handshake
310317
if( draft.getCloseHandshakeType() == CloseHandshakeType.TWOWAY )
311-
close( code, reason );
312-
closeConnection( code, reason, false );
318+
close( code, reason, true );
319+
flushAndClose( code, reason, false );
313320
}
314321
continue;
315322
} else if( curop == Opcode.PING ) {
@@ -348,42 +355,47 @@ private void decodeFrames( ByteBuffer socketBuffer ) {
348355
}
349356
}
350357

351-
@Override
352-
public void close( int code, String message ) {
353-
if( !closeHandshakeSent ) {
358+
359+
private void close( int code, String message, boolean remote ) {
360+
if( !closeHandshakeSubmitted ) {
354361
if( handshakeComplete ) {
355362
if( code == CloseFrame.ABNORMAL_CLOSE ) {
356-
closeConnection( code, true );
357-
closeHandshakeSent = true;
363+
assert ( remote == false );
364+
flushAndClose( code, message, false );
365+
closeHandshakeSubmitted = true;
358366
return;
359367
}
360368
if( draft.getCloseHandshakeType() != CloseHandshakeType.NONE ) {
361369
try {
362370
sendFrame( new CloseFrameBuilder( code, message ) );
371+
wsl.onWebsocketCloseInitiated( this, code, message );
363372
} catch ( InvalidDataException e ) {
364373
wsl.onWebsocketError( this, e );
365-
closeConnection( CloseFrame.ABNORMAL_CLOSE, "generated frame is invalid", false );
374+
flushAndClose( CloseFrame.ABNORMAL_CLOSE, "generated frame is invalid", false );
366375
}
367376
} else {
368-
closeConnection( code, false );
377+
flushAndClose( code, message, false );
369378
}
370379
} else if( code == CloseFrame.FLASHPOLICY ) {
371-
closeConnection( CloseFrame.FLASHPOLICY, true );
380+
assert ( remote );
381+
flushAndClose( CloseFrame.FLASHPOLICY, message, true );
372382
} else {
373-
closeConnection( CloseFrame.NEVERCONNECTED, false );
383+
flushAndClose( CloseFrame.NEVERCONNECTED, message, false );
374384
}
375385
if( code == CloseFrame.PROTOCOL_ERROR )// this endpoint found a PROTOCOL_ERROR
376-
closeConnection( code, false );
377-
closeHandshakeSent = true;
386+
flushAndClose( code, message, remote );
387+
closeHandshakeSubmitted = true;
378388
tmpHandshakeBytes = null;
379389
return;
380390
}
381391
}
382392

393+
@Override
394+
public void close( int code, String message ) {
395+
close( code, message, false );
396+
}
397+
383398
/**
384-
* closes the socket no matter if the closing handshake completed.
385-
* Does not send any not yet written data before closing.
386-
* Calling this method more than once will have no effect.
387399
*
388400
* @param remote
389401
* Indicates who "generated" <code>code</code>.<br>
@@ -393,32 +405,62 @@ public void close( int code, String message ) {
393405
**/
394406

395407
protected synchronized void closeConnection( int code, String message, boolean remote ) {
396-
if( connectionClosed ) {
408+
if( isclosed ) {
397409
return;
398410
}
399-
connectionClosed = true;
400411

401-
/*if( key != null ) {
412+
if( key != null ) {
402413
// key.attach( null ); //see issue #114
403414
key.cancel();
404415
try {
405416
channel.close();
406417
} catch ( IOException e ) {
407418
wsl.onWebsocketError( this, e );
408419
}
409-
}*/
410-
wsl.onWriteDemand( this ); // ensures that all outgoing frames are flushed before closing the connection
411-
420+
}
412421
this.wsl.onWebsocketClose( this, code, message, remote );
413422
if( draft != null )
414423
draft.reset();
415424
tempContiniousFrame = null;
416425
handshakerequest = null;
426+
427+
isclosed = true;
428+
417429
}
418430

419431
protected void closeConnection( int code, boolean remote ) {
420432
closeConnection( code, "", remote );
421433
}
434+
435+
public void closeConnection() {
436+
if( closedremotely == null ) {
437+
throw new IllegalStateException( "this method must be used in conjuction with flushAndClose" );
438+
}
439+
closeConnection( closecode, closemessage, closedremotely );
440+
}
441+
442+
public void closeConnection( int code, String message ) {
443+
closeConnection( code, message, false );
444+
}
445+
446+
protected synchronized void flushAndClose( int code, String message, boolean remote ) {
447+
if( flushandclosestate ) {
448+
return;
449+
}
450+
closecode = code;
451+
closemessage = message;
452+
closedremotely = remote;
453+
454+
flushandclosestate = true;
455+
456+
wsl.onWriteDemand( this ); // ensures that all outgoing frames are flushed before closing the connection
457+
458+
this.wsl.onWebsocketCloseInitiated( this, code, message );
459+
if( draft != null )
460+
draft.reset();
461+
tempContiniousFrame = null;
462+
handshakerequest = null;
463+
}
422464

423465
public void eot() {
424466
if( draft == null ) {
@@ -437,12 +479,12 @@ public void eot() {
437479

438480
@Override
439481
public void close( int code ) {
440-
close( code, "" );
482+
close( code, "", false );
441483
}
442484

443485
@Override
444486
public void close( InvalidDataException e ) {
445-
close( e.getCloseCode(), e.getMessage() );
487+
close( e.getCloseCode(), e.getMessage(), false );
446488
}
447489

448490
/**
@@ -580,22 +622,27 @@ private void open( Handshakedata d ) throws IOException {
580622

581623
@Override
582624
public boolean isConnecting() {
583-
return ( !connectionClosed && !closeHandshakeSent && !handshakeComplete );
625+
return ( !flushandclosestate && !closeHandshakeSubmitted && !handshakeComplete );
584626
}
585627

586628
@Override
587629
public boolean isOpen() {
588-
return ( !connectionClosed && !closeHandshakeSent && handshakeComplete );
630+
return ( !flushandclosestate && !isclosed && !closeHandshakeSubmitted && handshakeComplete );
589631
}
590632

591633
@Override
592634
public boolean isClosing() {
593-
return ( !connectionClosed && closeHandshakeSent );
635+
return ( !isclosed && closeHandshakeSubmitted );
636+
}
637+
638+
@Override
639+
public boolean isFlushAndClose() {
640+
return flushandclosestate;
594641
}
595642

596643
@Override
597644
public boolean isClosed() {
598-
return connectionClosed;
645+
return isclosed;
599646
}
600647

601648
/**
@@ -613,7 +660,7 @@ public int getReadyState() {
613660
return READY_STATE_OPEN;
614661
} else if( isClosing() ) {
615662
return READY_STATE_CLOSING;
616-
} else if( isClosed() ) {
663+
} else if( isFlushAndClose() ) {
617664
return READY_STATE_CLOSED;
618665
}
619666
assert ( false );

src/main/java/org/java_websocket/WebSocketListener.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,12 @@ public interface WebSocketListener {
103103
*/
104104
public void onWebsocketClose( WebSocket conn, int code, String reason, boolean remote );
105105

106+
/** called as soon as no further frames are accepted */
107+
public void onWebsocketClosing( WebSocket conn, int code, String reason, boolean remote );
108+
109+
/** send when this peer sends a close handshake */
110+
public void onWebsocketCloseInitiated( WebSocket conn, int code, String reason );
111+
106112
/**
107113
* Called if an exception worth noting occurred.
108114
* If an error causes the connection to fail onClose will be called additionally afterwards.

0 commit comments

Comments
 (0)