OkHttp-源码分析(二)

1,302 阅读10分钟
前言

OkHttp系列其他篇章

上篇文章介绍了一些OkHttp的执行流程和OkHttp的前三个拦截器,本文主要介绍一下ConnectInterceptor连接拦截器都做了些什么

ConnectInterceptor源码解析
object ConnectInterceptor : Interceptor {
  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val realChain = chain as RealInterceptorChain
    val exchange = realChain.call.initExchange(chain)
    val connectedChain = realChain.copy(exchange = exchange)
    return connectedChain.proceed(realChain.request)
  }
}
  • 首先初始化一个可交换连接的对象
  • 拷贝一个全新的RealInterceptorChain对象,并且调用该对象的proceed方法执行下一拦截器

这个拦截器主要操作都在initExchange(chain)当中

initExchange(chain)
internal fun initExchange(chain: RealInterceptorChain): Exchange {
  synchronized(this) {
    check(expectMoreExchanges) { "released" }
    check(!responseBodyOpen)
    check(!requestBodyOpen)
  }

  val exchangeFinder = this.exchangeFinder!!
  val codec = exchangeFinder.find(client, chain)
  val result = Exchange(this, eventListener, exchangeFinder, codec)
  this.interceptorScopedExchange = result
  this.exchange = result
  synchronized(this) {
    this.requestBodyOpen = true
    this.responseBodyOpen = true
  }

  if (canceled) throw IOException("Canceled")
  return result
}

上面这段代码的重点在exchangeFinder.find(client, chain)这一行,找到ExchangeCodec通过该对象生成Exchange进行返回。

exchangeFinder.find
fun find(
  client: OkHttpClient,
  chain: RealInterceptorChain
): ExchangeCodec {
  try {
    val resultConnection = findHealthyConnection(
        connectTimeout = chain.connectTimeoutMillis,
        readTimeout = chain.readTimeoutMillis,
        writeTimeout = chain.writeTimeoutMillis,
        pingIntervalMillis = client.pingIntervalMillis,
        connectionRetryEnabled = client.retryOnConnectionFailure,
        doExtensiveHealthChecks = chain.request.method != "GET"
    )
    return resultConnection.newCodec(client, chain)
  } catch (e: RouteException) {
    trackFailure(e.lastConnectException)
    throw e
  } catch (e: IOException) {
    trackFailure(e)
    throw RouteException(e)
  }
}
  • 通过findHealthyConnection找到一个健康的连接resultConnection,也就是一个活跃的连接,
  • 调用 resultConnection.newCodec(client, chain)获取到ExchangeCodec进行返回
findHealthyConnection
@Throws(IOException::class)
private fun findHealthyConnection(
  connectTimeout: Int,
  readTimeout: Int,
  writeTimeout: Int,
  pingIntervalMillis: Int,
  connectionRetryEnabled: Boolean,
  doExtensiveHealthChecks: Boolean
): RealConnection {
  while (true) {
    val candidate = findConnection(
        connectTimeout = connectTimeout,
        readTimeout = readTimeout,
        writeTimeout = writeTimeout,
        pingIntervalMillis = pingIntervalMillis,
        connectionRetryEnabled = connectionRetryEnabled
    )

    if (candidate.isHealthy(doExtensiveHealthChecks)) {
      return candidate
    }
    candidate.noNewExchanges()
    if (nextRouteToTry != null) continue
    val routesLeft = routeSelection?.hasNext() ?: true
    if (routesLeft) continue
    val routesSelectionLeft = routeSelector?.hasNext() ?: true
    if (routesSelectionLeft) continue
    throw IOException("exhausted all routes")
  }
}
  • findConnection从OkHttp的连接池中找到对应的RealConnection进行返回,如果没有的话,则创建一个
  • candidate.isHealthy(doExtensiveHealthChecks)检查该连接是否活跃可用
  • 如果当前连接是不健康,则调用candidate.noNewExchanges()noNewExchanges设置为true,表示该连接存在问题
  • if (routesSelectionLeft) continue判断是否还有其他路由需要尝试,如果有的话则返回true,进入下一循环。
findConnection
@Throws(IOException::class)
private fun findConnection(
  connectTimeout: Int,
  readTimeout: Int,
  writeTimeout: Int,
  pingIntervalMillis: Int,
  connectionRetryEnabled: Boolean
): RealConnection {
  if (call.isCanceled()) throw IOException("Canceled")
  val callConnection = call.connection 
  if (callConnection != null) {
    var toClose: Socket? = null
    synchronized(callConnection) {
      if (callConnection.noNewExchanges || !sameHostAndPort(callConnection.route().address.url)) {
        toClose = call.releaseConnectionNoEvents()
      }
    }

    if (call.connection != null) {
      check(toClose == null)
      return callConnection
    }

    toClose?.closeQuietly()
    eventListener.connectionReleased(call, callConnection)
  }

  refusedStreamCount = 0
  connectionShutdownCount = 0
  otherFailureCount = 0

  if (connectionPool.callAcquirePooledConnection(address, call, null, false)) {
    val result = call.connection!!
    eventListener.connectionAcquired(call, result)
    return result
  }

  val routes: List<Route>?
  val route: Route
  if (nextRouteToTry != null) {
    routes = null
    route = nextRouteToTry!!
    nextRouteToTry = null
  } else if (routeSelection != null && routeSelection!!.hasNext()) {
    routes = null
    route = routeSelection!!.next()
  } else {
    var localRouteSelector = routeSelector
    if (localRouteSelector == null) {
      localRouteSelector = RouteSelector(address, call.client.routeDatabase, call, eventListener)
      this.routeSelector = localRouteSelector
    }
    val localRouteSelection = localRouteSelector.next()
    routeSelection = localRouteSelection
    routes = localRouteSelection.routes

    if (call.isCanceled()) throw IOException("Canceled")

    if (connectionPool.callAcquirePooledConnection(address, call, routes, false)) {
      val result = call.connection!!
      eventListener.connectionAcquired(call, result)
      return result
    }

    route = localRouteSelection.next()
  }

  val newConnection = RealConnection(connectionPool, route)
  call.connectionToCancel = newConnection
  try {
    newConnection.connect(
        connectTimeout,
        readTimeout,
        writeTimeout,
        pingIntervalMillis,
        connectionRetryEnabled,
        call,
        eventListener
    )
  } finally {
    call.connectionToCancel = null
  }
  call.client.routeDatabase.connected(newConnection.route())

  if (connectionPool.callAcquirePooledConnection(address, call, routes, true)) {
    val result = call.connection!!
    nextRouteToTry = route
    newConnection.socket().closeQuietly()
    eventListener.connectionAcquired(call, result)
    return result
  }

  synchronized(newConnection) {
    connectionPool.put(newConnection)
    call.acquireConnectionNoEvents(newConnection)
  }

  eventListener.connectionAcquired(call, newConnection)
  return newConnection
}
  • call.connection首先从RealCall对象中获取 RealConnection

  • 在获取到RealConnection对象时,存在三种情况

    1. RealConnection对象不为空,但是host和port不匹配
    2. RealConnection对象不为空,完全匹配
    3. RealConnection对象为空
  • 判断RealConnection对象是否为空, 如果不为空则检查一下host和port是否匹配。

    • 如果不匹配则调用releaseConnectionNoEvents(),把RealConnection绑定的RealCall队列中对应的RealCall移除,并从ConnectionPool中移除该RealConnection,当前RealCall中绑定的RealConnection设置为空, 并获取当前缓存RealConnectionsocket对象,并关闭该socket
    • 如果noNewExchangesfalse,并且hostport匹配,则返回该callConnection对象
    • 如果RealConnection对象为空,则会通过connectionPoolroute生成一个新的RealConnection对象

我们继续向下看

if (connectionPool.callAcquirePooledConnection(address, call, null, false)) {
  val result = call.connection!!
  eventListener.connectionAcquired(call, result)
  return result
}
callAcquirePooledConnection
fun callAcquirePooledConnection(
  address: Address,
  call: RealCall,
  routes: List<Route>?,
  requireMultiplexed: Boolean
): Boolean {
  for (connection in connections) {
    synchronized(connection) {
      if (requireMultiplexed && !connection.isMultiplexed) return@synchronized
      if (!connection.isEligible(address, routes)) return@synchronized
      call.acquireConnectionNoEvents(connection)
      return true
    }
  }
  return false
}

