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

com.twitter.finagle.http2.transport.RichHttpToHttp2ConnectionHandler.scala Maven / Gradle / Ivy

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

import com.twitter.finagle.http2.transport.Http2ClientDowngrader.Message
import com.twitter.logging.Logger
import io.netty.channel.{ChannelHandlerContext, ChannelPromise}
import io.netty.handler.codec.http.{HttpRequest, FullHttpRequest, HttpContent, LastHttpContent, HttpObject}
import io.netty.handler.codec.http2.{Http2ConnectionDecoder, Http2ConnectionEncoder, Http2Settings, HttpConversionUtil, HttpToHttp2ConnectionHandler}
import io.netty.util.concurrent.PromiseCombiner
import scala.util.control.NonFatal

/**
 * An extension of HttpToHttp2ConnectionHandler that handles streaming
 * correctly, by handling [[Message]] instead of [[HttpObject]] directly.
 */
private[http2] class RichHttpToHttp2ConnectionHandler(
    dec: Http2ConnectionDecoder,
    enc: Http2ConnectionEncoder,
    initialSettings: Http2Settings)
  extends HttpToHttp2ConnectionHandler(dec, enc, initialSettings, false) {

  private[this] val log = Logger.get(getClass.getName)

  private[this] def handleMessage(
    ctx: ChannelHandlerContext,
    promise: ChannelPromise,
    msg: HttpObject,
    streamId: Int
  ): Unit = {
    val combiner = new PromiseCombiner()

    try {
      msg match {
        case req: HttpRequest =>
          val headers = HttpConversionUtil.toHttp2Headers(req, false /* validateHeaders */)
          val endStream = req match {
            case full: FullHttpRequest if !full.content.isReadable => true
            case _ => false
          }
          val p = ctx.newPromise()
          combiner.add(p)
          encoder.writeHeaders(ctx, streamId, headers, 0, endStream, p)
        case _ => // nop
      }
      msg match {
        case content: HttpContent =>
          val data = content.content
          if (data.isReadable) {
            val endStream = content.isInstanceOf[LastHttpContent]
            val p = ctx.newPromise()
            combiner.add(p)
            encoder.writeData(ctx, streamId, data, 0, endStream, p)
          }
        case _ => // nop
      }
    } catch {
      case NonFatal(e) =>
        val p = ctx.newPromise()
        p.setFailure(e)
        combiner.add(p)
    } finally {
      combiner.finish(promise)
    }
  }

  override def write(ctx: ChannelHandlerContext, msg: Object, promise: ChannelPromise): Unit = {
    msg match {
      case Message(obj, streamId) =>
        handleMessage(ctx, promise, obj, streamId)
      // TODO we need to add support for writing RSTs and GOAWAYs
      case _ =>
        val wrongType = new IllegalArgumentException(
          s"Expected a Message, got ${msg.getClass.getName} instead.")
        log.error(wrongType, "Tried to write the wrong type to the http2 client pipeline")
        promise.setFailure(wrongType)
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy