网络库OKHttp(1)流程+拦截器

序、慢慢来才是最快的方法。

背景

OkHttp 是一套处理 HTTP 网络请求的依赖库,由 Square 公司设计研发并开源,目前可以在 Java Kotlin 中使用。对于 Android App 来说,OkHttp 现在几乎已经占据了所有的网络请求操作。

OKHttp源码官网

版本

java 复制代码
api 'com.squareup.okhttp3:okhttp:4.10.0'

发起网络请求示例代码

Kotlin 复制代码
  val  okHttpClient = OkHttpClient()
        val request :Request = Request.Builder()
            .url("")
            .build()

        val newCall = okHttpClient.newCall(request)
        //同步请求
        newCall.execute()
        //异步请求
        newCall.enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                TODO("Not yet implemented")
            }

            override fun onResponse(call: Call, response: Response) {
                TODO("Not yet implemented")
            }
        })

核心类

OkHttpClient 、Request 、Call 、RealCall 、Dispatcher 、Deque 、AsyncCall 、Response

  • OkHttpClient: Okhttp用于请求的执行客户端
  • Request: 通过Bulider设计模式,构建的一个请求对象
  • Call: 是通过 client.newCall 生成的请求执行对象,当执行了execute之后才会真正的开始执行网络请求
  • Response: 是通过网络请求后,从服务器返回的信息都在里面。内含返回的状态码,以及代表响应消息正文的ResponseBody
  1. interceptor 用户定义的拦截器,在重试拦截器之前执行
  2. retryAndFollowUpInterceptor 重试拦截器
  3. BridgeInterceptor 建立网络桥梁的拦截器,主要是为了给网络请求时候,添加各种各种必要参数。如Cookie,Content-type
  4. CacheInterceptor 缓存拦截器,主要是为了在网络请求时候,根据返回码处理缓存。
  5. ConnectInterceptor 连接拦截器,主要是为了从连接池子中查找可以复用的socket连接。
  6. networkInterceptors 用户定义的网络拦截器,在CallServerInterceptor(执行网络请求拦截器)之前运行。
  7. CallServerInterceptor 真正执行网络请求的逻辑。

1.网路请求流程

OkHttpClient 网络配置层

Kotlin 复制代码
class Builder constructor() {
   //Okhttp 请求分发器,是整个OkhttpClient的执行核心
  internal var dispatcher: Dispatcher = Dispatcher()
  //Okhttp连接池,不过会把任务委托给RealConnectionPool处理
  internal var connectionPool: ConnectionPool = ConnectionPool()
  //用户定义的拦截器,在重试拦截器之前执行
  internal val interceptors: MutableList<Interceptor> = mutableListOf()
  //用户定义的网络拦截器,在CallServerInterceptor(执行网络请求拦截器)之前运行。
  internal val networkInterceptors: MutableList<Interceptor> = mutableListOf()
  //流程监听器
  internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()
  //连接失败时是否重连
  internal var retryOnConnectionFailure = true
  //服务器认证设置
  internal var authenticator: Authenticator = Authenticator.NONE
  //是否重定向
  internal var followRedirects = true
  //是否重定向到https
  internal var followSslRedirects = true
  //cookie持久化的设置
  internal var cookieJar: CookieJar = CookieJar.NO_COOKIES
  //缓存设置
  internal var cache: Cache? = null
  //DNS设置
  internal var dns: Dns = Dns.SYSTEM
  //代理设置
  internal var proxy: Proxy? = null
  internal var proxySelector: ProxySelector? = null
  internal var proxyAuthenticator: Authenticator = Authenticator.NONE
  //默认的socket连接池
  internal var socketFactory: SocketFactory = SocketFactory.getDefault()
  //用于https的socket连接池
  internal var sslSocketFactoryOrNull: SSLSocketFactory? = null
  //用于信任Https证书的对象
  internal var x509TrustManagerOrNull: X509TrustManager? = null
  internal var connectionSpecs: List<ConnectionSpec> = DEFAULT_CONNECTION_SPECS
  //http协议集合
  internal var protocols: List<Protocol> = DEFAULT_PROTOCOLS
  //https对host的检验
  internal var hostnameVerifier: HostnameVerifier = OkHostnameVerifier
  internal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULT
  internal var certificateChainCleaner: CertificateChainCleaner? = null
  //请求超时
  internal var callTimeout = 0
  //连接超时
  internal var connectTimeout = 10_000
  //读取超时
  internal var readTimeout = 10_000
  //写入超时
  internal var writeTimeout = 10_000
  internal var pingInterval = 0
  internal var minWebSocketMessageToCompress = RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE
  internal var routeDatabase: RouteDatabase? = null
  }

client.newCall(request):

Kotlin 复制代码
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)

在这里生成一个RealCall对象,这里第三个参数是否为websocket,默认是false。 在拿到RealCall对象之后,这里有两种方式起发送网络请求:

  • execute() : 这种方式很少用
  • enqueue() : 这种方式是将每个请求放在队列中,按照顺序逐个去进行消费。

RealCall.enqueue()

java 复制代码
override fun enqueue(responseCallback: Callback) {
  check(executed.compareAndSet(false, true)) { "Already Executed" }

  callStart()
  client.dispatcher.enqueue(AsyncCall(responseCallback))
}


private fun callStart() {
  this.callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()")
  eventListener.callStart(this)
}

这里主要做了一下几步

  • 首先回调eventListener的callStart()方法,
  • 然后把创建AsyncCall对象将responseCallback传进去。
  • 最后Dispatcher的enqueue()方法.

Dispatcher.enqueue()

Kotlin 复制代码
class Dispatcher constructor() {

    ......

//按运行顺序准备异步调用的队列
private val readyAsyncCalls = ArrayDeque<AsyncCall>()

//正在运行的异步请求队列, 包含取消但是还未finish的AsyncCall
private val runningAsyncCalls = ArrayDeque<AsyncCall>()

//正在运行的同步请求队列, 包含取消但是还未finish的RealCall
private val runningSyncCalls = ArrayDeque<RealCall>()

    ......
    
internal fun enqueue(call: AsyncCall) {
  synchronized(this) {
    readyAsyncCalls.add(call)

  
    if (!call.call.forWebSocket) {
      val existingCall = findExistingCallWithHost(call.host)
      if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
    }
  }
  promoteAndExecute()
}

private fun findExistingCallWithHost(host: String): AsyncCall? {
  for (existingCall in runningAsyncCalls) {
    if (existingCall.host == host) return existingCall
  }
  for (existingCall in readyAsyncCalls) {
    if (existingCall.host == host) return existingCall
  }
  return null
}
  • 首先将AsyncCall加入readyAsyncCalls队列中.
  • 然后通过findExistingCallWithHost查找在runningAsyncCalls和readyAsyncCalls是否存在相同host的AsyncCall,如果存在则调用call.reuseCallsPerHostFrom()进行复用
  • 最后调用 promoteAndExecute() 通过线程池执行队列中的AsyncCall对象

Dispatcher.promoteAndExecute()

Kotlin 复制代码
private fun promoteAndExecute(): Boolean {
  this.assertThreadDoesntHoldLock()

  val executableCalls = mutableListOf<AsyncCall>()
    //判断是否有请求正在执行
  val isRunning: Boolean
   //加锁,保证线程安全
  synchronized(this) {
   //遍历 readyAsyncCalls 队列
    val i = readyAsyncCalls.iterator()
    while (i.hasNext()) {
      val asyncCall = i.next()
       //runningAsyncCalls的数量不能大于最大并发请求数 64
      if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
      //同一Host的最大数是5
      if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.
       //从readyAsyncCalls队列中移除并加入到executableCalls和runningAsyncCalls中
      i.remove()
      asyncCall.callsPerHost.incrementAndGet()
      executableCalls.add(asyncCall)
      runningAsyncCalls.add(asyncCall)
    }
    isRunning = runningCallsCount() > 0
  }
    //遍历executableCalls 执行asyncCall
  for (i in 0 until executableCalls.size) {
    val asyncCall = executableCalls[i]
    asyncCall.executeOn(executorService)
  }

  return isRunning
}

在这里遍历readyAsyncCalls队列,判断runningAsyncCalls的数量是否大于最大并发请求数64, 判断同一Host的请求是否大于5,然后将AsyncCall从readyAsyncCalls队列中移除,并加入到executableCalls和runningAsyncCalls中,遍历executableCalls 执行asyncCall.

RealCall.AsyncCall.exceuteOn()

Kotlin 复制代码
internal inner class AsyncCall(
  private val responseCallback: Callback
) : Runnable {

     ......

  fun executeOn(executorService: ExecutorService) {
    client.dispatcher.assertThreadDoesntHoldLock()

    var success = false
    try {
       //执行AsyncCall 的run方法
      executorService.execute(this)
      success = true
    } catch (e: RejectedExecutionException) {
      val ioException = InterruptedIOException("executor rejected")
      ioException.initCause(e)
      noMoreExchanges(ioException)
      responseCallback.onFailure(this@RealCall, ioException)
    } finally {
      if (!success) {
        client.dispatcher.finished(this) // This call is no longer running!
      }
    }
  }

  override fun run() {
    threadName("OkHttp ${redactedUrl()}") {
      var signalledCallback = false
      timeout.enter()
      try {
         //执行OkHttp的拦截器  获取response对象
        val response = getResponseWithInterceptorChain()
        signalledCallback = true
        //通过该方法将response对象回调出去
        responseCallback.onResponse(this@RealCall, response)
      } catch (e: IOException) {
        if (signalledCallback) {
  
          Platform.get().log("Callback failure for ${toLoggableString()}", Platform.INFO, e)
        } else {
            //遇到IO异常  回调失败方法
          responseCallback.onFailure(this@RealCall, e)
        }
      } catch (t: Throwable) {
         //遇到其他异常  回调失败方法
        cancel()
        if (!signalledCallback) {
          val canceledException = IOException("canceled due to $t")
          canceledException.addSuppressed(t)
          responseCallback.onFailure(this@RealCall, canceledException)
        }
        throw t
      } finally {
        client.dispatcher.finished(this)
      }
    }
  }
}

这里可以看到AsyncCall就是一个Runable对象,线程执行就会调用该对象的run方法,而executeOn方法就是执行runable对象. 在run方法中主要执行了以下几步:

