美文网首页
2019-11-05Okhttp源码解析-拦截器<三>

2019-11-05Okhttp源码解析-拦截器<三>

作者: 猫KK | 来源:发表于2019-11-05 18:03 被阅读0次

5. CallServerInterceptor

前面分析了前四个拦截器,现在到最后一个,用于请求服务器数据

class CallServerInterceptor(private val forWebSocket: Boolean) : Interceptor {

  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val realChain = chain as RealInterceptorChain
    //获取exchange,exchange 是在ConnectInterceptor拦截器中初始化的
    val exchange = realChain.exchange()
    //获取请求
    val request = realChain.request()
    //获取请求数据
    val requestBody = request.body
    val sentRequestMillis = System.currentTimeMillis()
    //向服务器写请求头信息,就是通过ConnectInterceptor 获取的socket的输入输出流向服务器写数据
    exchange.writeRequestHeaders(request)

    var responseHeadersStarted = false
    var responseBuilder: Response.Builder? = null
    //判断方法是GET或HEAD,如果是这两种方法,是不需要向服务器写请求数据的
    if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
      // 判断是否为100-continue 情况
      if ("100-continue".equals(request.header("Expect"), ignoreCase = true)) {
        exchange.flushRequest()
        responseHeadersStarted = true
        exchange.responseHeadersStart()
        responseBuilder = exchange.readResponseHeaders(true)
      }
      //不为100-continue 情况,向服务器写请求数据
      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
    //判断状态码为100情况
    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)
    //处理101情况
    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 {
      //不是101情况,获取服务器的响应数据,赋值到body中
      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()
    }
    //判断204、205情况
    if ((code == 204 || code == 205) && response.body?.contentLength() ?: -1L > 0L) {
      throw ProtocolException(
          "HTTP $code had non-zero Content-Length: ${response.body?.contentLength()}")
    }
     //返回响应数据
    return response
  }
}

对于向服务请写请求头

  @Throws(IOException::class)
  fun writeRequestHeaders(request: Request) {
    try {
      eventListener.requestHeadersStart(call)
      //通过前面获取的流来写
      codec.writeRequestHeaders(request)
      eventListener.requestHeadersEnd(call, request)
    } catch (e: IOException) {
      eventListener.requestFailed(call, e)
      trackFailure(e)
      throw e
    }
  }

codec 是在ConnectInterceptor拦截器中实现的,也就是RealConnection.newCodec()方法

//RealConnection

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

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

判断http2 和http1 关于两者的区别,请自行了解,这里来看http1 的情况,所以codec的值为Http1ExchangeCodec,看Http1ExchangeCodec.writeRequestHeaders()方法

override fun writeRequestHeaders(request: Request) {
    val requestLine = RequestLine.get(
        request, realConnection!!.route().proxy.type())
    writeRequest(request.headers, requestLine)
  }

fun writeRequest(headers: Headers, requestLine: String) {
    check(state == STATE_IDLE) { "state: $state" }
    //通过okio 向服务器写数据
    sink.writeUtf8(requestLine).writeUtf8("\r\n")
    for (i in 0 until headers.size) {
      sink.writeUtf8(headers.name(i))
          .writeUtf8(": ")
          .writeUtf8(headers.value(i))
          .writeUtf8("\r\n")
    }
    sink.writeUtf8("\r\n")
    state = STATE_OPEN_REQUEST_BODY
  }

循环头信息,不断的写数据,注意,往服务器写数据是规定格式的,所以就会像上面一样又是\r\n的

对于写请求数据,一般来说我们要写表单数据时是这样构建的

        var build = FormBody.Builder()
            .add("text", "text")
            .build()
        var request = Request.Builder()
            .url("https://wanandroid.com/wxarticle/chapters/json")
            .post(build)
            .build()

所以拦截器中的requestBody就是FormBody对象,看里面的writeTo方法

 @Throws(IOException::class)
  override fun writeTo(sink: BufferedSink) {
    writeOrCountBytes(sink, false)
  }


  private fun writeOrCountBytes(sink: BufferedSink?, countBytes: Boolean): Long {
    var byteCount = 0L
    //获取buffer
    val buffer: Buffer = if (countBytes) Buffer() else sink!!.buffer
    //循环不断往服务器写数据
    for (i in 0 until encodedNames.size) {
      if (i > 0) buffer.writeByte('&'.toInt())
      buffer.writeUtf8(encodedNames[i])
      buffer.writeByte('='.toInt())
      buffer.writeUtf8(encodedValues[i])
    }
    if (countBytes) {
      byteCount = buffer.size
      buffer.clear()
    }
    return byteCount
  }

到此,okhttp 的拦截器都已经分析完成。

相关文章

网友评论

      本文标题:2019-11-05Okhttp源码解析-拦截器<三>

      本文链接:https://www.haomeiwen.com/subject/ykdebctx.html