in httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/SSLIOSession.java [349:458]
private void doHandshake(final IOSession protocolSession) throws IOException {
boolean handshaking = true;
SSLEngineResult result = null;
while (handshaking) {
HandshakeStatus handshakeStatus = this.sslEngine.getHandshakeStatus();
// Work-around for what appears to be a bug in Conscrypt SSLEngine that does not
// transition into the handshaking state upon #closeOutbound() call but still
// has some handshake data stuck in its internal buffer.
if (handshakeStatus == HandshakeStatus.NOT_HANDSHAKING && outboundClosedCount.get() > 0) {
handshakeStatus = HandshakeStatus.NEED_WRAP;
}
switch (handshakeStatus) {
case NEED_WRAP:
// Generate outgoing handshake data
this.session.getLock().lock();
try {
// Acquire buffers
final ByteBuffer outEncryptedBuf = this.outEncrypted.acquire();
// Just wrap an empty buffer because there is no data to write.
result = doWrap(EMPTY_BUFFER, outEncryptedBuf);
if (result.getStatus() != SSLEngineResult.Status.OK || result.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) {
handshaking = false;
}
break;
} finally {
this.session.getLock().unlock();
}
case NEED_UNWRAP:
// Process incoming handshake data
// Acquire buffers
final ByteBuffer inEncryptedBuf = this.inEncrypted.acquire();
final ByteBuffer inPlainBuf = this.inPlain.acquire();
// Perform operations
inEncryptedBuf.flip();
try {
result = doUnwrap(inEncryptedBuf, inPlainBuf);
} finally {
inEncryptedBuf.compact();
}
try {
if (!inEncryptedBuf.hasRemaining() && result.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP) {
throw new SSLException("Input buffer is full");
}
} finally {
// Release inEncrypted if empty
if (inEncryptedBuf.position() == 0) {
this.inEncrypted.release();
}
}
if (this.status.compareTo(Status.CLOSING) >= 0) {
this.inPlain.release();
}
if (result.getStatus() != SSLEngineResult.Status.OK) {
handshaking = false;
}
break;
case NEED_TASK:
doRunTask();
break;
case NOT_HANDSHAKING:
handshaking = false;
break;
}
}
// The SSLEngine has just finished handshaking. This value is only generated by a call
// to SSLEngine.wrap()/unwrap() when that call finishes a handshake.
// It is never generated by SSLEngine.getHandshakeStatus().
if (result != null && result.getHandshakeStatus() == HandshakeStatus.FINISHED) {
this.handshakeStateRef.set(TLSHandShakeState.COMPLETE);
this.session.setSocketTimeout(this.socketTimeout);
if (this.verifier != null) {
this.tlsDetails = this.verifier.verify(this.targetEndpoint, this.sslEngine);
}
String applicationProtocol;
if (this.tlsDetails == null) {
final SSLSession sslSession = this.sslEngine.getSession();
try {
applicationProtocol = this.sslEngine.getApplicationProtocol();
} catch (final UnsupportedOperationException e) {
// If the underlying provider does not support the operation, the getApplicationProtocol() method throws an UnsupportedOperationException.
// In this case, we fall back to "http/1.1" as the application protocol.
// This is a workaround to allow older applications that do not support the getApplicationProtocol() method to continue working.
// This workaround is temporary and is meant to maintain compatibility with older systems.
applicationProtocol = "http/1.1";
}
this.tlsDetails = new TlsDetails(sslSession, applicationProtocol);
}
ensureHandler().connected(protocolSession);
if (this.sessionStartCallback != null) {
this.sessionStartCallback.execute(this);
}
final FutureCallback<SSLSession> resultCallback = handshakeCallbackRef.getAndSet(null);
if (resultCallback != null) {
resultCallback.completed(sslEngine.getSession());
}
}
}