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

com.twitter.finagle.netty4.channel.AbstractNetty4ClientChannelInitializer.scala Maven / Gradle / Ivy

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

import com.twitter.finagle.Stack
import com.twitter.finagle.client.Transporter
import com.twitter.finagle.netty4.proxy.HttpProxyConnectHandler
import com.twitter.finagle.netty4.proxy.Netty4ProxyConnectHandler
import com.twitter.finagle.netty4.ssl.client.Netty4ClientSslChannelInitializer
import com.twitter.finagle.param.Label
import com.twitter.finagle.param.Stats
import com.twitter.finagle.transport.Transport
import com.twitter.finagle.util.DefaultLogger
import com.twitter.util.Duration
import io.netty.channel.Channel
import io.netty.channel.ChannelInitializer
import io.netty.handler.proxy.HttpProxyHandler
import io.netty.handler.proxy.Socks5ProxyHandler
import io.netty.handler.timeout.ReadTimeoutHandler
import io.netty.handler.timeout.WriteTimeoutHandler
import java.util.logging.Level

/**
 * Base initializer which installs read / write timeouts and a connection handler
 */
private[netty4] abstract class AbstractNetty4ClientChannelInitializer(params: Stack.Params)
    extends ChannelInitializer[Channel] {

  import Netty4ClientChannelInitializer._

  private[this] val Transport.Liveness(readTimeout, writeTimeout, _) = params[Transport.Liveness]
  private[this] val Label(label) = params[Label]
  private[this] val Stats(stats) = params[Stats]
  private[this] val Transporter.HttpProxyTo(httpHostAndCredentials) =
    params[Transporter.HttpProxyTo]
  private[this] val Transporter.SocksProxy(socksAddress, socksCredentials, socksBypassLocalhost) =
    params[Transporter.SocksProxy]
  private[this] val Transporter.HttpProxy(httpAddress, httpCredentials) =
    params[Transporter.HttpProxy]

  private[this] val channelSnooper =
    if (params[Transport.Verbose].enabled)
      Some(ChannelSnooper.byteSnooper(label)(DefaultLogger.log(Level.INFO, _, _)))
    else
      None

  private[this] val sharedChannelRequestStats =
    if (!stats.isNull) Some(new ChannelRequestStatsHandler.SharedChannelRequestStats(stats))
    else None

  private[this] val sharedChannelStats =
    if (!stats.isNull) {
      val sharedChannelStatsFn = params[SharedChannelStats.Param].fn
      Some(sharedChannelStatsFn(params))
    } else None

  private[this] val exceptionHandler = new ChannelExceptionHandler(stats, DefaultLogger)

  def initChannel(ch: Channel): Unit = {

    // first => last
    // - a request flies from last to first
    // - a response flies from first to last
    //
    // http proxy => ssl => read timeout => write timeout => ...
    // ... => channel stats => req stats => exceptions

    val pipe = ch.pipeline

    sharedChannelStats.foreach { sharedStats =>
      val channelStatsHandler = new ChannelStatsHandler(sharedStats)
      pipe.addFirst(ChannelStatsHandlerKey, channelStatsHandler)
    }

    channelSnooper.foreach(pipe.addFirst(ChannelLoggerHandlerKey, _))

    sharedChannelRequestStats.foreach { sharedStats =>
      val channelRequestStatsHandler = new ChannelRequestStatsHandler(sharedStats)
      pipe.addLast(ChannelRequestStatsHandlerKey, channelRequestStatsHandler)
    }

    if (readTimeout.isFinite && readTimeout > Duration.Zero) {
      val (timeoutValue, timeoutUnit) = readTimeout.inTimeUnit
      pipe.addFirst(ReadTimeoutHandlerKey, new ReadTimeoutHandler(timeoutValue, timeoutUnit))
    }

    if (writeTimeout.isFinite && writeTimeout > Duration.Zero) {
      val (timeoutValue, timeoutUnit) = writeTimeout.inTimeUnit
      pipe.addLast(WriteTimeoutHandlerKey, new WriteTimeoutHandler(timeoutValue, timeoutUnit))
    }

    pipe.addLast("exceptionHandler", exceptionHandler)

    // Add SSL/TLS Channel Initializer to the pipeline.
    pipe.addFirst("sslInit", new Netty4ClientSslChannelInitializer(params))

    // HTTP proxy via `Netty4ProxyConnectHandler`.
    httpAddress.foreach { sa =>
      val proxyHandler = httpCredentials match {
        case None => new HttpProxyHandler(sa)
        case Some(c) => new HttpProxyHandler(sa, c.username, c.password)
      }

      // Use only Finagle's session acquisition timeout
      proxyHandler.setConnectTimeoutMillis(0)

      // TODO: Figure out if it makes sense to bypass localhost connections when HTTP proxy is
      // enabled (see CSL-4409).
      pipe.addFirst("httpProxyConnect", new Netty4ProxyConnectHandler(proxyHandler))
    }

    // TCP tunneling via HTTP proxy (using `HttpProxyConnectHandler`).
    httpHostAndCredentials.foreach {
      case (host, credentials) =>
        pipe.addFirst("httpProxyConnect", new HttpProxyConnectHandler(host, credentials))
    }

    // SOCKS5 proxy via `Netty4ProxyConnectHandler`.
    // We ensure the SOCKS5 proxy is the closest to the edge because we
    // typically see SOCKS5 proxies as ingress proxies and HTTP proxies
    // as egress proxies.  This allows us to simultaneously use a SOCKS5 proxy
    // for ingress followed by an HTTP proxy for egress.
    socksAddress.foreach { sa =>
      val proxyHandler = socksCredentials match {
        case None => new Socks5ProxyHandler(sa)
        case Some((u, p)) => new Socks5ProxyHandler(sa, u, p)
      }

      // Use only Finagle's session acquisition timeout
      proxyHandler.setConnectTimeoutMillis(0)

      pipe.addFirst(
        "socksProxyConnect",
        new Netty4ProxyConnectHandler(
          proxyHandler,
          bypassLocalhostConnections = socksBypassLocalhost)
      )
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy