@Override
  public void onFillable() {
    // onFillable means that there are encrypted bytes ready to be filled.
    // however we do not fill them here on this callback, but instead wakeup
    // the decrypted readInterest and/or writeFlusher so that they will attempt
    // to do the fill and/or flush again and these calls will do the actually
    // filling.

    if (DEBUG) LOG.debug("onFillable enter {}", getEndPoint());

    // We have received a close handshake, close the end point to send FIN.
    if (_decryptedEndPoint.isInputShutdown()) getEndPoint().close();

    // wake up whoever is doing the fill or the flush so they can
    // do all the filling, unwrapping, wrapping and flushing
    _decryptedEndPoint.getFillInterest().fillable();

    // If we are handshaking, then wake up any waiting write as well as it may have been blocked on
    // the read
    synchronized (_decryptedEndPoint) {
      if (_decryptedEndPoint._flushRequiresFillToProgress) {
        _decryptedEndPoint._flushRequiresFillToProgress = false;
        getExecutor().execute(_runCompletWrite);
      }
    }

    if (DEBUG) LOG.debug("onFillable exit {}", getEndPoint());
  }
  @Override
  public void onFillInterestedFailed(Throwable cause) {
    // this means that the fill interest in encrypted bytes has failed.
    // However we do not handle that here on this callback, but instead wakeup
    // the decrypted readInterest and/or writeFlusher so that they will attempt
    // to do the fill and/or flush again and these calls will do the actually
    // handle the cause.
    _decryptedEndPoint.getFillInterest().onFail(cause);

    boolean failFlusher = false;
    synchronized (_decryptedEndPoint) {
      if (_decryptedEndPoint._flushRequiresFillToProgress) {
        _decryptedEndPoint._flushRequiresFillToProgress = false;
        failFlusher = true;
      }
    }
    if (failFlusher) _decryptedEndPoint.getWriteFlusher().onFail(cause);
  }