  • 调用getResponseWithInterceptorChain()执行OkHttp拦截器,获取response对象
  • 调用responseCallback的onResponse方法将Response对象回调出去
  • 如果遇见IOException异常则调用responseCallback的onFailure方法将异常回调出去
  • 如果遇到其他异常,调用cancel()方法取消请求,调用responseCallback的onFailure方法将异常回调出去
  • 调用Dispatcher的finished方法结束执行

RealCall.getResponseWithInterceptorChain()

java 复制代码
@Throws(IOException::class)
internal fun getResponseWithInterceptorChain(): Response {
  // 拦截器集合
  val interceptors = mutableListOf<Interceptor>()
  //添加用户自定义集合
  interceptors += client.interceptors
  interceptors += RetryAndFollowUpInterceptor(client)
  interceptors += BridgeInterceptor(client.cookieJar)
  interceptors += CacheInterceptor(client.cache)
  interceptors += ConnectInterceptor
  //如果不是sockect 添加newtwork拦截器
  if (!forWebSocket) {
    interceptors += client.networkInterceptors
  }
  interceptors += CallServerInterceptor(forWebSocket)
    //构建拦截器责任链
  val chain = RealInterceptorChain(
      call = this,
      interceptors = interceptors,
      index = 0,
      exchange = null,
      request = originalRequest,
      connectTimeoutMillis = client.connectTimeoutMillis,
      readTimeoutMillis = client.readTimeoutMillis,
      writeTimeoutMillis = client.writeTimeoutMillis
  )

  var calledNoMoreExchanges = false
  try {
     //执行拦截器责任链获取Response
    val response = chain.proceed(originalRequest)
    //如果取消了  则抛出异常
    if (isCanceled()) {
      response.closeQuietly()
      throw IOException("Canceled")
    }
    return response
  } catch (e: IOException) {
    calledNoMoreExchanges = true
    throw noMoreExchanges(e) as Throwable
  } finally {
    if (!calledNoMoreExchanges) {
      noMoreExchanges(null)
    }
  }
}

在这里主要执行了以下几步操作

  • 首先构建一个可变interceptor集合,将所有拦截器添加进去,这里如果是websocket则不添加networkInterceptor拦截器,这个interceptor集合的添加顺序也就是OkHttp拦截器的执行顺序
  • 构建一个RealInterceptorChain对象,将所有的拦截器包裹
  • 调用RealInterceptorChain的proceed的方法,获得Response对象

简单的总结一下:这里才用了责任链设计模式,构建RealInterceptorChain对象,然后执行proceed方法获取response对象

2.Interceptor

Kotlin 复制代码
fun interface Interceptor {
    //拦截方法
  @Throws(IOException::class)
  fun intercept(chain: Chain): Response

  companion object {
    inline operator fun invoke(crossinline block: (chain: Chain) -> Response): Interceptor =
      Interceptor { block(it) }
  }

  interface Chain {
     //获取Request对象
    fun request(): Request

    //处理请求获取Reponse
    @Throws(IOException::class)
    fun proceed(request: Request): Response

    ......
  }
}
Kotlin 复制代码
class RealInterceptorChain(
  internal val call: RealCall,
  private val interceptors: List<Interceptor>,
  private val index: Int,
  internal val exchange: Exchange?,
  internal val request: Request,
  internal val connectTimeoutMillis: Int,
  internal val readTimeoutMillis: Int,
  internal val writeTimeoutMillis: Int
) : Interceptor.Chain {


  internal fun copy(
    index: Int = this.index,
    exchange: Exchange? = this.exchange,
    request: Request = this.request,
    connectTimeoutMillis: Int = this.connectTimeoutMillis,
    readTimeoutMillis: Int = this.readTimeoutMillis,
    writeTimeoutMillis: Int = this.writeTimeoutMillis
  ) = RealInterceptorChain(call, interceptors, index, exchange, request, connectTimeoutMillis,
      readTimeoutMillis, writeTimeoutMillis)
  
      ......
      
  override fun call(): Call = call

  override fun request(): Request = request

  @Throws(IOException::class)
  override fun proceed(request: Request): Response {
    check(index < interceptors.size)

     ......

    val next = copy(index = index + 1, request = request)
    val interceptor = interceptors[index]

    @Suppress("USELESS_ELVIS")
    val response = interceptor.intercept(next) ?: throw NullPointerException(
        "interceptor $interceptor returned null")

     ......

    return response
  }
}

这里看一看到copy()方法就是创建了一个RealInterceptorChain()对象,不过需要注意的是index在创建对象时是index = index + 1,这样就会执行index对应下标的拦截器,不断的调用下一个拦截器,直到有response对象返回,也就是chain.proceed(originalRequest)结束。

RetryAndFollowUpInterceptor(错误重定向拦截器)

主要处理了如下几个方向的问题:

  • 1.异常,或者协议重试(408客户端超时,权限问题,503服务暂时不处理,retry-after为0)
  • 2.重定向
  • 3.重试的次数不能超过20次。

RetryAndFollowUpInterceptor.intercept()

Kotlin 复制代码
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
var request = chain.request
val call = realChain.call
var followUpCount = 0
var priorResponse: Response? = null
var newExchangeFinder = true
var recoveredFailures = listOf<IOException>()
while (true) {
  //这里会新建一个ExchangeFinder,ConnectInterceptor会使用到
  call.enterNetworkInterceptorExchange(request, newExchangeFinder)

  var response: Response
  var closeActiveExchange = true
  try {
    if (call.isCanceled()) {
      throw IOException("Canceled")
    }

    try {
      response = realChain.proceed(request)
      newExchangeFinder = true
    } catch (e: RouteException) {
      //尝试通过路由连接失败。该请求将不会被发送。
      if (!recover(e.lastConnectException, call, request, requestSendStarted = false)) {
        throw e.firstConnectException.withSuppressed(recoveredFailures)
      } else {
        recoveredFailures += e.firstConnectException
      }
      newExchangeFinder = false
      continue
    } catch (e: IOException) {
      //尝试与服务器通信失败。该请求可能已发送。
      if (!recover(e, call, request, requestSendStarted = e !is ConnectionShutdownException)) {
        throw e.withSuppressed(recoveredFailures)
      } else {
        recoveredFailures += e
      }
      newExchangeFinder = false
      continue
    }

   //尝试关联上一个response,注意:body是为null
    if (priorResponse != null) {
      response = response.newBuilder()
          .priorResponse(priorResponse.newBuilder()
              .body(null)
              .build())
          .build()
    }

    val exchange = call.interceptorScopedExchange
    //会根据 responseCode 来判断,构建一个新的request并返回来重试或者重定向
    val followUp = followUpRequest(response, exchange)

    if (followUp == null) {
      if (exchange != null && exchange.isDuplex) {
        call.timeoutEarlyExit()
      }
      closeActiveExchange = false
      return response
    }
    //如果请求体是一次性的,不需要再次重试
    val followUpBody = followUp.body
    if (followUpBody != null && followUpBody.isOneShot()) {
      closeActiveExchange = false
      return response
    }

    response.body?.closeQuietly()
    //最大重试次数,不同的浏览器是不同的,比如:Chrome为21,Safari则是16
    if (++followUpCount > MAX_FOLLOW_UPS) {
        throw ProtocolException("Too many follow-up requests: $followUpCount")
    }


    request = followUp
    priorResponse = response
  } finally {
    call.exitNetworkInterceptorExchange(closeActiveExchange)
  }
}
}
  • 1.调用RealCall的enterNetworkInterceptorExchange方法实例化一个ExchangeFinder在RealCall对象中。

  • 2.执行RealCall的proceed 方法,进入下一个拦截器,进行下一步的请求处理。

  • 3.如果出现路由异常,则通过recover方法校验,当前的连接是否可以重试,不能重试则抛出异常,离开当前的循环。

Kotlin 复制代码
private fun recover(
  e: IOException,
  call: RealCall,
  userRequest: Request,
  requestSendStarted: Boolean
): Boolean {
  //禁止重连
  if (!client.retryOnConnectionFailure) return false

  // 不能再次发送请求体
  if (requestSendStarted && requestIsOneShot(e, userRequest)) return false

  // 致命异常
  if (!isRecoverable(e, requestSendStarted)) return false

  // 没有更多线路可以重连
  if (!call.retryAfterFailure()) return false

  // 对于故障恢复,将相同的路由选择器与新连接一起使用
  return true
}

BridgeInterceptor(应用层和网络层的桥接拦截器)

主要处理了如下几个问题:

  • 主要将Content-Type、Content-Length、Host等一些数据添加到头部。
  • 拿到数据之后对数据进行处理,判断是否为gzip,进行对数据数据解压。

BridgeInterceptor.intercept()

Kotlin 复制代码
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
  //获取原始请求数据
  val userRequest = chain.request()
  val requestBuilder = userRequest.newBuilder()
   //重新构建请求 添加一些必要的请求头信息
  val body = userRequest.body
  if (body != null) {
    val contentType = body.contentType()
    if (contentType != null) {
      requestBuilder.header("Content-Type", contentType.toString())
    }

    val contentLength = body.contentLength()
    if (contentLength != -1L) {
      requestBuilder.header("Content-Length", contentLength.toString())
      requestBuilder.removeHeader("Transfer-Encoding")
    } else {
      requestBuilder.header("Transfer-Encoding", "chunked")
      requestBuilder.removeHeader("Content-Length")
    }
  }

  if (userRequest.header("Host") == null) {
    requestBuilder.header("Host", userRequest.url.toHostHeader())
  }

  if (userRequest.header("Connection") == null) {
    requestBuilder.header("Connection", "Keep-Alive")
  }

  var transparentGzip = false
  if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
    transparentGzip = true
    requestBuilder.header("Accept-Encoding", "gzip")
  }

  val cookies = cookieJar.loadForRequest(userRequest.url)
  if (cookies.isNotEmpty()) {
    requestBuilder.header("Cookie", cookieHeader(cookies))
  }

  if (userRequest.header("User-Agent") == null) {
    requestBuilder.header("User-Agent", userAgent)
  }
    //执行下一个拦截器
  val networkResponse = chain.proceed(requestBuilder.build())

  cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)
  //创建一个新的responseBuilder,目的是将原始请求数据构建到response中
  val responseBuilder = networkResponse.newBuilder()
      .request(userRequest)

  if (transparentGzip &&
      "gzip".equals(networkResponse.header("Content-Encoding"), ignoreCase = true) &&
      networkResponse.promisesBody()) {
    val responseBody = networkResponse.body
    if (responseBody != null) {
      val gzipSource = GzipSource(responseBody.source())
      val strippedHeaders = networkResponse.headers.newBuilder()
          .removeAll("Content-Encoding")
          .removeAll("Content-Length")
          .build()
         
       //修改response header信息,移除Content-Encoding,Content-Length信息
      responseBuilder.headers(strippedHeaders)
      val contentType = networkResponse.header("Content-Type"
     //修改response body信息
      responseBuilder.body(RealResponseBody(contentType, -1L, gzipSource.buffer()))
    }
  }

  return responseBuilder.build()
}
  • 设置头部的Content-Type.说明内容类型是什么
  • 如果contentLength大于等于0,则设置头部的Content-Length(说明内容大小是多少);否则设置头部的Transfer-Encoding为chunked(说明传输编码为分块传输)
  • 如果Host不存在,设置头部的Host(在Http 1.1之后出现,可以通过同一个URL访问到不同主机,从而实现服务器虚拟服务器的负载均衡。如果1.1之后不设置就会返回404)。
  • 如果Connection不存在,设置头部的Connection为Keep-Alive(代表连接状态需要保持活跃)
  • 如果Accept-Encoding且Range为空,则强制设置Accept-Encoding为gzip(说明请求将会以gzip方式压缩)
  • 从CookieJar的缓存中取出cookie设置到头部的Cookie
  • 如果User-Agent为空,则设置User-Agent到头部

CacheInterceptor(缓存拦截器)

用户通过OkHttpClient.cache来配置缓存,缓存拦截器通过CacheStrategy来判断是使用网络还是缓存来构建response

CacheInterceptor.intercept()

Kotlin 复制代码
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
  val call = chain.call()
  //通过request从OkHttpClient.cache中获取缓存
  val cacheCandidate = cache?.get(chain.request())

  val now = System.currentTimeMillis()
 //创建缓存策略
  val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
    //为空表示不使用网络,反之,则表示使用网络
    val networkRequest = strategy.networkRequest
    //为空表示不使用缓存,反之,则表示使用缓存
    val cacheResponse = strategy.cacheResponse
    //追踪网络与缓存的使用情况
    cache?.trackResponse(strategy)
    val listener = (call as? RealCall)?.eventListener ?: EventListener.NONE
    //有缓存但不适用,关闭它
    if (cacheCandidate != null && cacheResponse == null) {
      cacheCandidate.body?.closeQuietly()
    }


 
    //如果网络被禁止,但是缓存又是空的,构建一个code为504的response,并返回
  if (networkRequest == null && cacheResponse == null) {
    return Response.Builder()
        .request(chain.request())
        .protocol(Protocol.HTTP_1_1)
        .code(HTTP_GATEWAY_TIMEOUT)
        .message("Unsatisfiable Request (only-if-cached)")
        .body(EMPTY_RESPONSE)
        .sentRequestAtMillis(-1L)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build().also {
          listener.satisfactionFailure(call, it)
        }
  }

 //如果我们禁用了网络不使用网络,且有缓存,直接根据缓存内容构建并返回response
  if (networkRequest == null) {
    return cacheResponse!!.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .build().also {
          listener.cacheHit(call, it)
        }
  }

   //为缓存添加监听
  if (cacheResponse != null) {
    listener.cacheConditionalHit(call, cacheResponse)
  } else if (cache != null) {
    listener.cacheMiss(call)
  }

  var networkResponse: Response? = null
  try {
      //执行下一个拦截器
    networkResponse = chain.proceed(networkRequest)
  } finally {
    //捕获I/O或其他异常,请求失败,networkResponse为空,且有缓存的时候,不暴露缓存内容
    if (networkResponse == null && cacheCandidate != null) {
       //否则关闭缓存响应体
      cacheCandidate.body?.closeQuietly()
    }
  }

    //如果有缓存
  if (cacheResponse != null) {
    //且网络返回response code为304的时候,使用缓存内容新构建一个Response返回。
    if (networkResponse?.code == HTTP_NOT_MODIFIED) {
      val response = cacheResponse.newBuilder()
          .headers(combine(cacheResponse.headers, networkResponse.headers))
          .sentRequestAtMillis(networkResponse.sentRequestAtMillis)
          .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis)
          .cacheResponse(stripBody(cacheResponse))
          .networkResponse(stripBody(networkResponse))
          .build()

      networkResponse.body!!.close()
      cache!!.trackConditionalCacheHit()
      cache.update(cacheResponse, response)
      return response.also {
        listener.cacheHit(call, it)
      }
    } else {
    //否则关闭缓存响应体
      cacheResponse.body?.closeQuietly()
    }
  }

  //构建网络请求的response
  val response = networkResponse!!.newBuilder()
      .cacheResponse(stripBody(cacheResponse))
      .networkResponse(stripBody(networkResponse))
      .build()

    //如果cache不为null,即用户在OkHttpClient中配置了缓存,则将上一步新构建的网络请求response存到cache中
  if (cache != null) {
    //根据response的code,header以及CacheControl.noStore来判断是否可以缓存
    if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) {
     // 将该response存入缓存
      val cacheRequest = cache.put(response)
      return cacheWritingResponse(cacheRequest, response).also {
        if (cacheResponse != null) {
          listener.cacheMiss(call)
        }
      }
    }
    
    //根据请求方法来判断缓存是否有效,只对Get请求进行缓存,其它方法的请求则移除
    if (HttpMethod.invalidatesCache(networkRequest.method)) {
      try {
         //缓存无效,将该请求缓存从client缓存配置中移除
        cache.remove(networkRequest)
      } catch (_: IOException) {
       
      }
    }
  }

  return response
}

网络请求前:

  • 首先根据request从OkHttpClient.cache中获取缓存,通过CacheStrategy获取本次请求的请求体及缓存的响应体。
  • 如果 请求体networkRequest和响应体cacheResponse都为空的话,则返回错误码为 504
  • 如果 请求体networkRequest为空 响应体cacheResponse不为空的话,则将该响应体返回
  • 如果请求体networkRequest不为空的话,则进入下一个拦截器。

网络请求后:

  • 如果当前cacheResponse不为空,且 networkResponse状态码为304, 则代表数据没有变化,那么就会根据 cacheResponse 构建一个新的 response,根据当前时间更新到缓存当中,并返回到上一拦截器中
  • 如果networkResponse状态码不为304,则判断是否进行缓存,最后返回到上一拦截器中

从LruCache中获取缓存

Kotlin 复制代码
val cacheCandidate = cache?.get(chain.request())
Kotlin 复制代码
internal fun get(request: Request): Response? {
  val key = key(request.url)
  val snapshot: DiskLruCache.Snapshot = try {
    cache[key] ?: return null
  } catch (_: IOException) {
    return null // Give up because the cache cannot be read.
  }

  val entry: Entry = try {
    Entry(snapshot.getSource(ENTRY_METADATA))
  } catch (_: IOException) {
    snapshot.closeQuietly()
    return null
  }

  val response = entry.response(snapshot)
  if (!entry.matches(request, response)) {
    response.body?.closeQuietly()
    return null
  }

  return response
}
Kotlin 复制代码
@JvmStatic
fun key(url: HttpUrl): String = url.toString().encodeUtf8().md5().hex()
  • 首先将url转化为urf-8,并且通过md5拿到摘要,再调用hex获取16进制的字符串,该字符串就是LruCache的key;
  • 通过key获取到DiskLruCache.Snapshot对象(这里在DiskLruCache中重写了get方法),根据DiskLruCache.Snapshot对象获取到okio 的source。

DiskLruCache:

Kotlin 复制代码
@Synchronized @Throws(IOException::class)
operator fun get(key: String): Snapshot? {
  initialize()

  checkNotClosed()
  validateKey(key)
  val entry = lruEntries[key] ?: return null
  val snapshot = entry.snapshot() ?: return null

  redundantOpCount++
  journalWriter!!.writeUtf8(READ)
      .writeByte(' '.toInt())
      .writeUtf8(key)
      .writeByte('\n'.toInt())
  if (journalRebuildRequired()) {
    cleanupQueue.schedule(cleanupTask)
  }

  return snapshot
}

ConnectInterceptot(链接拦截器)

Kotlin 复制代码
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()

Kotlin 复制代码
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

Kotlin 复制代码
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

Kotlin 复制代码
@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()

Kotlin 复制代码
@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对象

我们继续向下看

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

callAcquirePooledConnection

Kotlin 复制代码
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是否符合条件

Kotlin 复制代码
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 生成

Kotlin 复制代码
var localRouteSelector = routeSelector
if (localRouteSelector == null) {
  localRouteSelector = RouteSelector(address, call.client.routeDatabase, call, eventListener)
  this.routeSelector = localRouteSelector
}
复制代码
Kotlin 复制代码
class RouteSelector(
  private val address: Address,
  private val routeDatabase: RouteDatabase,
  private val call: Call,
  private val eventListener: EventListener
) {
    ......
    init {
      resetNextProxy(address.url, address.proxy)
    }
    ......
}
Kotlin 复制代码
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对象需要使用

Kotlin 复制代码
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对象。下面逐步分析。
Kotlin 复制代码
@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添加到集合当中
Kotlin 复制代码
@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()

Kotlin 复制代码
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方法

Kotlin 复制代码
@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

Kotlin 复制代码
@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 ,说明隧道建立成功。
Kotlin 复制代码
@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 进行权限校验。

Kotlin 复制代码
@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

Kotlin 复制代码
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

Kotlin 复制代码
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
Kotlin 复制代码
@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()
}

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

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

NetWorkInterceptor(网络连接器)

CallServerInterceptor(请求拦截器)

Kotlin 复制代码
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
  val realChain = chain as RealInterceptorChain
  val exchange = realChain.exchange!!
  val request = realChain.request
  val requestBody = request.body
  val sentRequestMillis = System.currentTimeMillis()

  exchange.writeRequestHeaders(request)

  var invokeStartEvent = true
  var responseBuilder: Response.Builder? = null
  if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
    if ("100-continue".equals(request.header("Expect"), ignoreCase = true)) {
      exchange.flushRequest()
      responseBuilder = exchange.readResponseHeaders(expectContinue = true)
      exchange.responseHeadersStart()
      invokeStartEvent = false
    }
    if (responseBuilder == null) {
      if (requestBody.isDuplex()) {
        exchange.flushRequest()
        val bufferedRequestBody = exchange.createRequestBody(request, true).buffer()
        requestBody.writeTo(bufferedRequestBody)
      } else {
        val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
        requestBody.writeTo(bufferedRequestBody)
        bufferedRequestBody.close()
      }
    } else {
      exchange.noRequestBody()
      if (!exchange.connection.isMultiplexed) {
        exchange.noNewExchangesOnConnection()
      }
    }
  } else {
    exchange.noRequestBody()
  }

  if (requestBody == null || !requestBody.isDuplex()) {
    exchange.finishRequest()
  }
  if (responseBuilder == null) {
    responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
    if (invokeStartEvent) {
      exchange.responseHeadersStart()
      invokeStartEvent = false
    }
  }
  var response = responseBuilder
      .request(request)
      .handshake(exchange.connection.handshake())
      .sentRequestAtMillis(sentRequestMillis)
      .receivedResponseAtMillis(System.currentTimeMillis())
      .build()
  var code = response.code
  if (code == 100) {
    responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
    if (invokeStartEvent) {
      exchange.responseHeadersStart()
    }
    response = responseBuilder
        .request(request)
        .handshake(exchange.connection.handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build()
    code = response.code
  }

  exchange.responseHeadersEnd(response)

  response = if (forWebSocket && code == 101) {
    response.newBuilder()
        .body(EMPTY_RESPONSE)
        .build()
  } else {
    response.newBuilder()
        .body(exchange.openResponseBody(response))
        .build()
  }
  if ("close".equals(response.request.header("Connection"), ignoreCase = true) ||
      "close".equals(response.header("Connection"), ignoreCase = true)) {
    exchange.noNewExchangesOnConnection()
  }
  if ((code == 204 || code == 205) && response.body?.contentLength() ?: -1L > 0L) {
    throw ProtocolException(
        "HTTP $code had non-zero Content-Length: ${response.body?.contentLength()}")
  }
  return response
}
  • exchange.writeRequestHeaders(request)写入请求头信息
  • 判断请求方式是不是GETHEAD,如果不是则需要传入请求体,接着判断请求头中的Expect值是否为100-continue,如果是的话,则会读取响应体的头部信息,如果读出的Response.Builder为空,接着判断 requestBody.isDuplex(),如果为true的话,则刷新缓冲区,通过exchange.createRequestBody(request, true).buffer()创建bufferedRequestBody,往请求的requestBody写入数据,如果为false的话,,通过exchange.createRequestBody(request, true).buffer()创建bufferedRequestBody,写入输出流中发送数据。
  • 如果请求方式是GETHEAD的话,则没有请求体。
  • 如果requestBody为空,也就是没有请求体,或者requestBody.isDuplex()为false的话,则结束请求。
  • 如果responseBuilder为空的话,则调用exchange.readResponseHeaders方法
  • 获取响应体
  • 判断响应体的code是否为100,如果响应体为100 则是说后面还有数据需要传输,则会重新调用 exchange.readResponseHeaders方法,再次生成响应体
  • 判断code是否为101 并且 为websocekt请求,如果是的话,则生成一个空的response,否则就会通过exchange.openResponseBody(response)读取response中的数据生成一个响应体。
  • 最后判断code是否为204、205并且响应体的body为空的话则抛出异常,否则正常返回

。。。。。。。省略

参考:

Overview - OkHttp

OkHttp-源码分析(一) - 掘金

OkHttp-源码分析(二) - 掘金

OkHttp-源码分析(三) - 掘金

OKHTTP 源码分析(1)调用流程梳理_okhttpclient().newbuilder().build();-CSDN博客

相关推荐
学习使我快乐019 小时前
AJAX 2——Bootstrap弹框使用、图书管理案例、图片上传方法
ajax·okhttp·bootstrap
前端李易安20 小时前
ajax的原理,使用场景以及如何实现
前端·ajax·okhttp
学习使我快乐012 天前
AJAX 1——axios体验、认识URL、常用请求方法、HTTP协议、错误处理、form-serialize插件
前端·http·ajax·okhttp·axios
帅次3 天前
解决 Android WebView 无法加载 H5 页面常见问题的实用指南
android·okhttp·gradle·binder·webview·retrofit·appcompat
懒洋洋大魔王4 天前
7.Javaweb-Ajax
前端·ajax·okhttp
被迫学习Java5 天前
前端工程化17-邂逅原生的ajax、跨域、JSONP
前端·ajax·okhttp
Liuxu09036 天前
Ajax开发技术
java·前端·ajax·okhttp·javaweb
上官花雨6 天前
第七章综合实践:JPA+Thymeleaf增删改查
spring boot·后端·okhttp
丶白泽7 天前
重修设计模式-行为型-责任链模式
okhttp·设计模式·责任链模式
快乐就好ya7 天前
AJAX(简介以及一些用法)
前端·ajax·okhttp