ConnectInterceptor 连接拦截器源码解析

源码查看
/**
 * Opens a connection to the target server and proceeds to the next interceptor. The network might
 * be used for the returned response, or to validate a cached response with a conditional GET.
 */
object ConnectInterceptor : Interceptor {
  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val realChain = chain as RealInterceptorChain
    // 关键点获取exchange
    val exchange = realChain.call.initExchange(chain)
    // copy方法,构造传参创建RealInterceptorChain对象,前面有讲过,不再赘述
    val connectedChain = realChain.copy(exchange = exchange)
    // 通过proceed获取response
    return connectedChain.proceed(realChain.request)
  }
}

通过注释可知,该拦截器用于开启一个到指定server的连接,并处理下一个拦截器。该网络可能用于返回response或者通过发起get请求来刷新response。

1、RealCall中initExchange(chain)方法

查找一个新的或者缓存池中的连接,用于即将到来的request和response

  /** Finds a new or pooled connection to carry a forthcoming request and response. */
  internal fun initExchange(chain: RealInterceptorChain): Exchange {
   // 同步代码块检查状态
   synchronized(this) {
      check(expectMoreExchanges) { "released" }
      check(!responseBodyOpen)
      check(!requestBodyOpen)
    }
    
    val exchangeFinder = this.exchangeFinder!!
    // 1.1 查询codec
    val codec = exchangeFinder.find(client, chain)
    // 1.2 构建exchange,用于处理IO操作
    val result = Exchange(this, eventListener, exchangeFinder, codec)
    // 缓存interceptorScopedExchange
    this.interceptorScopedExchange = result
    // 对exchange进行赋值操作
    this.exchange = result
    
    // 修改requestBodyOpen、responseBodyOpen状态值
    synchronized(this) {
      this.requestBodyOpen = true
      this.responseBodyOpen = true
    }
    // 如果处理过程中被取消,抛出io异常
    if (canceled) throw IOException("Canceled")
    return result
  }
  • 1.1 ExchangeFinder的exchangeFinder.find(client, chain)
  fun find(
    client: OkHttpClient,
    chain: RealInterceptorChain
  ): ExchangeCodec {
    try {
        // 1.1.1 查询HealthyConnection
      val resultConnection = findHealthyConnection(
          connectTimeout = chain.connectTimeoutMillis,
          readTimeout = chain.readTimeoutMillis,
          writeTimeout = chain.writeTimeoutMillis,
          pingIntervalMillis = client.pingIntervalMillis,
          connectionRetryEnabled = client.retryOnConnectionFailure,
          doExtensiveHealthChecks = chain.request.method != "GET"
      )
      // 1.1.2 通过resultConnection 获取ExchangeCodec实例
      return resultConnection.newCodec(client, chain)
    } catch (e: RouteException) {
      // 跟踪异常处理
      trackFailure(e.lastConnectException)
      throw e
    } catch (e: IOException) {
      trackFailure(e)
      throw RouteException(e)
    }
  }
  • 1.1.1 ExchangeFinder的findHealthyConnection()

