OkHttp 源码解析

本文深入解析OkHttp的源码,详细阐述了OkHttp如何发起网络请求,包括创建请求、设置拦截器、请求调度、连接建立、缓存策略、数据交互等关键步骤,以及重试和重定向机制,揭示了OkHttp在网络请求中的高效处理和智能优化。
摘要由CSDN通过智能技术生成

本文源码为okhttp:4.9.0版本。

implementation("com.squareup.okhttp3:okhttp:4.9.0")

GitHub地址

基本用法

创建请求Request后,要用OkHttpClient的newCall()方法创建一个RealCall对象,然后调用execute()发起同步请求或调用enqueue()发起异步请求。

   //1.创建请求(包含url,method,headers,body)
      val request = Request
              .Builder()
              .url("https://developer.android.google.cn/")
              .build()
       //2.创建OkHttpClient (包含调度器、拦截器、DNS等)
       val okHttpClient = OkHttpClient.Builder().build()
       //3.创建Call(用于调用请求)
       val newCall = okHttpClient.newCall(request)
       //4.通过异步请求数据
       newCall.enqueue(object :Callback{
           override fun onFailure(call: Call, e: IOException) {}
           override fun onResponse(call: Call, response: Response) {}
       })
       //4.通过同步请求数据
       val response =  newCall.execute()

请求调度器

网络请求主要分为同步请求和异步请求,Dispatcher主要用于控制并发的请求,无论是同步请求还是异步请求,都会通过Dispatcher来处理。

// Dispatcher.kt 源码

class Dispatcher constructor() {
   // 最大并发执行的请求数
    @get:Synchronized var maxRequests = 64
    
    //每个主机的最大任务请求数
    @get:Synchronized var maxRequestsPerHost = 5
    
    //执行异步请求的线程池
    private var executorServiceOrNull: ExecutorService? = null
    
    @get:Synchronized
    @get:JvmName("executorService") val executorService: ExecutorService
    get() {
      if (executorServiceOrNull == null) {
        // 默认创建线程池
        executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
            SynchronousQueue(), threadFactory("$okHttpName Dispatcher", false))
      }
      return executorServiceOrNull!!
    }
    
    //准备运行的异步请求队列
    private val readyAsyncCalls = ArrayDeque<AsyncCall>()
    
    //正在运行的异步请求队列
    private val runningAsyncCalls = ArrayDeque<AsyncCall>()
    
    //正在运行的同步请求队列
    private val runningSyncCalls = ArrayDeque<RealCall>()
  
    constructor(executorService: ExecutorService) : this() {
       this.executorServiceOrNull = executorService
    }
    
    
    fun executorService(): ExecutorService = executorService
    
}

请求调度器

请求分发机制

请求操作 Call

RealCall实现了Call接口,也是这个接口唯一的实现类,RealCall是一个OkHttp应用与网络层之间的桥梁。RealCall可以理解为同步请求操作,RealCall的内部类AsyncCall可以理解异步请求操作。

  • 发起同步请求execute()
// RealCall.kt 源码

override fun execute(): Response {
    check(executed.compareAndSet(false, true)) { "Already Executed" }
    // 超时就关闭socket/流
    timeout.enter()
    callStart()
    try {
      // 把当前RealCall 添加到同步请求操作队列
      client.dispatcher.executed(this)
      // 返回Response
      return getResponseWithInterceptorChain()
    } finally {
    // 把当前RealCall 从同步请求操作队列中删除
      client.dispatcher.finished(this)
    }
  }

// Dispatcher.kt 源码

@Synchronized internal fun executed(call: RealCall) {
    runningSyncCalls.add(call)
  }

在上面的代码中,execute()方法timeout.enter()这行代码调用AsyncTimeout的enter()方法让AsyncTimeout在请求超时的时候关闭Socket或流。在RealCall的execute()方法调用完enter()方法后,会调用Dispatcher的executed()把请求加入同步请求队列,然后调getResponseWithInterceptorChain()方法获取响应,获取到响应后,就会让Dispatcher把请求从同步请求操作队列中移除。

  • 发起异步请求enqueue()
// RealCall.kt 源码

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

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

// Dispatcher.kt 源码

internal fun enqueue(call: AsyncCall) {
    synchronized(this) {
      // 把AsyncCall 添加到待执行的异步请求队列中
      readyAsyncCalls.add(call)

      //不是WebSocket 连接请求
      if (!call.call.forWebSocket) {
        // 找出与当前请求地址的主机相同 AsyncCall
        val existingCall = findExistingCallWithHost(call.host)
        
        if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
      }
    }
    // 找出并执行可执行的请求
    promoteAndExecute()
  }

  private fun promoteAndExecute(): Boolean {
    this.assertThreadDoesntHoldLock()

    val executableCalls = mutableListOf<AsyncCall>()
    val isRunning: Boolean
    synchronized(this) {
      val i = readyAsyncCalls.iterator()
      while (i.hasNext()) {
        val asyncCall = i.next()
        // 并发执行的请求数超出最大的请求数64
        if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
        // 主机的并发请求数超过最大请求数5
        if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.

        i.remove()
        asyncCall.callsPerHost.incrementAndGet()
        executableCalls.add(asyncCall)
        // 把asyncCall 加入runningAsyncCalls 中
        runningAsyncCalls.add(asyncCall)
      }
      // 
      isRunning = runningCallsCount() > 0
    }

    for (i in 0 until executableCalls.size) {
      val asyncCall = executableCalls[i]
      // 把asyncCall提交到线程池executorService
      asyncCall.executeOn(executorService)
    }

    return isRunning
  }

在上面的代码中,RealCall的enqueue()方法会创建一个异步请求操作AsyncCall,并把它交给Dispatcher处理。AsyncCall实现了Runnable接口,Dispatcher接收到AsyncCall后,会把AsyncCall添加到待执行异步请求队列readyAsyncCalls中,然后调用自己的promoteAndExecute()方法。在这个方法中遍历readyAsyncCalls队列,筛选出符合条件并发执行的请求数要小于最大的请求数64并且主机的并发请求数不能超过最大请求数5的AsyncCall,把AsyncCall加入runningAsyncCalls队列后,将有效的请求筛选出后并保存,立即开始遍历请求,利用调度器Dispatcher里的ExecutorService进行Runnable任务,也就是遍历后加入到线程池中执行这些有效的网络请求。

// RealCall.kt 源码

internal inner class AsyncCall(
    private val responseCallback: Callback
  ) : Runnable {
      
      
    fun executeOn(executorService: ExecutorService) {
      client.dispatcher.assertThreadDoesntHoldLock()

      var success = false
      try {
         //将当前Runnable放到线程池中运行
        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 {
          //通过拦截器链来得到网络响应
          val response = getResponseWithInterceptorChain()
          signalledCallback = true
          // 成功的回调
          responseCallback.onResponse(this@RealCall, response)
        } catch (e: IOException) {
          if (signalledCallback) {
            // Do not signal the callback twice!
            Platform.get().log("Callback failure for ${toLoggableString()}", Platform.INFO, e)
          } else {
            // 失败的回调
            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 {
          // 把请求从正在运行异步请求队列 runningAsyncCalls 中移除
          client.dispatcher.finished(this)
        }
      }
    }
  }

AsyncCall类executeOn()方法主要是正在运行异步请求队列放入线程池,由线程池开启线程执行任务;run()方法是线程池中执行的请求任务,通过getResponseWithInterceptorChain()得到网络请求结果response。
异步请求执行流程:
异步请求执行流程

  • 拦截器链
// RealCall.kt 源码

  @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
    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 {
      //启动职责链
      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)
      }
    }
  }

getResponseWithInterceptorChain()方法内部实现是通过一个责任链模式来完成,将网络请求的各个阶段封装到各个链条中,首先会创建一个interceptors列表,然后添加拦截器到列表中,再用interceptors创建一个拦截器链RealInterceptorChain,然后调用拦截器链的proceed()方法。

// RealInterceptorChain.kt 源码

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

    calls++

    if (exchange != null) {
      check(exchange.finder.sameHostAndPort(request.url)) {
        "network interceptor ${interceptors[index - 1]} must retain the same host and port"
      }
      check(calls == 1) {
        "network interceptor ${interceptors[index - 1]} must call proceed() exactly once"
      }
    }

    // 调用了RealInterceptorChain的copy方法,
    // 其内部会新建一个RealInterceptorChain,
    // 通过参数index+1来循环interceptors中的拦截器
    val next = copy(index = index + 1, request = request)
    // 获取当前要执行的拦截器
    val interceptor = interceptors[index]

    // 运行当前的拦截器,并设置了下个拦截器。
    // 其内部的逻辑通常是:当前拦截器处理完成后会接着执行下个拦截器的proceed方法
    @Suppress("USELESS_ELVIS")
    val response = interceptor.intercept(next) ?: throw NullPointerException(
        "interceptor $interceptor returned null")

    if (exchange != null) {
      check(index + 1 >= interceptors.size || next.calls == 1) {
        "network interceptor $interceptor must call proceed() exactly once"
      }
    }

    check(response.body != null) { "interceptor $interceptor returned a response with no body" }

    return response
  }

proceed()方法是责任链模式的核心所在,将请求移交给下一个拦截器。

