再学Android:OkHttp源码探究(七)CallServerInterceptor

221 阅读4分钟

前言

终于!我们来到了ok内置五大拦截器最后一个拦截器的探究。关于这个拦截器我们直接来看一下源码的注释:

This is the last interceptor in the chain. It makes a network call to the server.

看到这里是不是有种拨开云雾见明月的感觉。话不多说我们继续先看下源码:


  @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 responseHeadersStarted = false
    var responseBuilder: Response.Builder? = null
    if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
      // If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
      // Continue" response before transmitting the request body. If we don't get that, return
      // what we did get (such as a 4xx response) without ever transmitting the request body.
      if ("100-continue".equals(request.header("Expect"), ignoreCase = true)) {
        exchange.flushRequest()
        responseHeadersStarted = true
        exchange.responseHeadersStart()
        responseBuilder = exchange.readResponseHeaders(true)
      }
      if (responseBuilder == null) {
        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()) {
      exchange.finishRequest()
    }
    if (!responseHeadersStarted) {
      exchange.responseHeadersStart()
    }
    if (responseBuilder == null) {
      responseBuilder = exchange.readResponseHeaders(false)!!
    }
    var response = responseBuilder
        .request(request)
        .handshake(exchange.connection()!!.handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build()
    var code = response.code
    if (code == 100) {
      // server sent a 100-continue even though we did not request one.
      // try again to read the actual response
      response = exchange.readResponseHeaders(false)!!
          .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
  }
  

流程分析

是不是感觉臭长臭长的,但是莫慌!我们来一点一点攻克它。 我们先来看下面几行代码

    val realChain = chain as RealInterceptorChain
    val exchange = realChain.exchange()
    val request = realChain.request()
    val requestBody = request.body
    val sentRequestMillis = System.currentTimeMillis()

这里几行基本上取出对应的参数,并且设置当前时间为请求发出时间。关键的是从realChain中取出Exchange对象,这个对象是后续操作的比较关键的,之前在connectInterceptor中构建出来并且传递到当前的。

接着网下看:通过 exchange.writeRequestHeaders(request)方法来开始写入请求头,接着根据请求方式和请求体是否为空来进入分支逻辑:

请求体不为空(主要针对POST请求)

100-continue

刚开始看到这里笔者也是有点懵逼的,这是什么鬼header。于是开始Google大法:

http 100-continue用于客户端在发送POST数据给服务器前,征询服务器情况,看服务器是否处理POST的数据,如果不处理,客户端则不上传POST数据,如果处理,则POST上传数据。在现实应用中,通常在POST大数据时,才会使用100-continue协议。
来源:https://zhuanlan.zhihu.com/p/30830041

接着就是读取响应头,同时通知eventListener响应头开始读取。我们看源码的调用链是调用ExchangeCodec的readResponseHeaders()方法来读取响应头,具体类似于BridgeInterceptor中的intercept方法。

紧接着根据responseBuilder是否为null来进入分支逻辑,这个responseBuilder是根据又是根据header中的Expect是否为100-continue来判断的。接下来就是比较简单了,根据request构建出来一个requesBody,并且把其写入到请求的body中。

请求体为空

请求体为null的情况下就比较简单了直接调用noRequestBody()方法。表示当前request阶段已经完成.

Response阶段

我们知道请求响应的开始也是从读取header。在上面经过发起Request结束之后,ok就已经做好准备进行response的接收。同样事件的发生也是会回调eventListener对应的监听。

    var response = responseBuilder
        .request(request)
        .handshake(exchange.connection()!!.handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build()

可以看到经过一系列操作,我们已经拿到了服务器的返回。最终就是通过:

 response.newBuilder()
          .body(exchange.openResponseBody(response))
          .build()

这个方法来将服务端解析为客户端真正可用的resonse。

总结

CallServerInterceptor的流程解析基本上到这里就结束了。纵观ok内置几个拦截器的源码,其实都不是很复杂,内部是采用了优秀的设计模式将以此请求中的每个阶段都做了分层,然后采用递归调用的思想通过几个拦截器将一个请求由上而下、再由下而上的完成。其中的设计模式以及代码的分离都是非常值得我们来学习的。

基本上关于拦截器的分析到这里就告一段了。整个流程下来笔者对于ok内部的很多东西也是一笔带过,后续将会好机会再详细分析。通过对ok一次请求的整体流程的探究,深知纸上得来终觉浅,绝知此事要躬行,这句话也跟大家一起共勉。