def parseFrame()

in http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/framing/Http2FrameParsing.scala [54:163]


  def parseFrame(
      tpe: FrameType, flags: ByteFlag, streamId: Int, payload: ByteReader, log: LoggingAdapter): FrameEvent = {
    // TODO: add @switch? seems non-trivial for now
    tpe match {
      case FrameType.GOAWAY =>
        Http2Compliance.requireZeroStreamId(streamId)
        GoAwayFrame(payload.readIntBE(), ErrorCode.byId(payload.readIntBE()), payload.takeAll())

      case FrameType.HEADERS =>
        val pad = Flags.PADDED.isSet(flags)
        val endStream = Flags.END_STREAM.isSet(flags)
        val endHeaders = Flags.END_HEADERS.isSet(flags)
        val priority = Flags.PRIORITY.isSet(flags)

        val paddingLength =
          if (pad) payload.readByte() & 0xFF
          else 0

        val priorityInfo =
          if (priority) {
            val dependencyAndE = payload.readIntBE()
            val weight = payload.readByte() & 0xFF

            val exclusiveFlag = (dependencyAndE >>> 31) == 1 // most significant bit for exclusive flag
            val dependencyId = dependencyAndE & 0x7FFFFFFF // remaining 31 bits for the dependency part
            Http2Compliance.requireNoSelfDependency(streamId, dependencyId)
            Some(PriorityFrame(streamId, exclusiveFlag, dependencyId, weight))
          } else
            None

        HeadersFrame(streamId, endStream, endHeaders, payload.take(payload.remainingSize - paddingLength), priorityInfo)

      case FrameType.DATA =>
        val pad = Flags.PADDED.isSet(flags)
        val endStream = Flags.END_STREAM.isSet(flags)

        val paddingLength =
          if (pad) payload.readByte() & 0xFF
          else 0

        DataFrame(streamId, endStream, payload.take(payload.remainingSize - paddingLength))

      case FrameType.SETTINGS =>
        val ack = Flags.ACK.isSet(flags)
        Http2Compliance.requireZeroStreamId(streamId)

        if (ack) {
          // validate that payload is empty: (6.5)
          if (payload.hasRemaining)
            throw new Http2Compliance.IllegalPayloadInSettingsAckFrame(payload.remainingSize,
              s"SETTINGS ACK frame MUST NOT contain payload (spec 6.5)!")

          SettingsAckFrame(Nil) // TODO if we were to send out settings, here would be the spot to include the acks for the ones we've sent out
        } else {

          if (payload.remainingSize % 6 != 0) throw new Http2Compliance.IllegalPayloadLengthInSettingsFrame(
            payload.remainingSize, "SETTINGS payload MUST be a multiple of multiple of 6 octets")
          SettingsFrame(readSettings(payload, log))
        }

      case FrameType.WINDOW_UPDATE =>
        // TODO: check frame size
        // TODO: check flags
        val increment = payload.readIntBE()
        Http2Compliance.requirePositiveWindowUpdateIncrement(streamId, increment)

        WindowUpdateFrame(streamId, increment)

      case FrameType.CONTINUATION =>
        val endHeaders = Flags.END_HEADERS.isSet(flags)

        ContinuationFrame(streamId, endHeaders, payload.remainingData)

      case FrameType.PING =>
        // see 6.7
        Http2Compliance.requireFrameSize(payload.remainingSize, 8)
        Http2Compliance.requireZeroStreamId(streamId)
        val ack = Flags.ACK.isSet(flags)
        PingFrame(ack, payload.remainingData)

      case FrameType.RST_STREAM =>
        Http2Compliance.requireFrameSize(payload.remainingSize, 4)
        Http2Compliance.requireNonZeroStreamId(streamId)
        RstStreamFrame(streamId, ErrorCode.byId(payload.readIntBE()))

      case FrameType.PRIORITY =>
        Http2Compliance.requireFrameSize(payload.remainingSize, 5)
        val streamDependency = payload.readIntBE() // whole word
        val exclusiveFlag = (streamDependency >>> 31) == 1 // most significant bit for exclusive flag
        val dependencyId = streamDependency & 0x7FFFFFFF // remaining 31 bits for the dependency part
        val priority = payload.readByte() & 0xFF
        Http2Compliance.requireNoSelfDependency(streamId, dependencyId)
        PriorityFrame(streamId, exclusiveFlag, dependencyId, priority)

      case FrameType.PUSH_PROMISE =>
        val pad = Flags.PADDED.isSet(flags)
        val endHeaders = Flags.END_HEADERS.isSet(flags)

        val paddingLength =
          if (pad) payload.readByte() & 0xFF
          else 0

        val promisedStreamId = payload.readIntBE()

        PushPromiseFrame(streamId, endHeaders, promisedStreamId, payload.take(payload.remainingSize - paddingLength))

      case tpe => // TODO: remove once all stream types are defined
        UnknownFrameEvent(tpe, flags, streamId, payload.remainingData)
    }
  }