拦截器作用
应用拦截器获取到原始请求,可以添加一些自定义header、通用参数、参数加密、网关接入等等
RetryAndFollowUpInterceptor(重试重定向拦截器)负责在请求失败时重试和重定向
BridgeInterceptor(桥接拦截器)应用层和网络层的桥接拦截器,主要工作是为请求添加cookie、添加固定的header,比如Host、Content-Length、Content-Type、User-Agent等等,然后保存响应结果的cookie,如果响应使用gzip压缩过,则还需要进行解压
CacheInterceptor(缓存拦截器)负责读取和更新缓存,可以配置自定义的缓存拦截器
ConnectInterceptor(连接拦截器)连接拦截器,内部会维护一个连接池,负责连接复用、创建连接(三次握手等等)、释放连接以及创建连接上的socket流
networkInterceptors(网络拦截器)用户自定义拦截器,通常用于监控网络层的数据传输
CallServerInterceptor(请求服务拦截器)拦截器链的最后的拦截器,用于向服务端发送数据并获取响应

拦截器

拦截器

RetryAndFollowUpInterceptor

负责在请求失败时重试和重定向。

RetryAndFollowUpInterceptor的intercept()方法中的代码是放在while中执行的,只有当重试的条件不成立时,请求才会被中断,而且这个拦截器没有设定重试次数的上限,最大重定向次数是写死的20次,如果有特殊需求的话,则要自定义一个重试拦截器和重定向拦截器。

  • 重试机制
// RetryAndFollowUpInterceptor.kt 源码

@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) {
      // 初始化RealCall 的ExchangeFinder,它的作用是查找可重用的连接。
      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
        }

        // 上一个响应
        if (priorResponse != null) {
          response = response.newBuilder()
              .priorResponse(priorResponse.newBuilder()
                  .body(null)
                  .build())
              .build()
        }
        // 数据交换器
        val exchange = call.interceptorScopedExchange
        // followUp重定向请求;followUpRequest()根据不同的code 确定是否需要创建newRequest
        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()
        //如果超过重试次数 则抛出异常。 MAX_FOLLOW_UPS=20
        if (++followUpCount > MAX_FOLLOW_UPS) {
          throw ProtocolException("Too many follow-up requests: $followUpCount")
        }

        request = followUp
        priorResponse = response
      } finally {
        call.exitNetworkInterceptorExchange(closeActiveExchange)
      }
    }
  }

上面源码中,在重试与重定向拦截器的intercept()方法中,当请求在后续的拦截器中处理时遇到路线异常(RouteException)或IO异常(IOException时)才会调用recover()方法判断是否要重试,不重试则抛出异常。

// RetryAndFollowUpInterceptor.kt 源码

private fun recover(e: IOException,call: RealCall,
userRequest: Request,requestSendStarted: Boolean
 ): Boolean {
    // 应用层禁止重试;OkHttpClient 的 retryOnConnectionFailure 的值为 false
    if (!client.retryOnConnectionFailure) return false

    // 不能再次发送请求体;
    // 1)请求执行过程中遇到 IO 异常(不包括 Http2Connection 抛出的 ConnectionShutdownException)
    // 2)requestIsOneShot() 返回 true,这个方法默认为 false ,除非我们自己重写了这个方法
    if (requestSendStarted && requestIsOneShot(e, userRequest)) return false

    // 致命异常;
    // 1)协议异常 ProtocalException
    // 2)Socket 超时异常 SocketTimeoutException
    // 3)证书验证异常 CertificateExeption
    // 4)SSL 对端验证异常 SSLPeerUnverifiedException
    if (!isRecoverable(e, requestSendStarted)) return false

    // 没有更多路线可重试
    // 1) 给 OkHttpClient 设置了代理
    // 2) DNS 服务器返回多个 IP 地址
    if (!call.retryAfterFailure()) return false

    return true
  }
  • 重定向机制
// RetryAndFollowUpInterceptor.kt 源码

@Throws(IOException::class)
private fun followUpRequest(userResponse: Response, exchange: Exchange?): Request? {
    val route = exchange?.connection?.route()
    val responseCode = userResponse.code

    val method = userResponse.request.method
    when (responseCode) {
      // 407 HTTP代理验证
      HTTP_PROXY_AUTH -> {
        val selectedProxy = route!!.proxy
        if (selectedProxy.type() != Proxy.Type.HTTP) {
          throw ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy")
        }
        return client.proxyAuthenticator.authenticate(route, userResponse)
      }
      
      // 401 未经授权的
      HTTP_UNAUTHORIZED -> return client.authenticator.authenticate(route, userResponse)
       //临时重定向 307~308,300~303
      HTTP_PERM_REDIRECT, HTTP_TEMP_REDIRECT, HTTP_MULT_CHOICE, HTTP_MOVED_PERM, HTTP_MOVED_TEMP, HTTP_SEE_OTHER -> {
         // 构建重定向请求
        return buildRedirectRequest(userResponse, method)
      }
      //客服端超时 408
      HTTP_CLIENT_TIMEOUT -> {
        // 408's are rare in practice, but some servers like HAProxy use this response code. The
        // spec says that we may repeat the request without modifications. Modern browsers also
        // repeat the request (even non-idempotent ones.)
        if (!client.retryOnConnectionFailure) {
          // The application layer has directed us not to retry the request.
          return null
        }

        val requestBody = userResponse.request.body
        if (requestBody != null && requestBody.isOneShot()) {
          return null
        }
        val priorResponse = userResponse.priorResponse
        if (priorResponse != null && priorResponse.code == HTTP_CLIENT_TIMEOUT) {
          // We attempted to retry and got another timeout. Give up.
          return null
        }

        if (retryAfter(userResponse, 0) > 0) {
          return null
        }

        return userResponse.request
      }
       // 服务器不可用 503
      HTTP_UNAVAILABLE -> {
        val priorResponse = userResponse.priorResponse
        if (priorResponse != null && priorResponse.code == HTTP_UNAVAILABLE) {
          // We attempted to retry and got another timeout. Give up.
          return null
        }

        if (retryAfter(userResponse, Integer.MAX_VALUE) == 0) {
          // specifically received an instruction to retry without delay
          return userResponse.request
        }

        return null
      }
      // 421
      HTTP_MISDIRECTED_REQUEST -> {
        // OkHttp can coalesce HTTP/2 connections even if the domain names are different. See
        // RealConnection.isEligible(). If we attempted this and the server returned HTTP 421, then
        // we can retry on a different connection.
        val requestBody = userResponse.request.body
        if (requestBody != null && requestBody.isOneShot()) {
          return null
        }

        if (exchange == null || !exchange.isCoalescedConnection) {
          return null
        }

        exchange.connection.noCoalescedConnections()
        return userResponse.request
      }

      else -> return null
    }
  }

从上面的代码可以看,followUpRequest()方法会根据不同的响应状态码构建不同类型的请求。当状态码407时,并且协议为 HTTP ,则返回一个包含认证挑战的请求;当状态码3XX时,会调用 buildRedirectRequest()构建重定向请求,告诉客户端使用替代位置访问客户端感兴趣的资源,要么提供一个替代的响应而不是资源的内容。

BridgeInterceptor

作为连接应用程序和服务端桥梁的拦截器。

主要在请求的过程中负责给Request添加必要的请求头信息(包括gzip压缩、cookie添加等等),在响应过程中解析Response信息(包括gzip解压、cookie保存)。

// BridgeInterceptor.kt 源码

@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) {
        // 添加contentType(实体主体的媒体类型)
        requestBuilder.header("Content-Type", contentType.toString())
      }

      val contentLength = body.contentLength()
      if (contentLength != -1L) {
        // 添加Content-Length(内容长度)
        requestBuilder.header("Content-Length", contentLength.toString())
        requestBuilder.removeHeader("Transfer-Encoding")
      } else {
        // 添加Transfer-Encoding(指定报文主体的传输方式)
        requestBuilder.header("Transfer-Encoding", "chunked")
        requestBuilder.removeHeader("Content-Length")
      }
    }
    // 添加 Host(请求资源所在的服务器)
    if (userRequest.header("Host") == null) {
      requestBuilder.header("Host", userRequest.url.toHostHeader())
    }
    // 默认keep-Alive
    if (userRequest.header("Connection") == null) {
      requestBuilder.header("Connection", "Keep-Alive")
    }

    //传输流的压缩方式 默认gzip方式
    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)
    //添加cookie(本地缓存)
    if (cookies.isNotEmpty()) {
      requestBuilder.header("Cookie", cookieHeader(cookies))
    }
    // 添加User-Agent(HTTP 客户端程序的信息)
    if (userRequest.header("User-Agent") == null) {
      requestBuilder.header("User-Agent", userAgent)
    }

   // 返回Response
    val networkResponse = chain.proceed(requestBuilder.build())
    // 处理header中Set-Cookie内容 将cookie保存
    cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)
     //将原始request放入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) {
      //封装成GzipSource  重写read方法 解压gzip
        val gzipSource = GzipSource(responseBody.source())
        //精简header
        val strippedHeaders = networkResponse.headers.newBuilder()
            .removeAll("Content-Encoding")
            .removeAll("Content-Length")
            .build()
        responseBuilder.headers(strippedHeaders)
        val contentType = networkResponse.header("Content-Type")
        responseBuilder.body(RealResponseBody(contentType, -1L, gzipSource.buffer()))
      }
    }

    return responseBuilder.build()
  }

其实BridgeInterceptor的作用就是帮用户处理网络请求,它会帮助用户填写服务器请求所需要的配置信息,如上面所展示的User-Agent、Connection、Host、Accept-Encoding等;同时也会对请求的结果进行相应处理。

BridgeInterceptor的内部实现主要分为以下三步:

  • 为用户网络请求设置Content-Type、Content-Length、Host、Connection、Cookie等参数,也就是将一般请求转换为适合服务器解析的格式,以适应服务器端;
  • 通过 chain.proceed(requestBuilder.build())方法,将转换后的请求移交给下一个拦截器CacheInterceptor,并接收返回的结果Response;
  • 对结果Response也进行gzip、Content-Type转换,以适应应用程序端。
CacheInterceptor

处理网络请求缓存的拦截器。

OkHttp中使用缓存的话,要在创建OkHttpClient初始化时用cache()方法设置缓存

/**
 * 网络缓存数据的最大值(字节)
 */
const val MAX_SIZE_NETWORK_CACHE = 50 * 1024 * 1024L

private fun initOkHttpClient() {
  val networkCacheDirectory = File(cacheDir?.absolutePath + "networkCache")

    if (!networkCacheDirectory.exists()) {
      networkCacheDirectory.mkdir()
    }

    val cache = Cache(networkCacheDirectory, MAX_SIZE_NETWORK_CACHE)

    okHttpClient = OkHttpClient.Builder()
        // 使用缓存
        .cache(cache)
        .build()
}

注意: CacheInterceptor只会缓存GET和HEAD等获取资源的方法的请求,而对于POST和PUT等修改资源的请求和响应数据是不会进行缓存的。

  • 得到/保存缓存响应
// CacheInterceptor.kt 源码

@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
    
    val call = chain.call()
    //获取缓存 如果我们配置了缓存 那么会去查找是否存在cache 
    // 注意:okhttp默认并不会配置缓存,通过OkHttpClient.Builder的cache方法设置
    // cacheCandidate是上次与服务器交互缓存的Response
    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()
    }

    // 不允许使用网络请求 并且当前网络没有缓存,会返回504(网关超时)
    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)
          }
    }

    // 不允许使用网络 仅直接使用缓存 
    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 {
      // If we're crashing on I/O or otherwise, don't leak the cache body.
      if (networkResponse == null && cacheCandidate != null) {
        cacheCandidate.body?.closeQuietly()
      }
    }

    //检查缓存是否可用,如果可用。那么就用当前缓存的Response,关闭网络连接,释放连接。
    if (cacheResponse != null) {
    // 如果缓存数据不为空并且code为304,表示数据没有变化,继续使用缓存数据;
      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()

        // Update the cache after combining headers but before stripping the
        // Content-Encoding header (as performed by initContentStream()).
        cache!!.trackConditionalCacheHit()
        //  更新缓存数据
        cache.update(cacheResponse, response)
        return response.also {
          listener.cacheHit(call, it)
        }
      } else {
        cacheResponse.body?.closeQuietly()
      }
    }

   // 返回 网络响应
    val response = networkResponse!!.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build()

    if (cache != null) {
      if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) {
        // 将网络数据保存到缓存中
        val cacheRequest = cache.put(response)
        return cacheWritingResponse(cacheRequest, response).also {
          if (cacheResponse != null) {
            // This will log a conditional cache miss only.
            listener.cacheMiss(call)
          }
        }
      }
       
      if (HttpMethod.invalidatesCache(networkRequest.method)) {
        try {
          // 不是get方法,移除缓存
          cache.remove(networkRequest)
        } catch (_: IOException) {
          // The cache cannot be written.
        }
      }
    }

    return response
  }

上面的代码中,调用CacheStrategy类的compute() 方法创建 CacheStrategy 。

  1. 如果 CacheControl中有onlyIfCached(不重新加载响应)指令,那么CacheStrategy的cacheResponse字段也为空。
  2. 当请求还是新鲜的(存在时间 age 小于新鲜时间 fresh ),那么 CacheStrategy 的 networkRequest 字段就为空,这时 CacheInterceptor 就会返回缓存中的响应。
  3. 当请求已经不新鲜时,CacheInterceptor 就会通过 ConnectInterceptor 和 CallServerInterceptor 获取响应
  • CacheStrategy 缓存策略
// CacheStrategy.kt  源码

//缓存策略类
class CacheStrategy{
    //如果我们需要请求网络 则networkRequest不为null 否则为null
    val networkRequest: Request?
    //请求的返回或者请求的响应 如果无法使用缓存(一般是过期或者无缓存 则为null)
    val cacheResponse: Response?
}

根据networkRequest和cacheResponse这两个变量来选择是否命中缓存。结论:

networkRequest\cacheResponsecacheResponse is nullcacheResponse is not null
networkRequest is nullHTTP_GATEWAY_TIMEOUT 504错误直接使用缓存
networkRequest is not null进行网络请求 并且缓存新response根据code(304) 判断是否需要重新request
// CacheStrategy.kt  源码

fun compute(): CacheStrategy {
    // 根据request的cache-control 和response 的cache-control判断
    val candidate = computeCandidate()

      // request仅使用缓存
    if (candidate.networkRequest != null && request.cacheControl.onlyIfCached) {
        return CacheStrategy(null, null)
      }

      return candidate
}

private fun computeCandidate(): CacheStrategy {
      // 如果为null 表示之前没有缓存
      if (cacheResponse == null) {
        return CacheStrategy(request, null)
      }

      // 如果缺少tls握手 直接请求网络
      if (request.isHttps && cacheResponse.handshake == null) {
        return CacheStrategy(request, null)
      }

      // 根据cacheResponse的code判断是否允许cache 
      if (!isCacheable(cacheResponse, request)) {
        return CacheStrategy(request, null)
      }

      //request的cacheControl
      val requestCaching = request.cacheControl
      // request的cacheControl 的noCache
      if (requestCaching.noCache || hasConditions(request)) {
        return CacheStrategy(request, null)
      }
      //response的cacheControl
      val responseCaching = cacheResponse.cacheControl

      val ageMillis = cacheResponseAge()
      var freshMillis = computeFreshnessLifetime()
      
      // 在freshMillis时间内是新鲜的,无需向服务器请求资源
      if (requestCaching.maxAgeSeconds != -1) {
        freshMillis = minOf(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds.toLong()))
      }

      // 最小刷新时间
      var minFreshMillis: Long = 0
      if (requestCaching.minFreshSeconds != -1) {
        minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds.toLong())
      }

      // 请求中Cache-Control字段  maxStale:客户端愿意接收一个超过缓存时间的资源
      var maxStaleMillis: Long = 0
      if (!responseCaching.mustRevalidate && requestCaching.maxStaleSeconds != -1) {
        maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds.toLong())
      }

      if (!responseCaching.noCache && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
        val builder = cacheResponse.newBuilder()
        if (ageMillis + minFreshMillis >= freshMillis) {
          builder.addHeader("Warning", "110 HttpURLConnection \"Response is stale\"")
        }
        val oneDayMillis = 24 * 60 * 60 * 1000L
        if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {
          builder.addHeader("Warning", "113 HttpURLConnection \"Heuristic expiration\"")
        }
        return CacheStrategy(null, builder.build())
      }

      // Find a condition to add to the request. If the condition is satisfied, the response body
      // will not be transmitted.
      val conditionName: String
      val conditionValue: String?
      when {
        etag != null -> {
          // If-None-Match:告诉服务器如果时间一致,返回状态码304
          conditionName = "If-None-Match"
          conditionValue = etag
        }
        
        // lastModified:该资源的最后更改时间
        lastModified != null -> {
         // If-Modified-Since:告诉服务器如果时间一致,返回状态码304
          conditionName = "If-Modified-Since"
          conditionValue = lastModifiedString
        }

        servedDate != null -> {
          conditionName = "If-Modified-Since"
          conditionValue = servedDateString
        }

        else -> return CacheStrategy(request, null) // No condition! Make a regular request.
      }

      val conditionalRequestHeaders = request.headers.newBuilder()
      conditionalRequestHeaders.addLenient(conditionName, conditionValue!!)

      val conditionalRequest = request.newBuilder()
          .headers(conditionalRequestHeaders.build())
          .build()
          //直接使用缓存
      return CacheStrategy(conditionalRequest, cacheResponse)
    }

companion object {
    /** Returns true if [response] can be stored to later serve another request. */
    fun isCacheable(response: Response, request: Request): Boolean {
      // Always go to network for uncacheable response codes (RFC 7231 section 6.1), This
      // implementation doesn't support caching partial content.
      when (response.code) {
        HTTP_OK,
        HTTP_NOT_AUTHORITATIVE,
        HTTP_NO_CONTENT,
        HTTP_MULT_CHOICE,
        HTTP_MOVED_PERM,
        HTTP_NOT_FOUND,
        HTTP_BAD_METHOD,
        HTTP_GONE,
        HTTP_REQ_TOO_LONG,
        HTTP_NOT_IMPLEMENTED,
        StatusLine.HTTP_PERM_REDIRECT -> {
          // These codes can be cached unless headers forbid it.
        }

        HTTP_MOVED_TEMP,
        StatusLine.HTTP_TEMP_REDIRECT -> {
          // These codes can only be cached with the right response headers.
          // http://tools.ietf.org/html/rfc7234#section-3
          // s-maxage is not checked because OkHttp is a private cache that should ignore s-maxage.
          if (response.header("Expires") == null &&
              response.cacheControl.maxAgeSeconds == -1 &&
              !response.cacheControl.isPublic &&
              !response.cacheControl.isPrivate) {
            return false
          }
        }

        else -> {
          // All other codes cannot be cached.
          return false
        }
      }

      // A 'no-store' directive on request or response prevents the response from being cached.
      return !response.cacheControl.noStore && !request.cacheControl.noStore
    }
  }

computeCandidate()方法 根据request和cacheResponse 的cacheControl计算出缓存策略。当响应的状态码为302(HTTP_MOVED_TEMP)或307(HTTP_TEMP_REDIRECT)时,isCacheable()方法就会根据响应的Expires首部和Cache-Control首部判断是否返回false(不缓存)。Expires首部的作用是服务器端可以指定一个绝对的日期,如果已经过了这个日期,就说明文档不“新鲜”了。

ConnectInterceptor

负责和服务器建立连接。流程是从连接池中查找连接;如果不存在,就创建连接并完成TCP、TLS握手。

查找连接
 // ConnectInterceptor.kt 源码
 
 @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val realChain = chain as RealInterceptorChain
    // exchange是用来和服务端交互的对象封装
    val exchange = realChain.call.initExchange(chain)
    val connectedChain = realChain.copy(exchange = exchange)
    return connectedChain.proceed(realChain.request)
  }
  
  // RealCall.kt 源码
  internal fun initExchange(chain: RealInterceptorChain): Exchange {
    ...

    val exchangeFinder = this.exchangeFinder!!
    // codec:编码解码器,确定是用Http1的方式还是以Http2的方式进行请求
    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
  }

从上面的代码来看,RealCall类的initExchange()方法初始化exchange数据交换类,然后根据下面的调用链initExchange()->ExchangeFinder#find()->ExchangeFinder#findHealthyConnection()->ExchangeFinder#findConnection()。

接着,下面是ExchangeFinder的findConnection()方法

// ExchangeFinder.kt 源码 

@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()
        }
      }

      // 如果调用的连接没有被释放,则重用它。
      // 这里我们没有调用connectionAcquired(),因为我们已经获得了它。
      if (call.connection != null) {
        check(toClose == null)
        return callConnection
      }

      // 释放 call connection
      toClose?.closeQuietly()
      eventListener.connectionReleased(call, callConnection)
    }

    // We need a new connection. Give it fresh stats.
    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 {
      // 进行TCP和TLS连接
      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)) {
      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
  }

indConnection()方法大致做了3件事,首先会尝试复用RealCall已有的连接,没有已有连接的话则尝试从连接池获取连接复用;如果连接池没有可复用连接的话,则创建一个新连接并返回给 CallServerInterceptor 使用。

接着,可以看一下RealConnectionPool类的callAcquirePooledConnection()方法

// RealConnectionPool.kt 源码

 fun callAcquirePooledConnection(
    address: Address,
    call: RealCall,
    routes: List<Route>?,
    requireMultiplexed: Boolean
  ): Boolean {
    for (connection in connections) {
      synchronized(connection) {
        // 判断connection是否支持多路复用
        if (requireMultiplexed && !connection.isMultiplexed) return@synchronized
        // 判断connection的host是否匹配
        if (!connection.isEligible(address, routes)) return@synchronized
        call.acquireConnectionNoEvents(connection)
        return true
      }
    }
    return false
  }

上面的方法是从连接池是否可以查找相应的连接。

建立连接
// RealConnection.kt 源码

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 {
         // 该请求是否使用了Proxy.Type.HTTP代理且目标是Https连接
        if (route.requiresTunnel()) {
          // 创建代理隧道连接;目的在于利用Http来代理请求Https
          connectTunnel(connectTimeout, readTimeout, writeTimeout, call, eventListener)
          if (rawSocket == null) {
            // We were unable to connect the tunnel but properly closed down our resources.
            break
          }
        } else {
          // 连接socket(TCP连接)
          connectSocket(connectTimeout, readTimeout, call, eventListener)
        }
        // 建立请求协议
        establishProtocol(connectionSpecSelector, pingIntervalMillis, call, eventListener)
        eventListener.connectEnd(call, route.socketAddress, route.proxy, protocol)
        break
      } catch (e: IOException) {
        ...
      }
    }

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

    idleAtNs = System.nanoTime()
  }

在 RealConnection 的 connect() 方法中首先会判断当前连接是否已连接,也就是 connect() 方法被调用过没有,如果被调用过的话,则抛出非法状态异常。如果没有连接过的话,则判断请求用的是不是HTTPS方案,是的话则连接隧道,不是的话则调用connectSocket()方法连接Socket。

// RealConnection.kt 源码

 @Throws(IOException::class)
 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)
    // TLS连接
    connectTls(connectionSpecSelector)
    eventListener.secureConnectEnd(call, handshake)

    if (protocol === Protocol.HTTP_2) {
      startHttp2(pingIntervalMillis)
    }
  }

