com.pusher.java_websocket.drafts.Draft_75 Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java-websocket Show documentation
Show all versions of java-websocket Show documentation
The Pusher fork of TooTallNate/Java-Websocket
package com.pusher.java_websocket.drafts;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import com.pusher.java_websocket.exceptions.InvalidDataException;
import com.pusher.java_websocket.exceptions.InvalidFrameException;
import com.pusher.java_websocket.exceptions.LimitExedeedException;
import com.pusher.java_websocket.exceptions.NotSendableException;
import com.pusher.java_websocket.framing.CloseFrame;
import com.pusher.java_websocket.framing.FrameBuilder;
import com.pusher.java_websocket.framing.Framedata;
import com.pusher.java_websocket.framing.FramedataImpl1;
import com.pusher.java_websocket.handshake.ClientHandshakeBuilder;
import com.pusher.java_websocket.handshake.ServerHandshake;
import com.pusher.java_websocket.handshake.ServerHandshakeBuilder;
import com.pusher.java_websocket.util.Charsetfunctions;
import com.pusher.java_websocket.exceptions.InvalidHandshakeException;
import com.pusher.java_websocket.handshake.ClientHandshake;
import com.pusher.java_websocket.handshake.HandshakeBuilder;
public class Draft_75 extends Draft {
/**
* The byte representing CR, or Carriage Return, or \r
*/
public static final byte CR = (byte) 0x0D;
/**
* The byte representing LF, or Line Feed, or \n
*/
public static final byte LF = (byte) 0x0A;
/**
* The byte representing the beginning of a WebSocket text frame.
*/
public static final byte START_OF_FRAME = (byte) 0x00;
/**
* The byte representing the end of a WebSocket text frame.
*/
public static final byte END_OF_FRAME = (byte) 0xFF;
/** Is only used to detect protocol violations */
protected boolean readingState = false;
protected List readyframes = new LinkedList();
protected ByteBuffer currentFrame;
private final Random reuseableRandom = new Random();
@Override
public HandshakeState acceptHandshakeAsClient( ClientHandshake request, ServerHandshake response ) {
return request.getFieldValue( "WebSocket-Origin" ).equals( response.getFieldValue( "Origin" ) ) && basicAccept( response ) ? HandshakeState.MATCHED : HandshakeState.NOT_MATCHED;
}
@Override
public HandshakeState acceptHandshakeAsServer( ClientHandshake handshakedata ) {
if( handshakedata.hasFieldValue( "Origin" ) && basicAccept( handshakedata ) ) {
return HandshakeState.MATCHED;
}
return HandshakeState.NOT_MATCHED;
}
@Override
public ByteBuffer createBinaryFrame( Framedata framedata ) {
if( framedata.getOpcode() != Framedata.Opcode.TEXT ) {
throw new RuntimeException( "only text frames supported" );
}
ByteBuffer pay = framedata.getPayloadData();
ByteBuffer b = ByteBuffer.allocate( pay.remaining() + 2 );
b.put( START_OF_FRAME );
pay.mark();
b.put( pay );
pay.reset();
b.put( END_OF_FRAME );
b.flip();
return b;
}
@Override
public List createFrames( ByteBuffer binary, boolean mask ) {
throw new RuntimeException( "not yet implemented" );
}
@Override
public List createFrames( String text, boolean mask ) {
FrameBuilder frame = new FramedataImpl1();
try {
frame.setPayload( ByteBuffer.wrap( Charsetfunctions.utf8Bytes( text ) ) );
} catch ( InvalidDataException e ) {
throw new NotSendableException( e );
}
frame.setFin( true );
frame.setOptcode( Framedata.Opcode.TEXT );
frame.setTransferemasked( mask );
return Collections.singletonList( (Framedata) frame );
}
@Override
public ClientHandshakeBuilder postProcessHandshakeRequestAsClient(ClientHandshakeBuilder request ) throws InvalidHandshakeException {
request.put( "Upgrade", "WebSocket" );
request.put( "Connection", "Upgrade" );
if( !request.hasFieldValue( "Origin" ) ) {
request.put( "Origin", "random" + reuseableRandom.nextInt() );
}
return request;
}
@Override
public HandshakeBuilder postProcessHandshakeResponseAsServer( ClientHandshake request, ServerHandshakeBuilder response ) throws InvalidHandshakeException {
response.setHttpStatusMessage( "Web Socket Protocol Handshake" );
response.put( "Upgrade", "WebSocket" );
response.put( "Connection", request.getFieldValue( "Connection" ) ); // to respond to a Connection keep alive
response.put( "WebSocket-Origin", request.getFieldValue( "Origin" ) );
String location = "ws://" + request.getFieldValue( "Host" ) + request.getResourceDescriptor();
response.put( "WebSocket-Location", location );
// TODO handle Sec-WebSocket-Protocol and Set-Cookie
return response;
}
protected List translateRegularFrame( ByteBuffer buffer ) throws InvalidDataException {
while ( buffer.hasRemaining() ) {
byte newestByte = buffer.get();
if( newestByte == START_OF_FRAME ) { // Beginning of Frame
if( readingState )
throw new InvalidFrameException( "unexpected START_OF_FRAME" );
readingState = true;
} else if( newestByte == END_OF_FRAME ) { // End of Frame
if( !readingState )
throw new InvalidFrameException( "unexpected END_OF_FRAME" );
// currentFrame will be null if END_OF_FRAME was send directly after
// START_OF_FRAME, thus we will send 'null' as the sent message.
if( this.currentFrame != null ) {
currentFrame.flip();
FramedataImpl1 curframe = new FramedataImpl1();
curframe.setPayload( currentFrame );
curframe.setFin( true );
curframe.setOptcode( Framedata.Opcode.TEXT );
readyframes.add( curframe );
this.currentFrame = null;
buffer.mark();
}
readingState = false;
} else if( readingState ) { // Regular frame data, add to current frame buffer //TODO This code is very expensive and slow
if( currentFrame == null ) {
currentFrame = createBuffer();
} else if( !currentFrame.hasRemaining() ) {
currentFrame = increaseBuffer( currentFrame );
}
currentFrame.put( newestByte );
} else {
return null;
}
}
// if no error occurred this block will be reached
/*if( readingState ) {
checkAlloc(currentFrame.position()+1);
}*/
List frames = readyframes;
readyframes = new LinkedList();
return frames;
}
@Override
public List translateFrame( ByteBuffer buffer ) throws InvalidDataException {
List frames = translateRegularFrame( buffer );
if( frames == null ) {
throw new InvalidDataException( CloseFrame.PROTOCOL_ERROR );
}
return frames;
}
@Override
public void reset() {
readingState = false;
this.currentFrame = null;
}
@Override
public CloseHandshakeType getCloseHandshakeType() {
return CloseHandshakeType.NONE;
}
public ByteBuffer createBuffer() {
return ByteBuffer.allocate( INITIAL_FAMESIZE );
}
public ByteBuffer increaseBuffer( ByteBuffer full ) throws LimitExedeedException, InvalidDataException {
full.flip();
ByteBuffer newbuffer = ByteBuffer.allocate( checkAlloc( full.capacity() * 2 ) );
newbuffer.put( full );
return newbuffer;
}
@Override
public Draft copyInstance() {
return new Draft_75();
}
}