All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.twitter.finagle.http2.MultiplexHandlerBuilder.scala Maven / Gradle / Ivy

There is a newer version: 24.2.0
Show newest version
package com.twitter.finagle.http2

import com.twitter.finagle.Stack
import com.twitter.finagle.http2.param.{
  EncoderIgnoreMaxHeaderListSize,
  FrameLoggerNamePrefix,
  FrameLogging,
  HeaderSensitivity
}
import com.twitter.finagle.param.Stats
import com.twitter.finagle.stats.StatsReceiver
import io.netty.channel.ChannelHandler.Sharable
import io.netty.channel.{
  Channel,
  ChannelFuture,
  ChannelFutureListener,
  ChannelHandler,
  ChannelInitializer
}
import io.netty.handler.codec.http2.{
  Http2FrameCodec,
  Http2FrameCodecBuilder,
  Http2HeadersEncoder,
  Http2MultiplexHandler
}

/**
 * Tooling for building a `Http2MultiplexHandler` for clients and servers
 */
private object MultiplexHandlerBuilder {

  // An initializer for use with the client that closes all inbound (pushed)
  // streams since we don't support push-promises on the client at this time.
  @Sharable
  private object ClosePushedStreamsInitializer extends ChannelInitializer[Channel] {
    override def initChannel(ch: Channel): Unit = ch.close()
  }

  /** Attach a "streams" gauge to the channel and manage its lifecycle */
  def addStreamsGauge(
    statsReceiver: StatsReceiver,
    frameCodec: Http2FrameCodec,
    channel: Channel
  ): Unit = {
    // scalafix:off StoreGaugesAsMemberVariables
    val streams = statsReceiver.addGauge("streams") {
      // Note that this isn't thread-safe because the session state is intended to be
      // single-threaded and used only from within the channel, but addressing that
      // would be very challenging.
      frameCodec.connection.numActiveStreams
    }
    // scalafix:on StoreGaugesAsMemberVariables

    // We're removing the gauge on channel closure. This ensures both eager removal of the
    // gauge and ensures that a strong reference to the gauge is captured on the `closeFuture`
    // which will exist for the lifetime of the channel.
    channel.closeFuture.addListener(new ChannelFutureListener {
      def operationComplete(f: ChannelFuture): Unit = streams.remove()
    })
  }

  /** Construct a `Http2MultiplexHandler` for server pipelines */
  def serverFrameCodec(
    params: Stack.Params,
    inboundInitializer: ChannelHandler
  ): (Http2FrameCodec, Http2MultiplexHandler) = {
    val codec = newFrameCodec(params, isServer = true)
    if (trackH2SessionExceptions()) {
      // Add the listener so that we can count up different exceptions seen during the session.
      val oldListener = codec.decoder.frameListener
      val statsReceiver = params[Stats].statsReceiver
      codec.decoder.frameListener(new ExceptionTrackingFrameListener(statsReceiver, oldListener))
    }

    codec -> new Http2MultiplexHandler(inboundInitializer)
  }

  /** Construct a `Http2MultiplexHandler` for client pipelines */
  def clientFrameCodec(
    params: Stack.Params,
    upgradeHandler: Option[ChannelHandler]
  ): (Http2FrameCodec, Http2MultiplexHandler) = {
    val codec = newFrameCodec(params, isServer = false)
    val handler = upgradeHandler match {
      case Some(handler) => new Http2MultiplexHandler(ClosePushedStreamsInitializer, handler)
      case None => new Http2MultiplexHandler(ClosePushedStreamsInitializer)
    }

    codec -> handler
  }

  // Create a new MultiplexHttp2Codec from the supplied configuration
  private def newFrameCodec(params: Stack.Params, isServer: Boolean): Http2FrameCodec = {
    val initialSettings = Settings.fromParams(params, isServer = isServer)
    val builder: Http2FrameCodecBuilder =
      if (isServer) Http2FrameCodecBuilder.forServer()
      else Http2FrameCodecBuilder.forClient()

    builder
      .initialSettings(initialSettings)
      .encoderIgnoreMaxHeaderListSize(
        params[EncoderIgnoreMaxHeaderListSize].ignoreMaxHeaderListSize
      )
      .headerSensitivityDetector(detector(params))

    if (params[FrameLogging].enabled) {
      builder.frameLogger(
        new LoggerPerFrameTypeLogger(params[FrameLoggerNamePrefix].loggerNamePrefix))
    }

    builder.build()
  }

  // Build a sensitivity detector from the params
  private[this] def detector(params: Stack.Params): Http2HeadersEncoder.SensitivityDetector =
    new Http2HeadersEncoder.SensitivityDetector {
      private[this] val sensitivityDetector = params[HeaderSensitivity].sensitivityDetector
      def isSensitive(name: CharSequence, value: CharSequence): Boolean = {
        sensitivityDetector(name, value)
      }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy