docs: private void readyInbound(VirtualConnection inVC) {
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) {
              Tr.entry(tc, "readyInbound, vc=" + getVCHash());
          }
  
          // Encrypted buffer from the network.
          WsByteBuffer netBuffer = getDeviceReadInterface().getBuffer();
          // Verify the buffer is not null first.
          if (netBuffer == null) {
              // Unable to handle this condition. TCP could not get any more data
              // and looked to punt to the only available channel above.
              if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                  Tr.debug(tc, "Received null buffer so closing connection.");
              }
              close(inVC, null);
              if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) {
                  Tr.exit(tc, "readyInbound, vc=" + getVCHash());
              }
              return;
          }
          netBuffer.flip();
          // Unencrypted buffer from the ssl engine output to be handed up to the application.
          WsByteBuffer decryptedNetBuffer = null;
          // Encrypted buffer from the ssl engine to be sent sent out on the network.
          WsByteBuffer encryptedAppBuffer = null;
          // Result output from the SSL engine.
          SSLEngineResult result = null;
          // Indicate that an error occurred and buffers should be cleaned up.
          boolean errorOccurred = false;
  
          if (TraceComponent.isAnyTracingEnabled() && tc.isEventEnabled()) {
              Tr.event(tc, "Initial read bytes: " + netBuffer.limit());
          }
  
          // Note, net and decNet buffers will always be one buffer for discrimination.
          // See if discrimination happened. Init method saved discState if so.
          if (discState == null) {
              // Allocate a new buffer for the results of decrypting the network buffer.
              decryptedNetBuffer = SSLUtils.allocateByteBuffer(
                                                               getAppBufferSize(), sslChannel.getConfig().getDecryptBuffersDirect());
          } else {
              // Extract the results of the call to the engine during discrimination.
              result = discState.getEngineResult();
              // Extract output buffer used during discrimination.
              decryptedNetBuffer = discState.getDecryptedNetBuffer();
              // Determine the resulting position and limit of networkBuffer after unwrap in discrimination.
              netBuffer.position(discState.getNetBufferPosition());
              netBuffer.limit(discState.getNetBufferLimit());
          }
          // Line up buffers needed for the SSL handshake. These are temporary.
          encryptedAppBuffer = SSLUtils.allocateByteBuffer(getPacketBufferSize(), true);
  
          try {
              if (discState == null) {
                  // Since data is ready now, we can't call handleHandshake yet, unwrap first.
                  if (TraceComponent.isAnyTracingEnabled() && tc.isEventEnabled()) {
                      Tr.event(tc, "Before unwrap\r\n\tnetBuf: " + SSLUtils.getBufferTraceInfo(netBuffer)
                                   + "\r\n\tdecBuf: " + SSLUtils.getBufferTraceInfo(decryptedNetBuffer));
                  }
                  // Should not get any app data until handshake is done
                  // Protect JSSE from potential SSL packet sizes that are too big.
                  int savedLimit = SSLUtils.adjustBufferForJSSE(netBuffer, getPacketBufferSize());
  
                  // Have the SSL engine inspect the first packet.
                  result = getSSLEngine().unwrap(
                                                 netBuffer.getWrappedByteBuffer(),
                                                 decryptedNetBuffer.getWrappedByteBuffer());
                  if (0 < result.bytesProduced()) {
                      decryptedNetBuffer.flip();
                  }
                  if (TraceComponent.isAnyTracingEnabled() && tc.isEventEnabled()) {
                      Tr.event(tc,
                               "After unwrap\r\n\tnetBuf: " + SSLUtils.getBufferTraceInfo(netBuffer)
                                   + "\r\n\tdecBuf: " + SSLUtils.getBufferTraceInfo(decryptedNetBuffer)
                                   + "\r\n\tstatus=" + result.getStatus()
                                   + " HSstatus=" + result.getHandshakeStatus()
                                   + " consumed=" + result.bytesConsumed()
                                   + " produced=" + result.bytesProduced());
                  }
  
                  // If adjustments were made for the JSSE, restore them.
                  if (-1 != savedLimit) {
                      netBuffer.limit(savedLimit);
                  }
                  // If all data was consumed, clear the buffer.
                  if (netBuffer.remaining() == 0) {
                      netBuffer.clear();
                  }
              }
  
              // Build a callback for the asynchronous SSL handshake
              MyHandshakeCompletedCallback callback = new MyHandshakeCompletedCallback(this, netBuffer, decryptedNetBuffer, encryptedAppBuffer, FlowType.INBOUND);
              // Continue the SSL handshake. Do this with asynchronous handShake
              result = SSLUtils.handleHandshake(this, netBuffer, decryptedNetBuffer,
                                                encryptedAppBuffer, result, callback, false);
              // Check to see if the work was able to be done synchronously.
              if (result != null) {
                  // Handshake is done.
                  readyInboundPostHandshake(netBuffer, decryptedNetBuffer,
                                            encryptedAppBuffer, result.getHandshakeStatus());
              } else {
                  // Handshake is being done asynchronously.
                  if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) {
                      Tr.exit(tc, "readyInbound");
                  }
                  return;
              }
  
          } catch (IOException ioe) {
              // no FFDC required
              if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                  Tr.debug(tc, "Caught ioexception, " + ioe);
              }
              errorOccurred = true;
              // Handle the handshake error.
              getChannel().getHandshakeErrorTracker().noteHandshakeError(ioe);
              close(inVC, ioe);
          } catch (ReadOnlyBufferException robe) {
              FFDCFilter.processException(robe, getClass().getName(), "359", this);
              if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                  Tr.debug(tc, "Caught read-only exception, " + robe);
              }
              errorOccurred = true;
              close(inVC, robe);
          }
  
          // Clean up buffers if an error occurred. Note, connection was already closed.
          if (errorOccurred) {
              if (decryptedNetBuffer != null) {
                  decryptedNetBuffer.release();
                  decryptedNetBuffer = null;
              }
              netBuffer.release();
              netBuffer = null;
              getDeviceReadInterface().setBuffers(null);
              if (encryptedAppBuffer != null) {
                  encryptedAppBuffer.release();
                  encryptedAppBuffer = null;
              }
          }
  
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) {
              Tr.exit(tc, "readyInbound");
          }
      }