Skip to content

Commit 21663de

Browse files
authored
Move processing of frames to drafts & improve handling of IOExceptions (TooTallNate#538)
* Move processing of frames to draft All deprecated drafts have no implementation * Improve handling of IOExceptions causing eot() TooTallNate#516 SSLException will call onError and then call eot()
1 parent e30eb11 commit 21663de

File tree

7 files changed

+244
-147
lines changed

7 files changed

+244
-147
lines changed

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

Lines changed: 47 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
import org.java_websocket.framing.CloseFrame;
3636
import org.java_websocket.framing.Framedata;
3737
import org.java_websocket.framing.Framedata.Opcode;
38-
import org.java_websocket.framing.FramedataImpl1;
3938
import org.java_websocket.framing.PingFrame;
4039
import org.java_websocket.handshake.*;
4140
import org.java_websocket.server.WebSocketServer.WebSocketWorker;
@@ -48,10 +47,7 @@
4847
import java.nio.channels.ByteChannel;
4948
import java.nio.channels.NotYetConnectedException;
5049
import java.nio.channels.SelectionKey;
51-
import java.util.ArrayList;
52-
import java.util.Collection;
53-
import java.util.Collections;
54-
import java.util.List;
50+
import java.util.*;
5551
import java.util.concurrent.BlockingQueue;
5652
import java.util.concurrent.LinkedBlockingQueue;
5753

@@ -63,9 +59,9 @@
6359
public class WebSocketImpl implements WebSocket {
6460
public static int RCVBUF = 16384;
6561

66-
/**
67-
* Activate debug mode for additional infos
68-
*/
62+
/**
63+
* Activate debug mode for additional infos
64+
*/
6965
public static boolean DEBUG = false; // must be final in the future in order to take advantage of VM optimization
7066

7167
/**
@@ -95,26 +91,21 @@ public class WebSocketImpl implements WebSocket {
9591
private volatile boolean flushandclosestate = false;
9692
private READYSTATE readystate = READYSTATE.NOT_YET_CONNECTED;
9793

98-
/**
99-
* A list of drafts available for this websocket
100-
*/
94+
/**
95+
* A list of drafts available for this websocket
96+
*/
10197
private List<Draft> knownDrafts;
10298

103-
/**
104-
* The draft which is used by this websocket
105-
*/
99+
/**
100+
* The draft which is used by this websocket
101+
*/
106102
private Draft draft = null;
107103

108-
/**
109-
* The role which this websocket takes in the connection
110-
*/
104+
/**
105+
* The role which this websocket takes in the connection
106+
*/
111107
private Role role;
112108

113-
/**
114-
* The frame which had the opcode Continous set
115-
*/
116-
private Framedata current_continuous_frame = null;
117-
118109
/**
119110
* the bytes of an incomplete received handshake
120111
*/
@@ -359,6 +350,22 @@ private boolean decodeHandshake( ByteBuffer socketBufferNew ) {
359350
return false;
360351
}
361352

353+
private void decodeFrames( ByteBuffer socketBuffer ) {
354+
List<Framedata> frames;
355+
try {
356+
frames = draft.translateFrame( socketBuffer );
357+
for( Framedata f : frames ) {
358+
if( DEBUG )
359+
System.out.println( "matched frame: " + f );
360+
draft.processFrame( this, f );
361+
}
362+
} catch ( InvalidDataException e1 ) {
363+
wsl.onWebsocketError( this, e1 );
364+
close( e1 );
365+
return;
366+
}
367+
}
368+
362369
/**
363370
* Close the connection if the received handshake was not correct
364371
* @param exception the InvalidDataException causing this problem
@@ -395,115 +402,7 @@ private ByteBuffer generateHttpResponseDueToError(int errorCode) {
395402
return ByteBuffer.wrap( Charsetfunctions.asciiBytes( "HTTP/1.1 "+ errorCodeDescription +"\r\nContent-Type: text/html\nServer: TooTallNate Java-WebSocket\r\nContent-Length: " + (48 + errorCodeDescription.length()) +"\r\n\r\n<html><head></head><body><h1>" + errorCodeDescription + "</h1></body></html>" ));
396403
}
397404

398-
private void decodeFrames( ByteBuffer socketBuffer ) {
399-
400-
List<Framedata> frames;
401-
try {
402-
frames = draft.translateFrame( socketBuffer );
403-
for( Framedata f : frames ) {
404-
if( DEBUG )
405-
System.out.println( "matched frame: " + f );
406-
Opcode curop = f.getOpcode();
407-
boolean fin = f.isFin();
408-
//Not evaluating any further frames if the connection is in READYSTATE CLOSE
409-
if( readystate == READYSTATE.CLOSING )
410-
return;
411-
412-
if( curop == Opcode.CLOSING ) {
413-
int code = CloseFrame.NOCODE;
414-
String reason = "";
415-
if( f instanceof CloseFrame ) {
416-
CloseFrame cf = ( CloseFrame ) f;
417-
code = cf.getCloseCode();
418-
reason = cf.getMessage();
419-
}
420-
if( readystate == READYSTATE.CLOSING ) {
421-
// complete the close handshake by disconnecting
422-
closeConnection( code, reason, true );
423-
} else {
424-
// echo close handshake
425-
if( draft.getCloseHandshakeType() == CloseHandshakeType.TWOWAY )
426-
close( code, reason, true );
427-
else
428-
flushAndClose( code, reason, false );
429-
}
430-
continue;
431-
} else if( curop == Opcode.PING ) {
432-
wsl.onWebsocketPing( this, f );
433-
continue;
434-
} else if( curop == Opcode.PONG ) {
435-
lastPong = System.currentTimeMillis();
436-
wsl.onWebsocketPong( this, f );
437-
continue;
438-
} else if( !fin || curop == Opcode.CONTINUOUS ) {
439-
if( curop != Opcode.CONTINUOUS ) {
440-
if( current_continuous_frame != null )
441-
throw new InvalidDataException( CloseFrame.PROTOCOL_ERROR, "Previous continuous frame sequence not completed." );
442-
current_continuous_frame = f;
443-
} else if( fin ) {
444-
if( current_continuous_frame == null )
445-
throw new InvalidDataException( CloseFrame.PROTOCOL_ERROR, "Continuous frame sequence was not started." );
446-
//Check if the whole payload is valid utf8, when the opcode indicates a text
447-
if( current_continuous_frame.getOpcode() == Opcode.TEXT ) {
448-
//Checking a bit more from the frame before this one just to make sure all the code points are correct
449-
int off = Math.max( current_continuous_frame.getPayloadData().limit() - 64, 0 );
450-
current_continuous_frame.append( f );
451-
if( !Charsetfunctions.isValidUTF8( current_continuous_frame.getPayloadData(), off ) ) {
452-
throw new InvalidDataException( CloseFrame.NO_UTF8 );
453-
}
454-
}
455-
current_continuous_frame = null;
456-
} else if( current_continuous_frame == null ) {
457-
throw new InvalidDataException( CloseFrame.PROTOCOL_ERROR, "Continuous frame sequence was not started." );
458-
}
459-
//Check if the whole payload is valid utf8, when the opcode indicates a text
460-
if( curop == Opcode.TEXT ) {
461-
if( !Charsetfunctions.isValidUTF8( f.getPayloadData() ) ) {
462-
throw new InvalidDataException( CloseFrame.NO_UTF8 );
463-
}
464-
}
465-
//Checking if the current continous frame contains a correct payload with the other frames combined
466-
if( curop == Opcode.CONTINUOUS && current_continuous_frame != null && current_continuous_frame.getOpcode() == Opcode.TEXT ) {
467-
//Checking a bit more from the frame before this one just to make sure all the code points are correct
468-
int off = Math.max( current_continuous_frame.getPayloadData().limit() - 64, 0 );
469-
current_continuous_frame.append( f );
470-
if( !Charsetfunctions.isValidUTF8( current_continuous_frame.getPayloadData(), off ) ) {
471-
throw new InvalidDataException( CloseFrame.NO_UTF8 );
472-
}
473-
}
474-
try {
475-
wsl.onWebsocketMessageFragment( this, f );
476-
} catch ( RuntimeException e ) {
477-
wsl.onWebsocketError( this, e );
478-
}
479-
480-
} else if( current_continuous_frame != null ) {
481-
throw new InvalidDataException( CloseFrame.PROTOCOL_ERROR, "Continuous frame sequence not completed." );
482-
} else if( curop == Opcode.TEXT ) {
483-
try {
484-
wsl.onWebsocketMessage( this, Charsetfunctions.stringUtf8( f.getPayloadData() ) );
485-
} catch ( RuntimeException e ) {
486-
wsl.onWebsocketError( this, e );
487-
}
488-
} else if( curop == Opcode.BINARY ) {
489-
try {
490-
wsl.onWebsocketMessage( this, f.getPayloadData() );
491-
} catch ( RuntimeException e ) {
492-
wsl.onWebsocketError( this, e );
493-
}
494-
} else {
495-
throw new InvalidDataException( CloseFrame.PROTOCOL_ERROR, "non control or continious frame expected" );
496-
}
497-
}
498-
} catch ( InvalidDataException e1 ) {
499-
wsl.onWebsocketError( this, e1 );
500-
close( e1 );
501-
return;
502-
}
503-
504-
}
505-
506-
private void close( int code, String message, boolean remote ) {
405+
public void close( int code, String message, boolean remote ) {
507406
if( readystate != READYSTATE.CLOSING && readystate != READYSTATE.CLOSED ) {
508407
if( readystate == READYSTATE.OPEN ) {
509408
if( code == CloseFrame.ABNORMAL_CLOSE ) {
@@ -524,13 +423,8 @@ private void close( int code, String message, boolean remote ) {
524423
CloseFrame closeFrame = new CloseFrame();
525424
closeFrame.setReason(message);
526425
closeFrame.setCode(code);
527-
try {
528-
closeFrame.isValid();
529-
sendFrame(closeFrame);
530-
} catch (InvalidDataException e) {
531-
//Rethrow invalid data exception
532-
throw e;
533-
}
426+
closeFrame.isValid();
427+
sendFrame(closeFrame);
534428
} catch ( InvalidDataException e ) {
535429
wsl.onWebsocketError( this, e );
536430
flushAndClose( CloseFrame.ABNORMAL_CLOSE, "generated frame is invalid", false );
@@ -566,7 +460,7 @@ public void close( int code, String message ) {
566460
* false means this endpoint decided to send the given code,<br>
567461
* <code>remote</code> may also be true if this endpoint started the closing handshake since the other endpoint may not simply echo the <code>code</code> but close the connection the same time this endpoint does do but with an other <code>code</code>. <br>
568462
**/
569-
protected synchronized void closeConnection( int code, String message, boolean remote ) {
463+
public synchronized void closeConnection( int code, String message, boolean remote ) {
570464
if( readystate == READYSTATE.CLOSED ) {
571465
return;
572466
}
@@ -610,7 +504,7 @@ public void closeConnection( int code, String message ) {
610504
closeConnection( code, message, false );
611505
}
612506

613-
protected synchronized void flushAndClose( int code, String message, boolean remote ) {
507+
public synchronized void flushAndClose( int code, String message, boolean remote ) {
614508
if( flushandclosestate ) {
615509
return;
616510
}
@@ -694,7 +588,7 @@ private void send( Collection<Framedata> frames ) {
694588
for (Framedata f : frames) {
695589
if( DEBUG )
696590
System.out.println( "send frame: " + f );
697-
outgoingFrames.add( draft.createBinaryFrame( f ) );
591+
outgoingFrames.add( draft.createBinaryFrame( f ) );
698592
}
699593
write( outgoingFrames );
700594
}
@@ -868,4 +762,16 @@ public String getResourceDescriptor() {
868762
long getLastPong() {
869763
return lastPong;
870764
}
765+
766+
public void setLastPong( long lastPong ) {
767+
this.lastPong = lastPong;
768+
}
769+
770+
/**
771+
* Getter for the websocket listener
772+
* @return the websocket listener associated with this instance
773+
*/
774+
public WebSocketListener getWebSocketListener() {
775+
return wsl;
776+
}
871777
}

src/main/java/org/java_websocket/client/WebSocketClient.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import java.util.concurrent.CountDownLatch;
4141

4242
import javax.net.ssl.SSLContext;
43+
import javax.net.ssl.SSLException;
4344
import javax.net.ssl.SSLSocketFactory;
4445

4546
import org.java_websocket.AbstractWebSocket;
@@ -278,14 +279,15 @@ public void run() {
278279
}
279280
engine.eot();
280281
} catch ( IOException e ) {
281-
engine.eot();
282+
handleIOException(e);
282283
} catch ( RuntimeException e ) {
283284
// this catch case covers internal errors only and indicates a bug in this websocket implementation
284285
onError( e );
285286
engine.closeConnection( CloseFrame.ABNORMAL_CLOSE, e.getMessage() );
286287
}
287288
assert ( socket.isClosed() );
288289
}
290+
289291
private int getPort() {
290292
int port = uri.getPort();
291293
if( port == -1 ) {
@@ -457,13 +459,13 @@ private class WebsocketWriteThread implements Runnable {
457459
public void run() {
458460
Thread.currentThread().setName( "WebsocketWriteThread" );
459461
try {
460-
while ( !Thread.interrupted() ) {
462+
while( !Thread.interrupted() ) {
461463
ByteBuffer buffer = engine.outQueue.take();
462464
ostream.write( buffer.array(), 0, buffer.limit() );
463465
ostream.flush();
464466
}
465467
} catch ( IOException e ) {
466-
engine.eot();
468+
handleIOException(e);
467469
} catch ( InterruptedException e ) {
468470
// this thread is regularly terminated via an interrupt
469471
}
@@ -562,4 +564,16 @@ public InetSocketAddress getRemoteSocketAddress() {
562564
public String getResourceDescriptor() {
563565
return uri.getPath();
564566
}
567+
568+
569+
/**
570+
* Method to give some additional info for specific IOExceptions
571+
* @param e the IOException causing a eot.
572+
*/
573+
private void handleIOException( IOException e ) {
574+
if (e instanceof SSLException) {
575+
onError( e );
576+
}
577+
engine.eot();
578+
}
565579
}

src/main/java/org/java_websocket/drafts/Draft.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.util.Locale;
3333

3434
import org.java_websocket.WebSocket.Role;
35+
import org.java_websocket.WebSocketImpl;
3536
import org.java_websocket.exceptions.IncompleteHandshakeException;
3637
import org.java_websocket.exceptions.InvalidDataException;
3738
import org.java_websocket.exceptions.InvalidHandshakeException;
@@ -161,6 +162,14 @@ protected boolean basicAccept( Handshakedata handshakedata ) {
161162

162163
public abstract List<Framedata> createFrames( String text, boolean mask );
163164

165+
166+
/**
167+
* Handle the frame specific to the draft
168+
* @param webSocketImpl the websocketimpl used for this draft
169+
* @param frame the frame which is supposed to be handled
170+
*/
171+
public abstract void processFrame( WebSocketImpl webSocketImpl, Framedata frame ) throws InvalidDataException;
172+
164173
public List<Framedata> continuousFrame( Opcode op, ByteBuffer buffer, boolean fin ) {
165174
if(op != Opcode.BINARY && op != Opcode.TEXT) {
166175
throw new IllegalArgumentException( "Only Opcode.BINARY or Opcode.TEXT are allowed" );

src/main/java/org/java_websocket/drafts/Draft_10.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
package org.java_websocket.drafts;
2727

2828
import org.java_websocket.WebSocket.Role;
29+
import org.java_websocket.WebSocketImpl;
2930
import org.java_websocket.exceptions.*;
3031
import org.java_websocket.framing.*;
3132
import org.java_websocket.framing.Framedata.Opcode;
@@ -170,6 +171,10 @@ public List<Framedata> createFrames( String text, boolean mask ) {
170171
return Collections.singletonList( ( Framedata ) curframe );
171172
}
172173

174+
public void processFrame( WebSocketImpl webSocketImpl, Framedata frame ) throws InvalidDataException {
175+
throw new UnsupportedOperationException( "This draft is not supported any more. Please use Draft_6455." );
176+
}
177+
173178
private byte fromOpcode( Opcode opcode ) {
174179
if( opcode == Opcode.CONTINUOUS )
175180
return 0;

0 commit comments

Comments
 (0)