All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.pusher.java_websocket.drafts.Draft_75 Maven / Gradle / Ivy

There is a newer version: 1.4.1
Show newest version
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();
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy