@Override
    public void complete() {
      bufferPool.release(buffer);

      super.complete();

      if (frame.getType() == ControlFrameType.GO_AWAY) {
        // After sending a GO_AWAY we need to hard close the connection.
        // Recipients will know the last good stream id and act accordingly.
        close();
      }
      IStream stream = getStream();
      if (stream != null && stream.isClosed()) removeStream(stream);
    }
  private void generateAndEnqueueControlFrame(
      IStream stream, ControlFrame frame, long timeout, TimeUnit unit, Callback callback) {
    try {
      // Synchronization is necessary, since we may have concurrent replies
      // and those needs to be generated and enqueued atomically in order
      // to maintain a correct compression context
      synchronized (this) {
        ByteBuffer buffer = generator.control(frame);
        LOG.debug("Queuing {} on {}", frame, stream);
        ControlFrameBytes frameBytes = new ControlFrameBytes(stream, callback, frame, buffer);
        if (timeout > 0) frameBytes.task = scheduler.schedule(frameBytes, timeout, unit);

        // Special handling for PING frames, they must be sent as soon as possible
        if (ControlFrameType.PING == frame.getType()) prepend(frameBytes);
        else append(frameBytes);
      }
    } catch (Exception x) {
      notifyCallbackFailed(callback, x);
    }
  }
  @Override
  public void onControlFrame(ControlFrame frame) {
    notifyIdle(idleListener, false);
    try {
      LOG.debug("Processing {}", frame);

      if (goAwaySent.get()) {
        LOG.debug("Skipped processing of {}", frame);
        return;
      }

      switch (frame.getType()) {
        case SYN_STREAM:
          {
            onSyn((SynStreamFrame) frame);
            break;
          }
        case SYN_REPLY:
          {
            onReply((SynReplyFrame) frame);
            break;
          }
        case RST_STREAM:
          {
            onRst((RstStreamFrame) frame);
            break;
          }
        case SETTINGS:
          {
            onSettings((SettingsFrame) frame);
            break;
          }
        case NOOP:
          {
            // Just ignore it
            break;
          }
        case PING:
          {
            onPing((PingFrame) frame);
            break;
          }
        case GO_AWAY:
          {
            onGoAway((GoAwayFrame) frame);
            break;
          }
        case HEADERS:
          {
            onHeaders((HeadersFrame) frame);
            break;
          }
        case WINDOW_UPDATE:
          {
            onWindowUpdate((WindowUpdateFrame) frame);
            break;
          }
        case CREDENTIAL:
          {
            onCredential((CredentialFrame) frame);
            break;
          }
        default:
          {
            throw new IllegalStateException();
          }
      }
    } finally {
      notifyIdle(idleListener, true);
    }
  }