转载

OkHttp - BridgeInterceptor源码简析

Github: okhttp 分析版本: 930d4d0

Bridges from application code to network code

intercept(chain: Interceptor.Chain)

class BridgeInterceptor(private val cookieJar: CookieJar) : Interceptor {

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

    // 默认保持连接 Keep-Alive
    if (userRequest.header("Connection") == null) {
      requestBuilder.header("Connection", "Keep-Alive")
    }

    // 默认 GZIP 压缩
    // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
    // the transfer stream.
    var transparentGzip = false
    if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
      transparentGzip = true
      requestBuilder.header("Accept-Encoding", "gzip")
    }

    // cookies 信息
    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())

    // 接受服务器返回的 Cookies
    cookieJar.receiveHeaders(userRequest.url, networkResponse.headers())

    val responseBuilder = networkResponse.newBuilder()
        .request(userRequest)

    if (transparentGzip &&
        "gzip".equals(networkResponse.header("Content-Encoding"), ignoreCase = true) &&
        networkResponse.promisesBody()) {
      // 当服务器返回的数据是 GZIP 压缩的,那么客户端就进行解压操作
      val responseBody = networkResponse.body()
      if (responseBody != null) {
        val gzipSource = GzipSource(responseBody.source())
        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()))
      }
    }

    // 构建 Response
    return responseBuilder.build()
  }

  /** Returns a 'Cookie' HTTP request header with all cookies, like `a=b; c=d`. */
  private fun cookieHeader(cookies: List<Cookie>): String = buildString {
    cookies.forEachIndexed { index, cookie ->
      if (index > 0) append("; ")
      append(cookie.name).append('=').append(cookie.value)
    }
  }
}
  • 将用户构建的一个 Request 请求转化为能够进行网络访问的请求
  • 将 Request 丢给下一个拦截器去进行网络请求
  • 将网络请求回来的响应 Response 转化为用户可用的 Response

Request 转换

因为用户在构建一个 Request 对象的时侯往往只会简单设置 url, RequestBody 等几个属性,但是真正要发送一个请求实际上没这么简单,在原来的基础上添加了很多请求头

  • Content-Type: 网络请求类型
  • Content-Length: 请求体内容的长度,与 Transfer-Encoding 互斥
  • Transfer-Encoding: 值为 chunked 表示请求体的内容大小是未知的,与 Content-Length 互斥
  • Host: 请求的 url 的主机
  • Connection: 默认就是 Keep-Alive,就是一个 TCP 连接之后不会关闭,保持连接状态
  • Accept-Encoding: 默认是 gzip,告诉服务器客户端支持 gzip 编码的响应
  • Cookie: 当请求设置了 Cookie 那么就是添加 Cookie 这个请求头
  • User-Agent: 根据 OKHTTP 的版本来返回
原文  http://yydcdut.com/2019/07/09/okhttp-bridge-interceptor-analyse/
正文到此结束
Loading...