@Override
  public void incomingFrame(Frame frame) {
    // Incoming frames are always non concurrent because
    // they are read and parsed with a single thread, and
    // therefore there is no need for synchronization.

    // This extension requires the RSV1 bit set only in the first frame.
    // Subsequent continuation frames don't have RSV1 set, but are compressed.
    if (frame.getType().isData()) incomingCompressed = frame.isRsv1();

    if (OpCode.isControlFrame(frame.getOpCode()) || !frame.hasPayload() || !incomingCompressed) {
      nextIncomingFrame(frame);
      return;
    }

    boolean appendTail = frame.isFin();
    ByteBuffer payload = frame.getPayload();
    int remaining = payload.remaining();
    byte[] input = new byte[remaining + (appendTail ? TAIL_BYTES.length : 0)];
    payload.get(input, 0, remaining);
    if (appendTail) System.arraycopy(TAIL_BYTES, 0, input, remaining, TAIL_BYTES.length);

    forwardIncoming(frame, decompress(input));

    if (frame.isFin()) incomingCompressed = false;
  }
    private void fragment(FrameEntry entry, boolean first) {
      Frame frame = entry.frame;
      ByteBuffer payload = frame.getPayload();
      int remaining = payload.remaining();
      int length = Math.min(remaining, maxLength);
      finished = length == remaining;

      boolean continuation = frame.getType().isContinuation() || !first;
      DataFrame fragment = new DataFrame(frame, continuation);
      boolean fin = frame.isFin() && finished;
      fragment.setFin(fin);

      int limit = payload.limit();
      int newLimit = payload.position() + length;
      payload.limit(newLimit);
      ByteBuffer payloadFragment = payload.slice();
      payload.limit(limit);
      fragment.setPayload(payloadFragment);
      if (LOG.isDebugEnabled()) LOG.debug("Fragmented {}->{}", frame, fragment);
      payload.position(newLimit);

      nextOutgoingFrame(fragment, this, entry.batchMode);
    }