1、返回一个连接到主机的流,优先选择存的的连接,未找到的话会从连接池中进行查找,最后仍未发现的话会创建新的连接。
2、每次运行查询前检查该连接是否被取消

  /**
   * Returns a connection to host a new stream. This prefers the existing connection if it exists,
   * then the pool, finally building a new connection.
   *
   * This checks for cancellation before each blocking operation.
   */
  @Throws(IOException::class)
  private fun findConnection(
    connectTimeout: Int,
    readTimeout: Int,
    writeTimeout: Int,
    pingIntervalMillis: Int,
    connectionRetryEnabled: Boolean
  ): RealConnection {
    if (call.isCanceled()) throw IOException("Canceled")
       
      // 通过call尝试复用连接
    // Attempt to reuse the connection from the call.
    // This may be mutated by releaseConnectionNoEvents()!
    val callConnection = call.connection 
    if (callConnection != null) {
      var toClose: Socket? = null
      synchronized(callConnection) {
        // 没有新的exchange或者不是相同的host和port
        if (callConnection.noNewExchanges || !sameHostAndPort(callConnection.route().address.url)) {
         // 释放socket连接
          toClose = call.releaseConnectionNoEvents()
        }
      }

      // If the call's connection wasn't released, reuse it. We don't call connectionAcquired() here
      // because we already acquired it.
      
      // call的连接不为空,复用连接
      if (call.connection != null) {
        check(toClose == null)
        return callConnection
      }
    
      // The call's connection was released.
      // toClose不为空,关闭socket
      toClose?.closeQuietly()
      eventListener.connectionReleased(call, callConnection)
    }

    // We need a new connection. Give it fresh stats.
    // 创建新连接,刷新状态值
    refusedStreamCount = 0
    connectionShutdownCount = 0
    otherFailureCount = 0

    // Attempt to get a connection from the pool.
    // 尝试从连接池中获取连接
    if (connectionPool.callAcquirePooledConnection(address, call, null, false)) {
      val result = call.connection!!
      eventListener.connectionAcquired(call, result)
      return result
    }
    
    // Nothing in the pool. Figure out what route we'll try next.
    // 未从连接池中查找到连接,尝试找出route(路由)
    val routes: List<Route>?
    val route: Route
    if (nextRouteToTry != null) {
       // 路由不为空,合并连接
      // Use a route from a preceding coalesced connection.
      routes = null
      route = nextRouteToTry!!
      // 将nextRouteToTry置空
      nextRouteToTry = null
    } else if (routeSelection != null && routeSelection!!.hasNext()) {
       // 使用现有的routeSelection
      // Use a route from an existing route selection.
      routes = null
      route = routeSelection!!.next()
    } else {
      // Compute a new route selection. This is a blocking operation!
      // 计算一个新的routeSelection 阻塞操作
      var localRouteSelector = routeSelector
      if (localRouteSelector == null) {
        // 创建RouteSelector
        localRouteSelector = RouteSelector(address, call.client.routeDatabase, call, eventListener)
        this.routeSelector = localRouteSelector
      }
      // 通过localRouteSelector获取localRouteSelection
      val localRouteSelection = localRouteSelector.next()
      routeSelection = localRouteSelection
      // 获取路由
      routes = localRouteSelection.routes

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

      // Now that we have a set of IP addresses, make another attempt at getting a connection from
      // the pool. We have a better chance of matching thanks to connection coalescing.
      // 从connectionPool中获取已被回收的call中获取连接
      if (connectionPool.callAcquirePooledConnection(address, call, routes, false)) {
        val result = call.connection!!
        eventListener.connectionAcquired(call, result)
        return result
      }
        // 从localRouteSelection获取路由
      route = localRouteSelection.next()
    }

    // Connect. Tell the call about the connecting call so async cancels work.
    // 创建新的连接
    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 we raced another call connecting to this host, coalesce the connections. This makes for 3
    // different lookups in the connection pool!
    // 如果连接支持多路复用,在连接池中查看三个不同的连接,确保连接的多路复用
    if (connectionPool.callAcquirePooledConnection(address, call, routes, true)) {
      // 通过call获取连接
      val result = call.connection!!
      nextRouteToTry = route
      // 关闭socket
      newConnection.socket().closeQuietly()
      eventListener.connectionAcquired(call, result)
      return result
    }
    
    // 将连接放到连接池中
    synchronized(newConnection) {
      connectionPool.put(newConnection)
      call.acquireConnectionNoEvents(newConnection)
    }
    
    eventListener.connectionAcquired(call, newConnection)
    // 返回连接
    return newConnection
  }
  • 流程图
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RE2YQ6tp-1629800296660)(evernotecid://995B5497-6FAA-4C24-AE87-2F5BD5F2399B/appyinxiangcom/18006758/ENResource/p168)]

  • 1.1.2 resultConnection.newCodec方法

 @Throws(SocketException::class)
  internal fun newCodec(client: OkHttpClient, chain: RealInterceptorChain): ExchangeCodec {
    val socket = this.socket!!
    val source = this.source!!
    val sink = this.sink!!
    val http2Connection = this.http2Connection

    return if (http2Connection != null) {
       // http2类型ExchangeCodec
      Http2ExchangeCodec(client, this, chain, http2Connection)
    } else {
        // http1类型ExchangeCodec
      socket.soTimeout = chain.readTimeoutMillis()
      source.timeout().timeout(chain.readTimeoutMillis.toLong(), MILLISECONDS)
      sink.timeout().timeout(chain.writeTimeoutMillis.toLong(), MILLISECONDS)
      Http1ExchangeCodec(client, this, source, sink)
    }
  }

根据http2Connection判断使用Http2ExchangeCodec还是Http1ExchangeCodec作为具体实现类

  • 1.2 构建 Exchange(this, eventListener, exchangeFinder, codec)

用于传递一个独立的请求和响应对,连接在ExchangeCodec上的管理和事件,用于处理实际的IO操作,实际上就是一个RealCall、EventListener、ExchangeFinder、ExchangeCodec的一个包装类,将参数及回调进行处理并通过EventListener将事件状态进行回调

/**
 * Transmits a single HTTP request and a response pair. This layers connection management and events
 * on [ExchangeCodec], which handles the actual I/O.
 */
class Exchange(
  internal val call: RealCall,
  internal val eventListener: EventListener,
  internal val finder: ExchangeFinder,
  private val codec: ExchangeCodec
) {
  /** Returns true if the request body need not complete before the response body starts. */
  internal var isDuplex: Boolean = false
    private set

  internal val connection: RealConnection = codec.connection

  internal val isCoalescedConnection: Boolean
    get() = finder.address.url.host != connection.route().address.url.host

// 写请求头
  @Throws(IOException::class)
  fun writeRequestHeaders(request: Request) {
    try {
      eventListener.requestHeadersStart(call)
      codec.writeRequestHeaders(request)
      eventListener.requestHeadersEnd(call, request)
    } catch (e: IOException) {
      eventListener.requestFailed(call, e)
      trackFailure(e)
      throw e
    }
  }

// 创建请求体
  @Throws(IOException::class)
  fun createRequestBody(request: Request, duplex: Boolean): Sink {
    this.isDuplex = duplex
    val contentLength = request.body!!.contentLength()
    eventListener.requestBodyStart(call)
    val rawRequestBody = codec.createRequestBody(request, contentLength)
    return RequestBodySink(rawRequestBody, contentLength)
  }

// 刷新请求
  @Throws(IOException::class)
  fun flushRequest() {
    try {
      codec.flushRequest()
    } catch (e: IOException) {
      eventListener.requestFailed(call, e)
      trackFailure(e)
      throw e
    }
  }

// 完成请求
  @Throws(IOException::class)
  fun finishRequest() {
    try {
      codec.finishRequest()
    } catch (e: IOException) {
      eventListener.requestFailed(call, e)
      trackFailure(e)
      throw e
    }
  }

  fun responseHeadersStart() {
    eventListener.responseHeadersStart(call)
  }

// 读取响应头
  @Throws(IOException::class)
  fun readResponseHeaders(expectContinue: Boolean): Response.Builder? {
    try {
      val result = codec.readResponseHeaders(expectContinue)
      result?.initExchange(this)
      return result
    } catch (e: IOException) {
      eventListener.responseFailed(call, e)
      trackFailure(e)
      throw e
    }
  }

  fun responseHeadersEnd(response: Response) {
    eventListener.responseHeadersEnd(call, response)
  }

// 通过response构建RealResponseBody
  @Throws(IOException::class)
  fun openResponseBody(response: Response): ResponseBody {
    try {
      val contentType = response.header("Content-Type")
      val contentLength = codec.reportedContentLength(response)
      val rawSource = codec.openResponseBodySource(response)
      val source = ResponseBodySource(rawSource, contentLength)
      return RealResponseBody(contentType, contentLength, source.buffer())
    } catch (e: IOException) {
      eventListener.responseFailed(call, e)
      trackFailure(e)
      throw e
    }
  }

  @Throws(IOException::class)
  fun trailers(): Headers = codec.trailers()

// 创建webSocketStream
  @Throws(SocketException::class)
  fun newWebSocketStreams(): RealWebSocket.Streams {
    call.timeoutEarlyExit()
    return codec.connection.newWebSocketStreams(this)
  }

  fun webSocketUpgradeFailed() {
    bodyComplete(-1L, responseDone = true, requestDone = true, e = null)
  }

  fun noNewExchangesOnConnection() {
    codec.connection.noNewExchanges()
  }

  fun cancel() {
    codec.cancel()
  }

  /**
   * Revoke this exchange's access to streams. This is necessary when a follow-up request is
   * required but the preceding exchange hasn't completed yet.
   */
  fun detachWithViolence() {
    codec.cancel()
    call.messageDone(this, requestDone = true, responseDone = true, e = null)
  }

  private fun trackFailure(e: IOException) {
    finder.trackFailure(e)
    codec.connection.trackFailure(call, e)
  }

  fun <E : IOException?> bodyComplete(
    bytesRead: Long,
    responseDone: Boolean,
    requestDone: Boolean,
    e: E
  ): E {
    if (e != null) {
      trackFailure(e)
    }
    if (requestDone) {
      if (e != null) {
        eventListener.requestFailed(call, e)
      } else {
        eventListener.requestBodyEnd(call, bytesRead)
      }
    }
    if (responseDone) {
      if (e != null) {
        eventListener.responseFailed(call, e)
      } else {
        eventListener.responseBodyEnd(call, bytesRead)
      }
    }
    return call.messageDone(this, requestDone, responseDone, e)
  }

  fun noRequestBody() {
    call.messageDone(this, requestDone = true, responseDone = false, e = null)
  }

// 请求体压缩,实际上就是一个IO操作,同时将状态通过eventListener进行通知
  /** A request body that fires events when it completes. */
  private inner class RequestBodySink(
    delegate: Sink,
    /** The exact number of bytes to be written, or -1L if that is unknown. */
    private val contentLength: Long
  ) : ForwardingSink(delegate) {
    private var completed = false
    private var bytesReceived = 0L
    private var closed = false

    @Throws(IOException::class)
    override fun write(source: Buffer, byteCount: Long) {
      check(!closed) { "closed" }
      if (contentLength != -1L && bytesReceived + byteCount > contentLength) {
        throw ProtocolException(
            "expected $contentLength bytes but received ${bytesReceived + byteCount}")
      }
      try {
        super.write(source, byteCount)
        this.bytesReceived += byteCount
      } catch (e: IOException) {
        throw complete(e)
      }
    }

    @Throws(IOException::class)
    override fun flush() {
      try {
        super.flush()
      } catch (e: IOException) {
        throw complete(e)
      }
    }

    @Throws(IOException::class)
    override fun close() {
      if (closed) return
      closed = true
      if (contentLength != -1L && bytesReceived != contentLength) {
        throw ProtocolException("unexpected end of stream")
      }
      try {
        super.close()
        complete(null)
      } catch (e: IOException) {
        throw complete(e)
      }
    }

    private fun <E : IOException?> complete(e: E): E {
      if (completed) return e
      completed = true
      return bodyComplete(bytesReceived, responseDone = false, requestDone = true, e = e)
    }
  }

  /** A response body that fires events when it completes. */
  internal inner class ResponseBodySource(
    delegate: Source,
    private val contentLength: Long
  ) : ForwardingSource(delegate) {
    private var bytesReceived = 0L
    private var invokeStartEvent = true
    private var completed = false
    private var closed = false

    init {
      if (contentLength == 0L) {
        complete(null)
      }
    }

    @Throws(IOException::class)
    override fun read(sink: Buffer, byteCount: Long): Long {
      check(!closed) { "closed" }
      try {
        val read = delegate.read(sink, byteCount)

        if (invokeStartEvent) {
          invokeStartEvent = false
          eventListener.responseBodyStart(call)
        }

        if (read == -1L) {
          complete(null)
          return -1L
        }

        val newBytesReceived = bytesReceived + read
        if (contentLength != -1L && newBytesReceived > contentLength) {
          throw ProtocolException("expected $contentLength bytes but received $newBytesReceived")
        }

        bytesReceived = newBytesReceived
        if (newBytesReceived == contentLength) {
          complete(null)
        }

        return read
      } catch (e: IOException) {
        throw complete(e)
      }
    }

    @Throws(IOException::class)
    override fun close() {
      if (closed) return
      closed = true
      try {
        super.close()
        complete(null)
      } catch (e: IOException) {
        throw complete(e)
      }
    }

    fun <E : IOException?> complete(e: E): E {
      if (completed) return e
      completed = true
      // If the body is closed without reading any bytes send a responseBodyStart() now.
      if (e == null && invokeStartEvent) {
        invokeStartEvent = false
        eventListener.responseBodyStart(call)
      }
      return bodyComplete(bytesReceived, responseDone = true, requestDone = false, e = e)
    }
  }
}
  • 参考: https://blog.csdn.net/heng615975867/article/details/105289872
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值