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 */
4945public 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 );
0 commit comments