private void readPushPromiseFrame(
      final ChannelHandlerContext ctx, ByteBuf payload, Http2FrameObserver observer)
      throws Http2Exception {
    final int pushPromiseStreamId = streamId;
    int padding = flags.readPaddingLength(payload);
    final int promisedStreamId = readUnsignedInt(payload);

    // Create a handler that invokes the observer when the header block is complete.
    headersContinuation =
        new HeadersContinuation() {
          @Override
          public int getStreamId() {
            return pushPromiseStreamId;
          }

          @Override
          public void processFragment(
              boolean endOfHeaders, ByteBuf fragment, int padding, Http2FrameObserver observer)
              throws Http2Exception {
            builder().addFragment(fragment, ctx.alloc(), endOfHeaders);
            if (endOfHeaders) {
              Http2Headers headers = builder().buildHeaders();
              observer.onPushPromiseRead(
                  ctx, pushPromiseStreamId, promisedStreamId, headers, padding);
              close();
            }
          }
        };

    // Process the initial fragment, invoking the observer's callback if end of headers.
    final ByteBuf fragment = payload.readSlice(payload.readableBytes() - padding);
    headersContinuation.processFragment(flags.endOfHeaders(), fragment, padding, observer);
  }
  private void readContinuationFrame(ByteBuf payload, Http2FrameObserver observer)
      throws Http2Exception {
    int padding = flags.readPaddingLength(payload);

    // Process the initial fragment, invoking the observer's callback if end of headers.
    final ByteBuf continuationFragment = payload.readSlice(payload.readableBytes() - padding);
    headersContinuation.processFragment(
        flags.endOfHeaders(), continuationFragment, padding, observer);
  }
  private void verifyContinuationFrame() throws Http2Exception {
    verifyPayloadLength(payloadLength);

    if (headersContinuation == null) {
      throw protocolError("Received %s frame but not currently processing headers.", frameType);
    }

    if (streamId != headersContinuation.getStreamId()) {
      throw protocolError(
          "Continuation stream ID does not match pending headers. "
              + "Expected %d, but received %d.",
          headersContinuation.getStreamId(), streamId);
    }

    if (!flags.isPaddingLengthValid()) {
      throw protocolError("Pad high is set but pad low is not");
    }

    if (payloadLength < flags.getNumPaddingLengthBytes()) {
      throw protocolError("Frame length %d too small for padding.", payloadLength);
    }
  }
 @Override
 public void close() {
   if (headersContinuation != null) {
     headersContinuation.close();
   }
 }
  private void readHeadersFrame(
      final ChannelHandlerContext ctx, ByteBuf payload, Http2FrameObserver observer)
      throws Http2Exception {
    final int headersStreamId = streamId;
    final Http2Flags headersFlags = flags;
    int padding = flags.readPaddingLength(payload);

    // The callback that is invoked is different depending on whether priority information
    // is present in the headers frame.
    if (flags.priorityPresent()) {
      long word1 = payload.readUnsignedInt();
      final boolean exclusive = (word1 & 0x80000000L) > 0;
      final int streamDependency = (int) (word1 & 0x7FFFFFFFL);
      final short weight = (short) (payload.readUnsignedByte() + 1);
      final ByteBuf fragment = payload.readSlice(payload.readableBytes() - padding);

      // Create a handler that invokes the observer when the header block is complete.
      headersContinuation =
          new HeadersContinuation() {
            @Override
            public int getStreamId() {
              return headersStreamId;
            }

            @Override
            public void processFragment(
                boolean endOfHeaders, ByteBuf fragment, int padding, Http2FrameObserver observer)
                throws Http2Exception {
              builder().addFragment(fragment, ctx.alloc(), endOfHeaders);
              if (endOfHeaders) {
                Http2Headers headers = builder().buildHeaders();
                observer.onHeadersRead(
                    ctx,
                    headersStreamId,
                    headers,
                    streamDependency,
                    weight,
                    exclusive,
                    padding,
                    headersFlags.endOfStream(),
                    headersFlags.endOfSegment());
                close();
              }
            }
          };

      // Process the initial fragment, invoking the observer's callback if end of headers.
      headersContinuation.processFragment(flags.endOfHeaders(), fragment, padding, observer);
      return;
    }

    // The priority fields are not present in the frame. Prepare a continuation that invokes
    // the observer callback without priority information.
    headersContinuation =
        new HeadersContinuation() {
          @Override
          public int getStreamId() {
            return headersStreamId;
          }

          @Override
          public void processFragment(
              boolean endOfHeaders, ByteBuf fragment, int padding, Http2FrameObserver observer)
              throws Http2Exception {
            builder().addFragment(fragment, ctx.alloc(), endOfHeaders);
            if (endOfHeaders) {
              Http2Headers headers = builder().buildHeaders();
              observer.onHeadersRead(
                  ctx,
                  headersStreamId,
                  headers,
                  padding,
                  headersFlags.endOfStream(),
                  headersFlags.endOfSegment());
              close();
            }
          }
        };

    // Process the initial fragment, invoking the observer's callback if end of headers.
    final ByteBuf fragment = payload.readSlice(payload.readableBytes() - padding);
    headersContinuation.processFragment(flags.endOfHeaders(), fragment, padding, observer);
  }