package net.tootallnate.websocket.drafts;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import net.tootallnate.websocket.Charsetfunctions;
import net.tootallnate.websocket.Draft;
import net.tootallnate.websocket.FrameBuilder;
import net.tootallnate.websocket.Framedata;
import net.tootallnate.websocket.Framedata.Opcode;
import net.tootallnate.websocket.FramedataImpl1;
import net.tootallnate.websocket.HandshakeBuilder;
import net.tootallnate.websocket.Handshakedata;
import net.tootallnate.websocket.exeptions.InvalidDataException;
import net.tootallnate.websocket.exeptions.InvalidHandshakeException;
import net.tootallnate.websocket.exeptions.NotSendableException;
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;
private boolean readingState = false;
private boolean inframe = false;
private ByteBuffer currentFrame;
@Override
public HandshakeState acceptHandshakeAsClient( Handshakedata request, Handshakedata response ) {
return request.getFieldValue( "WebSocket-Origin" ).equals( response.getFieldValue( "Origin" ) ) && basicAccept( response ) ? HandshakeState.MATCHED : HandshakeState.NOT_MATCHED;
}
@Override
public HandshakeState acceptHandshakeAsServer( Handshakedata handshakedata ) {
if( handshakedata.hasFieldValue( "Origin" ) && basicAccept( handshakedata ) ) {
return HandshakeState.MATCHED;
}
return HandshakeState.NOT_MATCHED;
}
@Override
public ByteBuffer createBinaryFrame( Framedata framedata ) {
byte[] pay = framedata.getPayloadData();
ByteBuffer b = ByteBuffer.allocate( pay.length + 2 );
b.put( START_OF_FRAME );
b.put( pay );
b.put( END_OF_FRAME );
b.flip();
return b;
}
@Override
public List<Framedata> createFrames( byte[] binary, boolean mask ) {
throw new RuntimeException( "not yet implemented" );
}
@Override
public List<Framedata> createFrames( String text, boolean mask ) {
FrameBuilder frame = new FramedataImpl1();
try {
frame.setPayload( Charsetfunctions.utf8Bytes( text ) );
} catch ( InvalidDataException e ) {
throw new NotSendableException( e );
}
frame.setFin( true );
frame.setOptcode( Opcode.TEXT );
frame.setTransferemasked( mask );
return Collections.singletonList( (Framedata) frame );
}
@Override
public HandshakeBuilder postProcessHandshakeRequestAsClient( HandshakeBuilder request ) throws InvalidHandshakeException {
request.put( "Upgrade", "WebSocket" );
request.put( "Connection", "Upgrade" );
if(!request.hasFieldValue( "Origin" )){
request.put( "Origin", "random"+new Random().nextInt() );
}
return request;
}
@Override
public HandshakeBuilder postProcessHandshakeResponseAsServer( Handshakedata request, HandshakeBuilder 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;
}
@Override
public List<Framedata> translateFrame( ByteBuffer buffer ) throws InvalidDataException {
List<Framedata> frames = new LinkedList<Framedata>();
while ( buffer.hasRemaining() ) {
byte newestByte = buffer.get();
if( newestByte == START_OF_FRAME && !readingState ) { // Beginning of Frame
this.currentFrame = null;
readingState = true;
} else if( newestByte == END_OF_FRAME && readingState ) { // 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 ) {
FramedataImpl1 curframe = new FramedataImpl1();
curframe.setPayload( currentFrame.array() );
curframe.setFin( true );
curframe.setOptcode( inframe ? Opcode.CONTINIOUS : Opcode.TEXT );
frames.add( curframe );
}
readingState = false;
inframe = false;
} else { // Regular frame data, add to current frame buffer //TODO This code is very expensive and slow
ByteBuffer frame = ByteBuffer.allocate( checkAlloc( ( this.currentFrame != null ? this.currentFrame.capacity() : 0 ) + 1 ) );
if( this.currentFrame != null ) {
this.currentFrame.rewind();
frame.put( this.currentFrame );
}
frame.put( newestByte );
this.currentFrame = frame;
}
}
if( readingState ) {
FramedataImpl1 curframe = new FramedataImpl1();
curframe.setPayload( currentFrame.array() );
curframe.setFin( false );
curframe.setOptcode( inframe ? Opcode.CONTINIOUS : Opcode.TEXT );
inframe = true;
frames.add( curframe );
}
return frames;
}
@Override
public void reset() {
readingState = false;
this.currentFrame = null;
}
@Override
public boolean hasCloseHandshake() {
return false;
}
}