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 verifyDataFrame() throws Http2Exception {
    verifyNotProcessingHeaders();
    verifyPayloadLength(payloadLength);

    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.", payloadLength);
    }
  }
  private void verifyPushPromiseFrame() throws Http2Exception {
    verifyNotProcessingHeaders();
    verifyPayloadLength(payloadLength);

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

    // Subtract the length of the promised stream ID field, to determine the length of the
    // rest of the payload (header block fragment + payload).
    int lengthWithoutPromisedId = payloadLength - INT_FIELD_LENGTH;
    if (lengthWithoutPromisedId < flags.getNumPaddingLengthBytes()) {
      throw protocolError("Frame length %d too small for padding.", payloadLength);
    }
  }
  private void verifyHeadersFrame() throws Http2Exception {
    verifyNotProcessingHeaders();
    verifyPayloadLength(payloadLength);

    int lengthWithoutPriority = flags.getNumPaddingLengthBytes();
    if (lengthWithoutPriority < 0) {
      throw protocolError("Frame length too small." + payloadLength);
    }

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

    if (lengthWithoutPriority < flags.getNumPaddingLengthBytes()) {
      throw protocolError("Frame length %d too small for padding.", payloadLength);
    }
  }
 private void readPingFrame(
     ChannelHandlerContext ctx, ByteBuf payload, Http2FrameObserver observer)
     throws Http2Exception {
   ByteBuf data = payload.readSlice(payload.readableBytes());
   if (flags.ack()) {
     observer.onPingAckRead(ctx, data);
   } else {
     observer.onPingRead(ctx, data);
   }
 }
  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);
    }
  }
 private void verifySettingsFrame() throws Http2Exception {
   verifyNotProcessingHeaders();
   verifyPayloadLength(payloadLength);
   if (streamId != 0) {
     throw protocolError("A stream ID must be zero.");
   }
   if (flags.ack() && payloadLength > 0) {
     throw protocolError("Ack settings frame must have an empty payload.");
   }
   if (payloadLength % SETTING_ENTRY_LENGTH > 0) {
     throw protocolError("Frame length %d invalid.", payloadLength);
   }
 }
  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 readSettingsFrame(
     ChannelHandlerContext ctx, ByteBuf payload, Http2FrameObserver observer)
     throws Http2Exception {
   if (flags.ack()) {
     observer.onSettingsAckRead(ctx);
   } else {
     int numSettings = payloadLength / SETTING_ENTRY_LENGTH;
     Http2Settings settings = new Http2Settings();
     for (int index = 0; index < numSettings; ++index) {
       short id = payload.readUnsignedByte();
       long value = payload.readUnsignedInt();
       switch (id) {
         case SETTINGS_HEADER_TABLE_SIZE:
           if (value < 0 || value > Integer.MAX_VALUE) {
             throw protocolError("Invalid value for HEADER_TABLE_SIZE: %d", value);
           }
           settings.maxHeaderTableSize((int) value);
           break;
         case SETTINGS_COMPRESS_DATA:
           if (value != 0 && value != 1) {
             throw protocolError("Invalid value for COMPRESS_DATA: %d", value);
           }
           settings.allowCompressedData(value == 1);
           break;
         case SETTINGS_ENABLE_PUSH:
           if (value != 0 && value != 1) {
             throw protocolError("Invalid value for ENABLE_PUSH: %d", value);
           }
           settings.pushEnabled(value == 1);
           break;
         case SETTINGS_INITIAL_WINDOW_SIZE:
           if (value < 0 || value > Integer.MAX_VALUE) {
             throw protocolError("Invalid value for INITIAL_WINDOW_SIZE: %d", value);
           }
           settings.initialWindowSize((int) value);
           break;
         case SETTINGS_MAX_CONCURRENT_STREAMS:
           if (value < 0 || value > Integer.MAX_VALUE) {
             throw protocolError("Invalid value for MAX_CONCURRENT_STREAMS: %d", value);
           }
           settings.maxConcurrentStreams((int) value);
           break;
         default:
           throw protocolError("Unsupport setting: %d", id);
       }
     }
     observer.onSettingsRead(ctx, settings);
   }
 }
  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);
  }