package net.tootallnate.websocket;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import net.tootallnate.websocket.WebSocket.Role;
import net.tootallnate.websocket.exeptions.InvalidDataException;
import net.tootallnate.websocket.exeptions.InvalidHandshakeException;
import net.tootallnate.websocket.exeptions.LimitExedeedException;
public abstract class Draft {
public enum HandshakeState {
/** Handshake matched this Draft successfully */
MATCHED ,
/** Handshake is does not match this Draft */
NOT_MATCHED ,
/** Handshake matches this Draft but is not complete */
MATCHING
// ,/**Can not yet say anything*/
// PENDING not yet in use
}
public static final byte[] FLASH_POLICY_REQUEST = Charsetfunctions.utf8Bytes( "<policy-file-request/>\0" );
protected Role role = null;
public static ByteBuffer readLine( ByteBuffer buf ) {
ByteBuffer sbuf = ByteBuffer.allocate( buf.remaining() );
byte prev = '0';
byte cur = '0';
while ( buf.hasRemaining() ) {
prev = cur;
cur = buf.get();
sbuf.put( cur );
if( prev == (byte) '\r' && cur == (byte) '\n' ) {
sbuf.limit( sbuf.position() - 2 );
sbuf.position( 0 );
return sbuf;
}
}
//ensure that there wont be any bytes skipped
buf.position( buf.position() - sbuf.position() );
return null;
}
public static String readStringLine( ByteBuffer buf ) {
ByteBuffer b = readLine( buf );
return b == null ? null : Charsetfunctions.stringAscii( b.array(), 0, b.limit() );
}
public static HandshakeBuilder translateHandshakeHttp( ByteBuffer buf ) throws InvalidHandshakeException {
HandshakedataImpl1 draft = new HandshakedataImpl1();
String line = readStringLine( buf );
if( line == null )
throw new InvalidHandshakeException( "could not match http status line" );
String[] firstLineTokens = line.split( " " );// eg. GET / HTTP/1.1
if( firstLineTokens.length < 3 ) {
throw new InvalidHandshakeException( "could not match http status line" );
}
draft.setResourceDescriptor( firstLineTokens[ 1 ] );
line = readStringLine( buf );
while ( line != null && line.length() > 0 ) {
String[] pair = line.split( ":", 2 );
if( pair.length != 2 )
throw new InvalidHandshakeException( "not an http header" );
draft.put( pair[ 0 ], pair[ 1 ].replaceFirst( "^ +", "" ) );
line = readStringLine( buf );
}
return draft;
}
public abstract HandshakeState acceptHandshakeAsClient( Handshakedata request, Handshakedata response ) throws InvalidHandshakeException;
public abstract HandshakeState acceptHandshakeAsServer( Handshakedata handshakedata ) throws InvalidHandshakeException;
protected boolean basicAccept( Handshakedata handshakedata ) {
return handshakedata.getFieldValue( "Upgrade" ).equalsIgnoreCase( "websocket" ) && handshakedata.getFieldValue( "Connection" ).toLowerCase( Locale.ENGLISH ).contains( "upgrade" );
}
public abstract ByteBuffer createBinaryFrame( Framedata framedata ); // TODO Allow to send data on the base of an Iterator or InputStream
public abstract List<Framedata> createFrames( byte[] binary, boolean mask );
public abstract List<Framedata> createFrames( String text, boolean mask );
public abstract void reset();
public List<ByteBuffer> createHandshake( Handshakedata handshakedata, Role ownrole ) {
return createHandshake( handshakedata, ownrole, true );
}
public List<ByteBuffer> createHandshake( Handshakedata handshakedata, Role ownrole, boolean withcontent ) {
StringBuilder bui = new StringBuilder( 100 );
if( ownrole == Role.CLIENT ) {
bui.append( "GET " );
bui.append( handshakedata.getResourceDescriptor() );
bui.append( " HTTP/1.1" );
} else if( ownrole == Role.SERVER ) {
bui.append( "HTTP/1.1 101 " + handshakedata.getHttpStatusMessage() );
} else {
throw new RuntimeException( "unknow role" );
}
bui.append( "\r\n" );
Iterator<String> it = handshakedata.iterateHttpFields();
while ( it.hasNext() ) {
String fieldname = it.next();
String fieldvalue = handshakedata.getFieldValue( fieldname );
bui.append( fieldname );
bui.append( ": " );
bui.append( fieldvalue );
bui.append( "\r\n" );
}
bui.append( "\r\n" );
byte[] httpheader = Charsetfunctions.asciiBytes( bui.toString() );
byte[] content = withcontent ? handshakedata.getContent() : null;
ByteBuffer bytebuffer = ByteBuffer.allocate( ( content == null ? 0 : content.length ) + httpheader.length );
bytebuffer.put( httpheader );
if( content != null )
bytebuffer.put( content );
bytebuffer.flip();
return Collections.singletonList( bytebuffer );
}
public abstract HandshakeBuilder postProcessHandshakeRequestAsClient( HandshakeBuilder request ) throws InvalidHandshakeException;
public abstract HandshakeBuilder postProcessHandshakeResponseAsServer( Handshakedata request, HandshakeBuilder response ) throws InvalidHandshakeException;
public abstract List<Framedata> translateFrame( ByteBuffer buffer ) throws InvalidDataException;
public Handshakedata translateHandshake( ByteBuffer buf ) throws InvalidHandshakeException {
return translateHandshakeHttp( buf );
}
public int checkAlloc( int bytecount ) throws LimitExedeedException , InvalidDataException {
if( bytecount < 0 )
throw new InvalidDataException( CloseFrame.PROTOCOL_ERROR, "Negative count" );
return bytecount;
}
public void setParseMode( Role role ){
this.role = role;
}
public abstract boolean hasCloseHandshake();
}