判断当前地址是否是HTTPS;如果不是HTTPS,则判断当前协议是否是明文HTTP2,如果是的则调用startHttp2,开始Http2的握手动作,如果是Http/1.1则直接return返回;如果是HTTPS,就开始建立TLS安全协议连接了(connectTls);如果是HTTPS且为HTTP2,除了建立TLS连接外,还会调用startHttp2,开始Http2的握手动作。

 @Throws(IOException::class)
  private fun connectTls(connectionSpecSelector: ConnectionSpecSelector) {
    val address = route.address
    val sslSocketFactory = address.sslSocketFactory
    var success = false
    var sslSocket: SSLSocket? = null
    try {
      // 利用请求地址host,端口以及TCP socket共同创建sslSocket
      sslSocket = sslSocketFactory!!.createSocket(
          rawSocket, address.url.host, address.url.port, true /* autoClose */) as SSLSocket

      // 为Socket 配置加密算法,TLS版本等
      val connectionSpec = connectionSpecSelector.configureSecureSocket(sslSocket)
      if (connectionSpec.supportsTlsExtensions) {
        Platform.get().configureTlsExtensions(sslSocket, address.url.host, address.protocols)
      }

      // 调用startHandshake()进行强制握手
      sslSocket.startHandshake()
      // block for session establishment
      val sslSocketSession = sslSocket.session
      val unverifiedHandshake = sslSocketSession.handshake()

      //验证服务器证书的合法性.
      if (!address.hostnameVerifier!!.verify(address.url.host, sslSocketSession)) {
        val peerCertificates = unverifiedHandshake.peerCertificates
        if (peerCertificates.isNotEmpty()) {
          val cert = peerCertificates[0] as X509Certificate
          throw SSLPeerUnverifiedException("""
              |Hostname ${address.url.host} not verified:
              |    certificate: ${CertificatePinner.pin(cert)}
              |    DN: ${cert.subjectDN.name}
              |    subjectAltNames: ${OkHostnameVerifier.allSubjectAltNames(cert)}
              """.trimMargin())
        } else {
          throw SSLPeerUnverifiedException(
              "Hostname ${address.url.host} not verified (no certificates)")
        }
      }

      val certificatePinner = address.certificatePinner!!

      handshake = Handshake(unverifiedHandshake.tlsVersion, unverifiedHandshake.cipherSuite,
          unverifiedHandshake.localCertificates) {
        certificatePinner.certificateChainCleaner!!.clean(unverifiedHandshake.peerCertificates,
            address.url.host)
      }

      // 利用握手记录进行证书锁定校验
      certificatePinner.check(address.url.host) {
        handshake!!.peerCertificates.map { it as X509Certificate }
      }

      // 连接成功则保存握手记录和ALPN协议
      val maybeProtocol = if (connectionSpec.supportsTlsExtensions) {
        Platform.get().getSelectedProtocol(sslSocket)
      } else {
        null
      }
      socket = sslSocket
      source = sslSocket.source().buffer()
      sink = sslSocket.sink().buffer()
      protocol = if (maybeProtocol != null) Protocol.get(maybeProtocol) else Protocol.HTTP_1_1
      success = true
    } finally {
      if (sslSocket != null) {
        Platform.get().afterHandshake(sslSocket)
      }
      if (!success) {
        sslSocket?.closeQuietly()
      }
    }
  }
CallServerInterceptor

负责与服务器进行数据交互;负责向服务器发送请求数据、从服务器读取响应数据。

@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()
    
    // 将请求头写入到socket中,底层通过ExchangeCodec协议类
    //(对应Http1ExchangeCodec和Http2ExchangeCodec),
    // 最终是通过Okio来实现的,具体实现在RealBufferedSink这个类里面
    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) {
        // 如果支持复用 传输request body
        if (requestBody.isDuplex()) {
          // Prepare a duplex body so that the application can send a request body later.
          exchange.flushRequest()
          val bufferedRequestBody = exchange.createRequestBody(request, true).buffer()
          requestBody.writeTo(bufferedRequestBody)
        } else {
          // Write the request body if the "Expect: 100-continue" expectation was met.
          val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
          requestBody.writeTo(bufferedRequestBody)
          bufferedRequestBody.close()
        }
      } else {
        exchange.noRequestBody()
        if (!exchange.connection.isMultiplexed) {
          // If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
          // from being reused. Otherwise we're still obligated to transmit the request body to
          // leave the connection in a consistent state.
          exchange.noNewExchangesOnConnection()
        }
      }
    } else {
      exchange.noRequestBody()
    }

    if (requestBody == null || !requestBody.isDuplex()) {
      //Request结束
      exchange.finishRequest()
    }
    
    if (responseBuilder == null) {
      responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
      if (invokeStartEvent) {
        exchange.responseHeadersStart()
        invokeStartEvent = false
      }
    }
    
    // 返回response
    var response = responseBuilder
        .request(request)
        .handshake(exchange.connection.handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build()
    
    var code = response.code
    if (code == 100) {
      // 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) {
      // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
      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
  }

资料

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值