这里我们主要看connection.isEligible是否符合条件

internal fun isEligible(address: Address, routes: List<Route>?): Boolean {
  assertThreadHoldsLock()
  //如果当前RealConnection复用的RealCall队列的大小大于allocationLimit 限制大小,或者noNewExchanges 为true
  if (calls.size >= allocationLimit || noNewExchanges) return false
  //如果RealConnection 中的route和当前传递进来的地址的address不一致,直接返回false即可
  if (!this.route.address.equalsNonHost(address)) return false
//如果RealConnection 中的route 和传递进来的host一致了,那么说明address和host都一致就是一个资源路径可以返回true。
  if (address.url.host == this.route().address.url.host) {
    return true 
  }
 // 如果host 不一致,且http2Connection 为空,也就不是http 2.0协议,那么就不可能做到不同的资源路径进行复用的情况直接返回
  if (http2Connection == null) return false
//此时就是必须要符合http 2.0的协议才能进行连接的复用,也就是路由可以共享。如果传进来的routes是空 或者通过routeMatchesAny 查找只要出现socket的地址一致且是直接连接的地址,则返回true,返回false一半就是代理的服务。此时就会直接返回
  if (routes == null || !routeMatchesAny(routes)) return false
//想要进一步匹配,那么整个网络请求的HostnameVerifier 校验服务器主机名的必须为OkHostnameVerifier
  if (address.hostnameVerifier !== OkHostnameVerifier) return false
  
//其次匹配HttpUrl和RealConnection的route 能否匹配。如果port 端口不匹配则直接返回false,如果host 匹配则直接返回true。否则就必须要保证noCoalescedConnections 为true (noCoalescedConnections 这个标志位为true,则说明该连接可以共享连接,但是不共享主机.),handshake不为空(说明已经经过了三次握手),且本次校验可以通过主机服务器名的校验。

  if (!supportsUrl(address.url)) return false

  try {
    address.certificatePinner!!.check(address.url.host, handshake()!!.peerCertificates)
  } catch (_: SSLPeerUnverifiedException) {
    return false
  }

  return true 
}
RouteSelector 生成
var localRouteSelector = routeSelector
if (localRouteSelector == null) {
  localRouteSelector = RouteSelector(address, call.client.routeDatabase, call, eventListener)
  this.routeSelector = localRouteSelector
}
class RouteSelector(
  private val address: Address,
  private val routeDatabase: RouteDatabase,
  private val call: Call,
  private val eventListener: EventListener
) {
    ......
    init {
      resetNextProxy(address.url, address.proxy)
    }
    ......
}
private fun resetNextProxy(url: HttpUrl, proxy: Proxy?) {
  fun selectProxies(): List<Proxy> {
    if (proxy != null) return listOf(proxy)

    val uri = url.toUri()
    if (uri.host == null) return immutableListOf(Proxy.NO_PROXY)

    val proxiesOrNull = address.proxySelector.select(uri)
    if (proxiesOrNull.isNullOrEmpty()) return immutableListOf(Proxy.NO_PROXY)

    return proxiesOrNull.toImmutableList()
  }

  eventListener.proxySelectStart(call, url)
  proxies = selectProxies()
  nextProxyIndex = 0
  eventListener.proxySelectEnd(call, url, proxies)
}
  1. 如果proxy不为空,则创建一个含有proxy的集合返回
  2. 如果host为空,则创建一个Proxy.NO_PROXY的集合返回
  3. 否则调用address 的proxySelector 的select方法获取代理集合返回

如果有需要进行代理可以在OkhttpClientBuilder的addProxy中为不同的uri设置自己的代理规则。

接着往下看,这里是获取route,下面创建RealConnection对象需要使用

val localRouteSelection = localRouteSelector.next()
routeSelection = localRouteSelection
routes = localRouteSelection.routes

if (call.isCanceled()) throw IOException("Canceled")
......
route = localRouteSelection.next()

val newConnection = RealConnection(connectionPool, route)

  • 首先调用next获取 Selection对象,通过调用next方法获取route,生成RealConnection对象。下面逐步分析。
@Throws(IOException::class)
operator fun next(): Selection {
  if (!hasNext()) throw NoSuchElementException()

  val routes = mutableListOf<Route>()
  while (hasNextProxy()) {

    val proxy = nextProxy()
    for (inetSocketAddress in inetSocketAddresses) {
      val route = Route(address, proxy, inetSocketAddress)
      if (routeDatabase.shouldPostpone(route)) {
        postponedRoutes += route
      } else {
        routes += route
      }
    }

    if (routes.isNotEmpty()) {
      break
    }
  }

  if (routes.isEmpty()) {
    routes += postponedRoutes
    postponedRoutes.clear()
  }

  return Selection(routes)
}

@Throws(IOException::class)
private fun nextProxy(): Proxy {
  if (!hasNextProxy()) {
    throw SocketException(
        "No route to ${address.url.host}; exhausted proxy configurations:$proxies")
  }
  val result = proxies[nextProxyIndex++]
  resetNextInetSocketAddress(result)
  return result
}
  • nextProxy不断的遍历获取所有的proxy,调用resetNextInetSocketAddress,创建route添加到集合当中
@Throws(IOException::class)
private fun resetNextInetSocketAddress(proxy: Proxy) {
  // Clear the addresses. Necessary if getAllByName() below throws!
  val mutableInetSocketAddresses = mutableListOf<InetSocketAddress>()
  inetSocketAddresses = mutableInetSocketAddresses

  val socketHost: String
  val socketPort: Int
  if (proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.SOCKS) {
    socketHost = address.url.host
    socketPort = address.url.port
  } else {
    val proxyAddress = proxy.address()
    require(proxyAddress is InetSocketAddress) {
      "Proxy.address() is not an InetSocketAddress: ${proxyAddress.javaClass}"
    }
    socketHost = proxyAddress.socketHost
    socketPort = proxyAddress.port
  }

  if (socketPort !in 1..65535) {
    throw SocketException("No route to $socketHost:$socketPort; port is out of range")
  }

  if (proxy.type() == Proxy.Type.SOCKS) {
    mutableInetSocketAddresses += InetSocketAddress.createUnresolved(socketHost, socketPort)
  } else {
    eventListener.dnsStart(call, socketHost)

    // Try each address for best behavior in mixed IPv4/IPv6 environments.
    val addresses = address.dns.lookup(socketHost)
    if (addresses.isEmpty()) {
      throw UnknownHostException("${address.dns} returned no addresses for $socketHost")
    }

    eventListener.dnsEnd(call, socketHost, addresses)

    for (inetAddress in addresses) {
      mutableInetSocketAddresses += InetSocketAddress(inetAddress, socketPort)
    }
  }
}
  • 如果代理类型是Proxy.Type.SOCKS,则先获取address.url的host和port,并调用InetSocketAddress.createUnresolved解析,并添加到mutableInetSocketAddresses集合中。
  • 如果代理类型是Proxy.Type.DIRECT,则先获取address.url的host和port,然后调用address.dnslookup方法获取地址,并通过改地址和port生成InetSocketAddress添加到mutableInetSocketAddresses集合中。
  • 如果代理类型是Proxy.Type.HTTP,则获取传递进来的Proxy中的host和port,调用address.dnslookup方法获取地址,并通过改地址和port生成InetSocketAddress添加到mutableInetSocketAddresses集合中。
newConnection.connect()
fun connect(
  connectTimeout: Int,
  readTimeout: Int,
  writeTimeout: Int,
  pingIntervalMillis: Int,
  connectionRetryEnabled: Boolean,
  call: Call,
  eventListener: EventListener
) {
  check(protocol == null) { "already connected" }

  var routeException: RouteException? = null
  val connectionSpecs = route.address.connectionSpecs
  val connectionSpecSelector = ConnectionSpecSelector(connectionSpecs)

  if (route.address.sslSocketFactory == null) {
    if (ConnectionSpec.CLEARTEXT !in connectionSpecs) {
      throw RouteException(UnknownServiceException(
          "CLEARTEXT communication not enabled for client"))
    }
    val host = route.address.url.host
    if (!Platform.get().isCleartextTrafficPermitted(host)) {
      throw RouteException(UnknownServiceException(
          "CLEARTEXT communication to $host not permitted by network security policy"))
    }
  } else {
    if (Protocol.H2_PRIOR_KNOWLEDGE in route.address.protocols) {
      throw RouteException(UnknownServiceException(
          "H2_PRIOR_KNOWLEDGE cannot be used with HTTPS"))
    }
  }

  while (true) {
    try {
      if (route.requiresTunnel()) {
        connectTunnel(connectTimeout, readTimeout, writeTimeout, call, eventListener)
        if (rawSocket == null) {
          break
        }
      } else {
        connectSocket(connectTimeout, readTimeout, call, eventListener)
      }
      establishProtocol(connectionSpecSelector, pingIntervalMillis, call, eventListener)
      eventListener.connectEnd(call, route.socketAddress, route.proxy, protocol)
      break
    } catch (e: IOException) {
      socket?.closeQuietly()
      rawSocket?.closeQuietly()
      socket = null
      rawSocket = null
      source = null
      sink = null
      handshake = null
      protocol = null
      http2Connection = null
      allocationLimit = 1

      eventListener.connectFailed(call, route.socketAddress, route.proxy, null, e)

      if (routeException == null) {
        routeException = RouteException(e)
      } else {
        routeException.addConnectException(e)
      }

      if (!connectionRetryEnabled || !connectionSpecSelector.connectionFailed(e)) {
        throw routeException
      }
    }
  }

  if (route.requiresTunnel() && rawSocket == null) {
    throw RouteException(ProtocolException(
        "Too many tunnel connections attempted: $MAX_TUNNEL_ATTEMPTS"))
  }

  idleAtNs = System.nanoTime()
}
  • 这里分为两种情况

    • requiresTunnel判断sslSocketFactory不为空,并且代理模式为Proxy.Type.HTTP时,会调用connectTunnel连接socket,其实最终还是调用 connectSocket方法。
    • requiresTunnel为false时,则直接调用connectSocket连接socket
  • 完成上面的操作之后调用establishProtocol方法

connectSocket
@Throws(IOException::class)
private fun connectSocket(
  connectTimeout: Int,
  readTimeout: Int,
  call: Call,
  eventListener: EventListener
) {
  val proxy = route.proxy
  val address = route.address

  val rawSocket = when (proxy.type()) {
    Proxy.Type.DIRECT, Proxy.Type.HTTP -> address.socketFactory.createSocket()!!
    else -> Socket(proxy)
  }
  this.rawSocket = rawSocket

  eventListener.connectStart(call, route.socketAddress, proxy)
  rawSocket.soTimeout = readTimeout
  try {
    Platform.get().connectSocket(rawSocket, route.socketAddress, connectTimeout)
  } catch (e: ConnectException) {
    throw ConnectException("Failed to connect to ${route.socketAddress}").apply {
      initCause(e)
    }
  }

  try {
    source = rawSocket.source().buffer()
    sink = rawSocket.sink().buffer()
  } catch (npe: NullPointerException) {
    if (npe.message == NPE_THROW_WITH_NULL) {
      throw IOException(npe)
    }
  }
}
  • 如果代理模式是Proxy.Type.DIRECT, Proxy.Type.HTTP则通过SocketFactory创建socket对象。
  • 如果代理模式是Proxy.Type.SOCKS,则直接new 一个socekt对象。
  • 通过connectSocket方法,调用socekt的connect方法建立连接。
  • 通过Okio分别获取source 写入流和sink 输出流缓存在RealConnection中。
connectTunnel
@Throws(IOException::class)
private fun connectTunnel(
  connectTimeout: Int,
  readTimeout: Int,
  writeTimeout: Int,
  call: Call,
  eventListener: EventListener
) {
  var tunnelRequest: Request = createTunnelRequest()
  val url = tunnelRequest.url
  for (i in 0 until MAX_TUNNEL_ATTEMPTS) {//MAX_TUNNEL_ATTEMPTS=21
    connectSocket(connectTimeout, readTimeout, call, eventListener)
    tunnelRequest = createTunnel(readTimeout, writeTimeout, tunnelRequest, url) ?: break 
    rawSocket?.closeQuietly()
    rawSocket = null
    sink = null
    source = null
    eventListener.connectEnd(call, route.socketAddress, route.proxy, null)
  }
}
  • 调用createTunnelRequest创建一个通道确认的请求
  • 调用connectSocket创建socket连接
  • createTunnel创建一个隧道,在 21 次重试范围内,进行 socket 和 tunnel 的连接。如果 createTunnel 返回是 null ,说明隧道建立成功。
@Throws(IOException::class)
private fun createTunnelRequest(): Request {
  val proxyConnectRequest = Request.Builder()
      .url(route.address.url)
      .method("CONNECT", null)
      .header("Host", route.address.url.toHostHeader(includeDefaultPort = true))
      .header("Proxy-Connection", "Keep-Alive") // For HTTP/1.0 proxies like Squid.
      .header("User-Agent", userAgent)
      .build()

  val fakeAuthChallengeResponse = Response.Builder()
      .request(proxyConnectRequest)
      .protocol(Protocol.HTTP_1_1)
      .code(HTTP_PROXY_AUTH)
      .message("Preemptive Authenticate")
      .body(EMPTY_RESPONSE)
      .sentRequestAtMillis(-1L)
      .receivedResponseAtMillis(-1L)
      .header("Proxy-Authenticate", "OkHttp-Preemptive")
      .build()

  val authenticatedRequest = route.address.proxyAuthenticator
      .authenticate(route, fakeAuthChallengeResponse)

  return authenticatedRequest ?: proxyConnectRequest
}

这个过程构建了构建了一个代理用的proxyConnectRequest 连接请求对象,以及一个虚假的响应,这个响应会包含proxyConnectRequest。然后通过设置的proxyAuthenticator 进行权限校验。

@Throws(IOException::class)
private fun createTunnel(
  readTimeout: Int,
  writeTimeout: Int,
  tunnelRequest: Request,
  url: HttpUrl
): Request? {
  var nextRequest = tunnelRequest
  // 拼接CONNECT命令
  val requestLine = "CONNECT ${url.toHostHeader(includeDefaultPort = true)} HTTP/1.1"
  while (true) {
    val source = this.source!!
    val sink = this.sink!!
    //对应http/1.1 编码HTTP请求并解码HTTP响应
    val tunnelCodec = Http1ExchangeCodec(null, this, source, sink)
    source.timeout().timeout(readTimeout.toLong(), MILLISECONDS)
    sink.timeout().timeout(writeTimeout.toLong(), MILLISECONDS)
    //发送CONNECT,请求打开隧道连接,
    tunnelCodec.writeRequest(nextRequest.headers, requestLine)
    //完成连接
    tunnelCodec.finishRequest()
    //构建response,操控的是inputStream流
    val response = tunnelCodec.readResponseHeaders(false)!!
        .request(nextRequest)
        .build()
    tunnelCodec.skipConnectBody(response)

    when (response.code) {
      HTTP_OK -> {
        if (!source.buffer.exhausted() || !sink.buffer.exhausted()) {
          throw IOException("TLS tunnel buffered too many bytes!")
        }
        return null
      }

      HTTP_PROXY_AUTH -> {
        nextRequest = route.address.proxyAuthenticator.authenticate(route, response)
            ?: throw IOException("Failed to authenticate with proxy")

        if ("close".equals(response.header("Connection"), ignoreCase = true)) {
          return nextRequest
        }
      }

      else -> throw IOException("Unexpected response code for CONNECT: ${response.code}")
    }
  }
}
establishProtocol
private fun establishProtocol(
  connectionSpecSelector: ConnectionSpecSelector,
  pingIntervalMillis: Int,
  call: Call,
  eventListener: EventListener
) {
  if (route.address.sslSocketFactory == null) {
    if (Protocol.H2_PRIOR_KNOWLEDGE in route.address.protocols) {
      socket = rawSocket
      protocol = Protocol.H2_PRIOR_KNOWLEDGE
      startHttp2(pingIntervalMillis)
      return
    }

    socket = rawSocket
    protocol = Protocol.HTTP_1_1
    return
  }

  eventListener.secureConnectStart(call)
  connectTls(connectionSpecSelector)
  eventListener.secureConnectEnd(call, handshake)

  if (protocol === Protocol.HTTP_2) {
    startHttp2(pingIntervalMillis)
  }
}
  • 判断route.address.sslSocketFactory == null,如果为true的话,则说明该请求为http请求
    • 如果http请求里包涵了“h2_prior_knowledge”协议,代表是一个支持明文的http2请求,所以仍然开启的是http2的连接
    • 如果http请求里不包涵了“h2_prior_knowledge”协议,则正常建立http连接
  • 如果route.address.sslSocketFactory == null,如果为false的话,则说明该请求为https请求
    • 建立TLS连接
    • 建立http2连接
