75

OkHttp - BridgeInterceptor源码简析

 4 years ago
source link: https://www.tuicool.com/articles/reeU3u7
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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 的版本来返回

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK