src/main/java/org/java_websocket/SSLSocketChannel.java
private boolean doHandshake() throws IOException {
		SSLEngineResult result;
		HandshakeStatus handshakeStatus;

		// NioSslPeer's fields myAppData and peerAppData are supposed to be large enough to hold all message data the peer
		// will send and expects to receive from the other peer respectively. Since the messages to be exchanged will usually be less
		// than 16KB long the capacity of these fields should also be smaller. Here we initialize these two local buffers
		// to be used for the handshake, while keeping client's buffers at the same size.
		int appBufferSize = engine.getSession().getApplicationBufferSize();
		myAppData = ByteBuffer.allocate( appBufferSize );
		peerAppData = ByteBuffer.allocate( appBufferSize );
		myNetData.clear();
		peerNetData.clear();

		handshakeStatus = engine.getHandshakeStatus();
		boolean handshakeComplete = false;
		while( !handshakeComplete) {
			switch(handshakeStatus) {
				case FINISHED:
					handshakeComplete = !this.peerNetData.hasRemaining();
					if (handshakeComplete)
						return true;
					socketChannel.write(this.peerNetData);
					break;
				case NEED_UNWRAP:
					if( socketChannel.read( peerNetData ) < 0 ) {
						if( engine.isInboundDone() && engine.isOutboundDone() ) {
							return false;
						}
						try {
							engine.closeInbound();
						} catch ( SSLException e ) {
							//Ignore, cant do anything against this exception
						}
						engine.closeOutbound();
						// After closeOutbound the engine will be set to WRAP state, in order to try to send a close message to the client.
						handshakeStatus = engine.getHandshakeStatus();
						break;
					}
					peerNetData.flip();
					try {
						result = engine.unwrap( peerNetData, peerAppData );
						peerNetData.compact();
						handshakeStatus = result.getHandshakeStatus();
					} catch ( SSLException sslException ) {
						engine.closeOutbound();
						handshakeStatus = engine.getHandshakeStatus();
						break;
					}
					switch(result.getStatus()) {
						case OK:
							break;
						case BUFFER_OVERFLOW:
							// Will occur when peerAppData's capacity is smaller than the data derived from peerNetData's unwrap.
							peerAppData = enlargeApplicationBuffer( peerAppData );
							break;
						case BUFFER_UNDERFLOW:
							// Will occur either when no data was read from the peer or when the peerNetData buffer was too small to hold all peer's data.
							peerNetData = handleBufferUnderflow( peerNetData );
							break;
						case CLOSED:
							if( engine.isOutboundDone() ) {
								return false;
							} else {
								engine.closeOutbound();
								handshakeStatus = engine.getHandshakeStatus();
								break;
							}
						default:
							throw new IllegalStateException( "Invalid SSL status: " + result.getStatus() );
					}
					break;
				case NEED_WRAP:
					myNetData.clear();
					try {
						result = engine.wrap( myAppData, myNetData );
						handshakeStatus = result.getHandshakeStatus();
					} catch ( SSLException sslException ) {
						engine.closeOutbound();
						handshakeStatus = engine.getHandshakeStatus();
						break;
					}
					switch(result.getStatus()) {
						case OK:
							myNetData.flip();
							while( myNetData.hasRemaining() ) {
								socketChannel.write( myNetData );
							}
							break;
						case BUFFER_OVERFLOW:
							// Will occur if there is not enough space in myNetData buffer to write all the data that would be generated by the method wrap.
							// Since myNetData is set to session's packet size we should not get to this point because SSLEngine is supposed
							// to produce messages smaller or equal to that, but a general handling would be the following:
							myNetData = enlargePacketBuffer( myNetData );
							break;
						case BUFFER_UNDERFLOW:
							throw new SSLException( "Buffer underflow occured after a wrap. I don't think we should ever get here." );
						case CLOSED:
							try {
								myNetData.flip();
								while( myNetData.hasRemaining() ) {
									socketChannel.write( myNetData );
								}
								// At this point the handshake status will probably be NEED_UNWRAP so we make sure that peerNetData is clear to read.
								peerNetData.clear();
							} catch ( Exception e ) {
								handshakeStatus = engine.getHandshakeStatus();
							}
							break;
						default:
							throw new IllegalStateException( "Invalid SSL status: " + result.getStatus() );
					}
					break;
				case NEED_TASK:
					Runnable task;
					while( ( task = engine.getDelegatedTask() ) != null ) {
						executor.execute( task );
					}
					handshakeStatus = engine.getHandshakeStatus();
					break;

				case NOT_HANDSHAKING:
					break;
				default:
					throw new IllegalStateException( "Invalid SSL status: " + handshakeStatus );
			}
		}

		return true;

	}