connectTls
private void connectTls(ConnectionSpecSelector connectionSpecSelector) throws IOException {
    Address address = route.address();
    SSLSocketFactory sslSocketFactory = address.sslSocketFactory();
    boolean success = false;
    SSLSocket sslSocket = null;
    try {
      sslSocket = (SSLSocket) sslSocketFactory.createSocket(
          rawSocket, address.url().host(), address.url().port(), true);

      ConnectionSpec connectionSpec = connectionSpecSelector.configureSecureSocket(sslSocket)
      if (connectionSpec.supportsTlsExtensions()) {
        Platform.get().configureTlsExtensions(
            sslSocket, address.url().host(), address.protocols());
      }
      sslSocket.startHandshake();
      SSLSession sslSocketSession = sslSocket.getSession();
      Handshake unverifiedHandshake = Handshake.get(sslSocketSession);
      if (!address.hostnameVerifier().verify(address.url().host(), sslSocketSession)) {
        List<Certificate> peerCertificates = unverifiedHandshake.peerCertificates();
        if (!peerCertificates.isEmpty()) {
          X509Certificate cert = (X509Certificate) peerCertificates.get(0);
          throw new SSLPeerUnverifiedException(
              "Hostname " + address.url().host() + " not verified:"
                  + "\n    certificate: " + CertificatePinner.pin(cert)
                  + "\n    DN: " + cert.getSubjectDN().getName()
                  + "\n    subjectAltNames: " + OkHostnameVerifier.allSubjectAltNames(cert));
        } else {
          throw new SSLPeerUnverifiedException(
              "Hostname " + address.url().host() + " not verified (no certificates)");
        }
      }

      address.certificatePinner().check(address.url().host(),
          unverifiedHandshake.peerCertificates());

      String maybeProtocol = connectionSpec.supportsTlsExtensions()
          ? Platform.get().getSelectedProtocol(sslSocket)
          : null;
     
      socket = sslSocket;
      source = Okio.buffer(Okio.source(socket));
      sink = Okio.buffer(Okio.sink(socket));
      handshake = unverifiedHandshake;
      protocol = maybeProtocol != null
          ? Protocol.get(maybeProtocol)
          : Protocol.HTTP_1_1;
      success = true;
    } catch (AssertionError e) {
      ...
    } finally {
      ...
    }
  }
  • 将刚刚得到的socket通过sslSocketFactory进行包裹,得到一个新的sslSocket
  • 调用configureSecureSocket对sslSocket进行配置协议。
  • 通过supportsTlsExtensions方法查看是否支持TLS拓展
  • 调用sslSocket.startHandshake()方法,开始握手协议
  • 判断(!address.hostnameVerifier().verify(address.url().host(), sslSocketSession)对sslSocket的地址与主机地址进行校验,确保一致可用。
  • 开始 证书校验
  • 将刚才完成握手和协议校验的sslSocket保存起来,并且获得用于IO传输的source、sink
@Throws(IOException::class)
private fun startHttp2(pingIntervalMillis: Int) {
  val socket = this.socket!!
  val source = this.source!!
  val sink = this.sink!!
  socket.soTimeout = 0 
  val http2Connection = Http2Connection.Builder(client = true, taskRunner = TaskRunner.INSTANCE)
      .socket(socket, route.address.url.host, source, sink)
      .listener(this)
      .pingIntervalMillis(pingIntervalMillis)
      .build()
  this.http2Connection = http2Connection
  this.allocationLimit = Http2Connection.DEFAULT_SETTINGS.getMaxConcurrentStreams()
  http2Connection.start()
}
  • 创建一个Http2Connection对象,将socket,host、source、sink传入
@Throws(IOException::class) @JvmOverloads
fun start(sendConnectionPreface: Boolean = true, taskRunner: TaskRunner = TaskRunner.INSTANCE) {
  if (sendConnectionPreface) {
    writer.connectionPreface()
    writer.settings(okHttpSettings)
    val windowSize = okHttpSettings.initialWindowSize
    if (windowSize != DEFAULT_INITIAL_WINDOW_SIZE) {
      writer.windowUpdate(0, (windowSize - DEFAULT_INITIAL_WINDOW_SIZE).toLong())
    }
  }
  taskRunner.newQueue().execute(name = connectionName, block = readerRunnable)
}

-首先调用connectionPreface 往socket里写入序言,说明客户端要开始发送数据了。

  • .Http2Writer.settings 往socket中写入的配置
  • 异步线程执行readerRunnable ,发送完配置数据后等待服务器的响应数据
inner class ReaderRunnable internal constructor(
  internal val reader: Http2Reader
) : Http2Reader.Handler, () -> Unit {
  override fun invoke() {
    var connectionErrorCode = ErrorCode.INTERNAL_ERROR
    var streamErrorCode = ErrorCode.INTERNAL_ERROR
    var errorException: IOException? = null
    try {
      reader.readConnectionPreface(this)
      while (reader.nextFrame(false, this)) {
      }
      connectionErrorCode = ErrorCode.NO_ERROR
      streamErrorCode = ErrorCode.CANCEL
    } catch (e: IOException) {
      errorException = e
      connectionErrorCode = ErrorCode.PROTOCOL_ERROR
      streamErrorCode = ErrorCode.PROTOCOL_ERROR
    } finally {
      close(connectionErrorCode, streamErrorCode, errorException)
      reader.closeQuietly()
    }
  }
  • 从Http2Reader的readConnectionPreface 读取标示头
  • 调用reader.nextFrame 不断的读取下一帧的响应数据,直到结束为止
@Throws(IOException::class)
fun nextFrame(requireSettings: Boolean, handler: Handler): Boolean {
  try {
    source.require(9) 
  } catch (e: EOFException) {
    return false
  }
  val length = source.readMedium()
  if (length > INITIAL_MAX_FRAME_SIZE) {
    throw IOException("FRAME_SIZE_ERROR: $length")
  }
  val type = source.readByte() and 0xff
  val flags = source.readByte() and 0xff
  val streamId = source.readInt() and 0x7fffffff // Ignore reserved bit.
  if (logger.isLoggable(FINE)) logger.fine(frameLog(true, streamId, length, type, flags))

  if (requireSettings && type != TYPE_SETTINGS) {
    throw IOException("Expected a SETTINGS frame but was ${formattedType(type)}")
  }

  when (type) {
    TYPE_DATA -> readData(handler, length, flags, streamId)
    TYPE_HEADERS -> readHeaders(handler, length, flags, streamId)
    TYPE_PRIORITY -> readPriority(handler, length, flags, streamId)
    TYPE_RST_STREAM -> readRstStream(handler, length, flags, streamId)
    TYPE_SETTINGS -> readSettings(handler, length, flags, streamId)
    TYPE_PUSH_PROMISE -> readPushPromise(handler, length, flags, streamId)
    TYPE_PING -> readPing(handler, length, flags, streamId)
    TYPE_GOAWAY -> readGoAway(handler, length, flags, streamId)
    TYPE_WINDOW_UPDATE -> readWindowUpdate(handler, length, flags, streamId)
    else -> source.skip(length.toLong()) 
  }

  return true
}
  • 首先获取数据长度
  • 获取当前数据的类型
  • 获取当前数据的flags
  • 获取当前数据所属的数据流id
  • 根据不同类型读出不同数据
总结

这个拦截器的主要作用是获取一个活跃可用连接,

  1. 首先从RealCall对象中获取,如果找到直接返回,
  2. 如果从RealCall对象没有获取到, 则再从连接池中查找,如果找到也是直接返回,
  3. 如果从连接池中没有找到,则通过路由再次在连接池中查询,如果找到也是直接返回
  4. 如果从连接池中再次没有找到,那么就创建一个RealConnection对象,然后创建Socket连接,封装地址信息,并将该连接添加到连接池中,最后进行返回