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 readDataFrame(
      ChannelHandlerContext ctx, ByteBuf payload, Http2FrameObserver observer)
      throws Http2Exception {
    int dataPadding = flags.readPaddingLength(payload);

    // Determine how much data there is to read by removing the trailing
    // padding.
    int dataLength = payload.readableBytes() - dataPadding;
    if (dataLength < 0) {
      throw protocolError("Frame payload too small for padding.");
    }

    ByteBuf data = payload.readSlice(dataLength);
    observer.onDataRead(
        ctx,
        streamId,
        data,
        dataPadding,
        flags.endOfStream(),
        flags.endOfSegment(),
        flags.compressed());
    payload.skipBytes(payload.readableBytes());
  }
  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);
  }