private boolean doHandshake() throws IOException {
		SSLEngineResult result;
		HandshakeStatus handshakeStatus;

		// NioSslPeer's fields myAppData and peerAppData are supposed to be large enough to hold all message data the peer
		// will send and expects to receive from the other peer respectively. Since the messages to be exchanged will usually be less
		// than 16KB long the capacity of these fields should also be smaller. Here we initialize these two local buffers
		// to be used for the handshake, while keeping client's buffers at the same size.
		int appBufferSize = engine.getSession().getApplicationBufferSize();
		myAppData = ByteBuffer.allocate( appBufferSize );
		peerAppData = ByteBuffer.allocate( appBufferSize );
		myNetData.clear();
		peerNetData.clear();

		handshakeStatus = engine.getHandshakeStatus();
		boolean handshakeComplete = false;
		while( !handshakeComplete) {
			switch(handshakeStatus) {
				case FINISHED:
					handshakeComplete = !this.peerNetData.hasRemaining();
					if (handshakeComplete)
						return true;
					socketChannel.write(this.peerNetData);
					break;
				case NEED_UNWRAP:
					if( socketChannel.read( peerNetData ) < 0 ) {
						if( engine.isInboundDone() && engine.isOutboundDone() ) {
							return false;
						}
						try {
							engine.closeInbound();
						} catch ( SSLException e ) {
							//Ignore, cant do anything against this exception
						}
						engine.closeOutbound();
						// After closeOutbound the engine will be set to WRAP state, in order to try to send a close message to the client.
						handshakeStatus = engine.getHandshakeStatus();
						break;
					}
					peerNetData.flip();
					try {
						result = engine.unwrap( peerNetData, peerAppData );
						peerNetData.compact();
						handshakeStatus = result.getHandshakeStatus();
					} catch ( SSLException sslException ) {
						engine.closeOutbound();
						handshakeStatus = engine.getHandshakeStatus();
						break;
					}
					switch(result.getStatus()) {
						case OK:
							break;
						case BUFFER_OVERFLOW:
							// Will occur when peerAppData's capacity is smaller than the data derived from peerNetData's unwrap.
							peerAppData = enlargeApplicationBuffer( peerAppData );
							break;
						case BUFFER_UNDERFLOW:
							// Will occur either when no data was read from the peer or when the peerNetData buffer was too small to hold all peer's data.
							peerNetData = handleBufferUnderflow( peerNetData );
							break;
						case CLOSED:
							if( engine.isOutboundDone() ) {
								return false;
							} else {
								engine.closeOutbound();
								handshakeStatus = engine.getHandshakeStatus();
								break;
							}
						default:
							throw new IllegalStateException( "Invalid SSL status: " + result.getStatus() );
					}
					break;
				case NEED_WRAP:
					myNetData.clear();
					try {
						result = engine.wrap( myAppData, myNetData );
						handshakeStatus = result.getHandshakeStatus();
					} catch ( SSLException sslException ) {
						engine.closeOutbound();
						handshakeStatus = engine.getHandshakeStatus();
						break;
					}
					switch(result.getStatus()) {
						case OK:
							myNetData.flip();
							while( myNetData.hasRemaining() ) {
								socketChannel.write( myNetData );
							}
							break;
						case BUFFER_OVERFLOW:
							// Will occur if there is not enough space in myNetData buffer to write all the data that would be generated by the method wrap.
							// Since myNetData is set to session's packet size we should not get to this point because SSLEngine is supposed
							// to produce messages smaller or equal to that, but a general handling would be the following:
							myNetData = enlargePacketBuffer( myNetData );
							break;
						case BUFFER_UNDERFLOW:
							throw new SSLException( "Buffer underflow occured after a wrap. I don't think we should ever get here." );
						case CLOSED:
							try {
								myNetData.flip();
								while( myNetData.hasRemaining() ) {
									socketChannel.write( myNetData );
								}
								// At this point the handshake status will probably be NEED_UNWRAP so we make sure that peerNetData is clear to read.
								peerNetData.clear();
							} catch ( Exception e ) {
								handshakeStatus = engine.getHandshakeStatus();
							}
							break;
						default:
							throw new IllegalStateException( "Invalid SSL status: " + result.getStatus() );
					}
					break;
				case NEED_TASK:
					Runnable task;
					while( ( task = engine.getDelegatedTask() ) != null ) {
						executor.execute( task );
					}
					handshakeStatus = engine.getHandshakeStatus();
					break;

				case NOT_HANDSHAKING:
					break;
				default:
					throw new IllegalStateException( "Invalid SSL status: " + handshakeStatus );
			}
		}

		return true;

	}
Implements the handshake protocol between two peers, required for the establishment of the SSL/TLS connection.
During the handshake, encryption configuration information - such as the list of available cipher suites - will be exchanged
and if the handshake is successful will lead to an established SSL/TLS session.
<p>
<p/>
A typical handshake will usually contain the following steps:
<p>
<ul>
<li>1. wrap:     ClientHello</li>
<li>2. unwrap:   ServerHello/Cert/ServerHelloDone</li>
<li>3. wrap:     ClientKeyExchange</li>
<li>4. wrap:     ChangeCipherSpec</li>
<li>5. wrap:     Finished</li>
<li>6. unwrap:   ChangeCipherSpec</li>
<li>7. unwrap:   Finished</li>
</ul>
<p/>
Handshake is also used during the end of the session, in order to properly close the connection between the two peers.
A proper connection close will typically include the one peer sending a CLOSE message to another, and then wait for
the other's CLOSE message to close the transport link. The other peer from his perspective would read a CLOSE message
from his peer and then enter the handshake procedure to send his own CLOSE message as well.

@return True if the connection handshake was successful or false if an error occurred.
@throws IOException - if an error occurs during read/write to the socket channel.