@Override
  protected final void channelRead0(final ChannelHandlerContext ctx, final ByteBuf msg) {
    final Optional<ByteBuf> nextMsg = nextHandshakeMessage(msg);
    if (nextMsg.isPresent()) {
      ctx.writeAndFlush(nextMsg.get());
    } else if (handshaker.getStatus() != Handshaker.HandshakeStatus.SUCCESS) {
      LOG.debug("waiting for more bytes");
    } else {

      final Bytes nodeId = handshaker.partyPubKey().getEncodedBytes();
      if (!localNode.isReady()) {
        // If we're handling a connection before the node is fully up, just disconnect
        LOG.debug("Rejecting connection because local node is not ready {}", nodeId);
        disconnect(ctx, DisconnectMessage.DisconnectReason.UNKNOWN);
        return;
      }

      LOG.trace("Sending framed hello");

      // Exchange keys done
      final Framer framer = this.framerProvider.buildFramer(handshaker.secrets());

      final ByteToMessageDecoder deFramer =
          new DeFramer(
              framer,
              subProtocols,
              localNode,
              expectedPeer,
              connectionEventDispatcher,
              connectionFuture,
              metricsSystem,
              inboundInitiated,
              peerTable);

      ctx.channel()
          .pipeline()
          .replace(this, "DeFramer", deFramer)
          .addBefore("DeFramer", "validate", new ValidateFirstOutboundMessage(framer));

      ctx.writeAndFlush(new OutboundMessage(null, HelloMessage.create(localNode.getPeerInfo())))
          .addListener(
              ff -> {
                if (ff.isSuccess()) {
                  LOG.trace("Successfully wrote hello message");
                }
              });
      msg.retain();
      ctx.fireChannelRead(msg);
    }
  }
Generates the next message in the handshake sequence.

@param msg Incoming Message
@return Optional of the next Handshake message that needs to be returned to the peer
ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/rlpx/connections/netty/AbstractHandshakeHandler.java