content: package org.java_websocket.drafts;
  
  import java.nio.BufferUnderflowException;
  import java.nio.ByteBuffer;
  import java.security.MessageDigest;
  import java.security.NoSuchAlgorithmException;
  import java.util.Arrays;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Random;
  
  import org.java_websocket.WebSocket.Role;
  import org.java_websocket.exceptions.IncompleteHandshakeException;
  import org.java_websocket.exceptions.InvalidDataException;
  import org.java_websocket.exceptions.InvalidFrameException;
  import org.java_websocket.exceptions.InvalidHandshakeException;
  import org.java_websocket.framing.CloseFrame;
  import org.java_websocket.framing.CloseFrameBuilder;
  import org.java_websocket.framing.Framedata;
  import org.java_websocket.framing.Framedata.Opcode;
  import org.java_websocket.handshake.ClientHandshake;
  import org.java_websocket.handshake.ClientHandshakeBuilder;
  import org.java_websocket.handshake.HandshakeBuilder;
  import org.java_websocket.handshake.Handshakedata;
  import org.java_websocket.handshake.ServerHandshake;
  import org.java_websocket.handshake.ServerHandshakeBuilder;
  
  public class Draft_76 extends Draft_75 {
  	private boolean failed = false;
  	private static final byte[] closehandshake = { -1, 0 };
  	
  	private final Random reuseableRandom = new Random();
  	
  
  	public static byte[] createChallenge( String key1, String key2, byte[] key3 ) throws InvalidHandshakeException {
  		byte[] part1 = getPart( key1 );
  		byte[] part2 = getPart( key2 );
  		byte[] challenge = new byte[ 16 ];
  		challenge[ 0 ] = part1[ 0 ];
  		challenge[ 1 ] = part1[ 1 ];
  		challenge[ 2 ] = part1[ 2 ];
  		challenge[ 3 ] = part1[ 3 ];
  		challenge[ 4 ] = part2[ 0 ];
  		challenge[ 5 ] = part2[ 1 ];
  		challenge[ 6 ] = part2[ 2 ];
  		challenge[ 7 ] = part2[ 3 ];
  		challenge[ 8 ] = key3[ 0 ];
  		challenge[ 9 ] = key3[ 1 ];
  		challenge[ 10 ] = key3[ 2 ];
  		challenge[ 11 ] = key3[ 3 ];
  		challenge[ 12 ] = key3[ 4 ];
  		challenge[ 13 ] = key3[ 5 ];
  		challenge[ 14 ] = key3[ 6 ];
  		challenge[ 15 ] = key3[ 7 ];
  		MessageDigest md5;
  		try {
  			md5 = MessageDigest.getInstance( "MD5" );
  		} catch ( NoSuchAlgorithmException e ) {
  			throw new RuntimeException( e );
  		}
  		return md5.digest( challenge );
  	}
  
  	private static String generateKey() {
  		Random r = new Random();
  		long maxNumber = 4294967295L;
  		long spaces = r.nextInt( 12 ) + 1;
  		int max = new Long( maxNumber / spaces ).intValue();
  		max = Math.abs( max );
  		int number = r.nextInt( max ) + 1;
  		long product = number * spaces;
  		String key = Long.toString( product );
  		// always insert atleast one random character
  		int numChars = r.nextInt( 12 ) + 1;
  		for( int i = 0 ; i < numChars ; i++ ) {
  			int position = r.nextInt( key.length() );
  			position = Math.abs( position );
  			char randChar = (char) ( r.nextInt( 95 ) + 33 );
  			// exclude numbers here
  			if( randChar >= 48 && randChar <= 57 ) {
  				randChar -= 15;
  			}
  			key = new StringBuilder( key ).insert( position, randChar ).toString();
  		}
  		for( int i = 0 ; i < spaces ; i++ ) {
  			int position = r.nextInt( key.length() - 1 ) + 1;
  			position = Math.abs( position );
  			key = new StringBuilder( key ).insert( position, "\u0020" ).toString();
  		}
  		return key;
  	}
  
  	private static byte[] getPart( String key ) throws InvalidHandshakeException {
  		try {
  			long keyNumber = Long.parseLong( key.replaceAll( "[^0-9]", "" ) );
  			long keySpace = key.split( "\u0020" ).length - 1;
  			if( keySpace == 0 ) {
  				throw new InvalidHandshakeException( "invalid Sec-WebSocket-Key (/key2/)" );
  			}
  			long part = new Long( keyNumber / keySpace );
  			return new byte[]{ (byte) ( part >> 24 ), (byte) ( ( part << 8 ) >> 24 ), (byte) ( ( part << 16 ) >> 24 ), (byte) ( ( part << 24 ) >> 24 ) };
  		} catch ( NumberFormatException e ) {
  			throw new InvalidHandshakeException( "invalid Sec-WebSocket-Key (/key1/ or /key2/)" );
  		}
  	}
  
  	@Override
  	public HandshakeState acceptHandshakeAsClient( ClientHandshake request, ServerHandshake response ) {
  		if( failed ) {
  			return HandshakeState.NOT_MATCHED;
  		}
  
  		try {
  			if( !response.getFieldValue( "Sec-WebSocket-Origin" ).equals( request.getFieldValue( "Origin" ) ) || !basicAccept( response ) ) {
  				return HandshakeState.NOT_MATCHED;
  			}
  			byte[] content = response.getContent();
  			if( content == null || content.length == 0 ) {
  				throw new IncompleteHandshakeException();
  			}
  			if( Arrays.equals( content, createChallenge( request.getFieldValue( "Sec-WebSocket-Key1" ), request.getFieldValue( "Sec-WebSocket-Key2" ), request.getContent() ) ) ) {
  				return HandshakeState.MATCHED;
  			} else {
  				return HandshakeState.NOT_MATCHED;
  			}
  		} catch ( InvalidHandshakeException e ) {
  			throw new RuntimeException( "bad handshakerequest", e );
  		}
  	}
  
  	@Override
  	public HandshakeState acceptHandshakeAsServer( ClientHandshake handshakedata ) {
  		if( handshakedata.getFieldValue( "Upgrade" ).equals( "WebSocket" ) && handshakedata.getFieldValue( "Connection" ).contains( "Upgrade" ) && handshakedata.getFieldValue( "Sec-WebSocket-Key1" ).length() > 0 && !handshakedata.getFieldValue( "Sec-WebSocket-Key2" ).isEmpty() && handshakedata.hasFieldValue( "Origin" ) )
  			return HandshakeState.MATCHED;
  		return HandshakeState.NOT_MATCHED;
  	}
  
  	@Override
  	public ClientHandshakeBuilder postProcessHandshakeRequestAsClient( ClientHandshakeBuilder request ) {
  		request.put( "Upgrade", "WebSocket" );
  		request.put( "Connection", "Upgrade" );
  		request.put( "Sec-WebSocket-Key1", generateKey() );
  		request.put( "Sec-WebSocket-Key2", generateKey() );
  
  		if( !request.hasFieldValue( "Origin" ) ) {
  			request.put( "Origin", "random" + reuseableRandom.nextInt() );
  		}
  
  		byte[] key3 = new byte[ 8 ];
  		reuseableRandom.nextBytes( key3 );
  		request.setContent( key3 );
  		return request;
  
  	}
  
  	@Override
  	public HandshakeBuilder postProcessHandshakeResponseAsServer( ClientHandshake request, ServerHandshakeBuilder response ) throws InvalidHandshakeException {
  		response.setHttpStatusMessage( "WebSocket Protocol Handshake" );
  		response.put( "Upgrade", "WebSocket" );
  		response.put( "Connection", request.getFieldValue( "Connection" ) ); // to respond to a Connection keep alive
  		response.put( "Sec-WebSocket-Origin", request.getFieldValue( "Origin" ) );
  		String location = "ws://" + request.getFieldValue( "Host" ) + request.getResourceDescriptor();
  		response.put( "Sec-WebSocket-Location", location );
  		String key1 = request.getFieldValue( "Sec-WebSocket-Key1" );
  		String key2 = request.getFieldValue( "Sec-WebSocket-Key2" );
  		byte[] key3 = request.getContent();
  		if( key1 == null || key2 == null || key3 == null || key3.length != 8 ) {
  			throw new InvalidHandshakeException( "Bad keys" );
  		}
  		response.setContent( createChallenge( key1, key2, key3 ) );
  		return response;
  	}
  
  	@Override
  	public Handshakedata translateHandshake( ByteBuffer buf ) throws InvalidHandshakeException {
  
  		HandshakeBuilder bui = translateHandshakeHttp( buf, role );
  		// the first drafts are lacking a protocol number which makes them difficult to distinguish. Sec-WebSocket-Key1 is typical for draft76
  		if( ( bui.hasFieldValue( "Sec-WebSocket-Key1" ) || role == Role.CLIENT ) && !bui.hasFieldValue( "Sec-WebSocket-Version" ) ) {
  			byte[] key3 = new byte[ role == Role.SERVER ? 8 : 16 ];
  			try {
  				buf.get( key3 );
  			} catch ( BufferUnderflowException e ) {
  				throw new IncompleteHandshakeException( buf.capacity() + 16 );
  			}
  			bui.setContent( key3 );
  
  		}
  		return bui;
  	}
  
  	@Override
  	public List<Framedata> translateFrame( ByteBuffer buffer ) throws InvalidDataException {
  		buffer.mark();
  		List<Framedata> frames = super.translateRegularFrame( buffer );
  		if( frames == null ) {
  			buffer.reset();
  			frames = readyframes;
  			readingState = true;
  			if( currentFrame == null )
  				currentFrame = ByteBuffer.allocate( 2 );
  			else {
  				throw new InvalidFrameException();
  			}
  			if( buffer.remaining() > currentFrame.remaining() ) {
  				throw new InvalidFrameException();
  			} else {
  				currentFrame.put( buffer );
  			}
  			if( !currentFrame.hasRemaining() ) {
  				if( Arrays.equals( currentFrame.array(), closehandshake ) ) {
  					frames.add( new CloseFrameBuilder( CloseFrame.NORMAL ) );
  					return frames;
  				}
  				else{
  					throw new InvalidFrameException();
  				}
  			} else {
  				readyframes = new LinkedList<Framedata>();
  				return frames;
  			}
  		} else {
  			return frames;
  		}
  	}
  	@Override
  	public ByteBuffer createBinaryFrame( Framedata framedata ) {
  		if( framedata.getOpcode() == Opcode.CLOSING )
  			return ByteBuffer.wrap( closehandshake );
  		return super.createBinaryFrame( framedata );
  	}
  
  	@Override
  	public CloseHandshakeType getCloseHandshakeType() {
  		return CloseHandshakeType.ONEWAY;
  	}
  
  	@Override
  	public Draft copyInstance() {
  		return new Draft_76();
  	}
  }
  