OkHttp源码学习 4. BridgeInterceptor 连接桥路由拦截器

BridgeIntercept

还没写完,顺序会有些乱,那篇写完发那篇

主要作用

连接桥路由.

  1. 添加一些必须的请求头信息

  2. 对服务器返回的数据进行解压缩(如果服务端返回的响应的编码请求头(Content-Encoding)是(gzip)格式的话,如果不是则直接返回)

源码解析
/**
 * @param cookieJar 我们在初始化OkHttpClient的时候设置的CookieJar
 */
class BridgeInterceptor(private val cookieJar: CookieJar) : Interceptor {

  /**
   * 实现于Interceptor的方法
   * @param chain  链表
   */
  override fun intercept(chain: Interceptor.Chain): Response {
    // 获取用户请求
    val userRequest = chain.request()
    // 根据用户请求重新获取一个RequestBuilder
    val requestBuilder = userRequest.newBuilder()
	// 获取请求的body信息
    val body = userRequest.body
    // 如果body不为空
    if (body != null) {
      // 获取body的类型,application/json  application/xml application/x-www-form-urlencoded multipart/form-data等
      val contentType = body.contentType()
      // 如果这个contentType不为空,则添加这个类型
      if (contentType != null) {
        requestBuilder.header("Content-Type", contentType.toString())
      }
	  // 获取body的长度
      val contentLength = body.contentLength()
      // 如果这个body的长度不为空
      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())
    }
	// 如果请求的连接为空的话
    if (userRequest.header("Connection") == null) {
      // 则添加相关的请求头
      requestBuilder.header("Connection", "Keep-Alive")
    }
	// 定义变量,是否进行GZIP压缩数据
    var transparentGzip = false
    // 如果获取到的请求关于是否压缩的请求头为空
    if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
      // 那么我们将这个变量定义为true
      transparentGzip = true
      // 且添加相关的请求头
      requestBuilder.header("Accept-Encoding", "gzip")
    }
	// 我们从设置的CookieJar中根据请求地址获取相关的cookie
    val cookies = cookieJar.loadForRequest(userRequest.url)
    // 如果cookie为空
    if (cookies.isNotEmpty()) {
      // 那么我们添加cookie的请求头
      requestBuilder.header("Cookie", cookieHeader(cookies))
    }
	
    // 如果用户得UserAgent请求头为空的话,那么我们就添加系统默认的User-Agent:okhttp/okhttp-version
    if (userRequest.header("User-Agent") == null) {
      requestBuilder.header("User-Agent", userAgent)
    }
	
    // 通过调用链的层层调用获取最终返回的响应.且看这里发送的可是我们包装过的request哦!!!!
    val networkResponse = chain.proceed(requestBuilder.build())

    // 调用CookieJar的扩展方法,等下看下面的解释吧
    cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)

    // 根据返回的响应我们重新构建出一个响应来
    val responseBuilder = networkResponse.newBuilder()
        .request(userRequest)
	
    /**
     * 处理响应数据,如果我们没有在请求头添加 Content-Encoding 和 Range 请求头,那么我们的transparentGzip则为true,反之
     * 			  false
     * 			  如果服务器返回的响应的响应头中包含Content-Encoding 请求头,且这个值为gzip
     * 			  如果这个响应的响应体有可能为0
     * 我们就对数据进行gzip解压缩,否则就不处理数据
     */
    if (transparentGzip &&
        "gzip".equals(networkResponse.header("Content-Encoding"), ignoreCase = true) &&
        networkResponse.promisesBody()) {
      // 获取到响应体
      val responseBody = networkResponse.body
      // 如果响应体不为空
      if (responseBody != null) {
        // 通过响应体构建出GzipSource
        val gzipSource = GzipSource(responseBody.source())
        // 重新构建出一个响应头,需要移除对应的两个请求头,因为都属于gzip,肯定长度啥的没了
        val strippedHeaders = networkResponse.headers.newBuilder()
            .removeAll("Content-Encoding")
            .removeAll("Content-Length")
            .build()
        // 将响应头给到响应
        responseBuilder.headers(strippedHeaders)
        // 构建出用户使用的ResponseBody
        val contentType = networkResponse.header("Content-Type")
        responseBuilder.body(RealResponseBody(contentType, -1L, gzipSource.buffer()))
      }
    }
	// 最后返回响应到上一个拦截器,应该就是RetryAndFollowUpInterceptor
    return responseBuilder.build()
  }

  /** 
   * Returns a 'Cookie' HTTP request header with all cookies, like `a=b; c=d`.
   * 
   * 根据用户保存的Cookie,通过StringBuilder,获取到cookie字符串.例如: 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)
    }
  }
}
额外扩展
CookieJar的扩展方法receiveHeaders
/**
 * 总体来说,这个方法是对返回的响应的Cookie的处理
 */
fun CookieJar.receiveHeaders(url: HttpUrl, headers: Headers) {
    // 如果CookieJar的策略是不缓存Cookie,那么我们直接进行返回 
    if (this === CookieJar.NO_COOKIES) return
	// 如果不是,我们对服务器返回的Cookie进行解析,获取到一个List<Cookie>
    val cookies = Cookie.parseAll(url, headers)
    // 如果解析到的Cookies是空,那么就返回
    if (cookies.isEmpty()) return
    // 如果解析到的Cookies不为空,那么我们就进行存储,而这个方法属于CookieJar的方法,我们在实现CookieJar的时候,会实现该方法
    saveFromResponse(url, cookies)
}
上面BridgeIntercept涉及的Header
  1. Content-Type

    内容类型,用于定义网络文件的类型和网页的编码,决定采用什么形式和什么编码来读取这个文件

    常见的媒体类型:

    text/html :HTML格式

    text/plain :纯文本格式

    text/xml : XML格式

    image/gif :gif格式图片

    image/jpeg :jpg格式图片

    image/png :png格式图片

    以application开头的媒体格式类型:

    application/json :JSON数据格式

    application/xhtml+xml : XHTML格式

    application/xml :XML数据格式

    application/pdf :pdf格式

    application/msword :word文档格式

    application/octet-stream :二进制流数据(如常见的文件下载)

    application/x-www-form-urlencoded :常见的表单上传

    另一种媒体长传格式类型:

    multipart/from-data :需要在表单中进行文件上传时,就需要使用此格式

    tips:

    这个请求头是你自己已经添加过了,那么就不在需要添加了.如果你没有写,库会根据你的请求体的类型,给你进行添加

  2. Content-Length

    内容长度

    tips:

    这个库会主动给你添加,无论你是否有写

    如果你的长度是没有的话,库会删除这个请求头. 如果你的长度是有的话,库会根据请求体的body从而进行赋值

  3. Transfer-Encoding

    内容长度不确定,常用值为:chunked

    tips:

    跟上面的Content-Length一块出现,但是属于老死不相往来的那种,有你没我,有我没你.体现在代码上就是,有长度就没我,没有长度就有我

  4. Host

    网络域名

    tips:

    就是指请求的域名

    这个请求头也是,如果你自己添加了,库就不会再给你添加了

  5. Connection

    是否需要保持连接.一般值为Keep-Alive.

    tips:

    解释: https://blog.csdn.net/xiaoduanayu/article/details/78386508

    这个请求头也是,如果你自己添加了,库就不会再给你添加了

  6. Accept-Encoding、Range

    设置编码方式

    tips:

    不管有没有用GZIP压缩请求内容,请不要动这个值,否则你会发现,你设置了GZIP压缩,结果乱码,还不能用了.

    因为源码中设置了如果你没有设置这两个请求头的时候,transparentGzip才会为true,而在响应来的时候,会根据这个值进行判断,如果为true,才可以进去,否则你就是设置了也是乱码,注意注意!!!

  7. Cookie

    用户标记

    tips:

    库不会给你设置,只能你自己来

  8. User-Agent

    用户代理,设置了你访问连接的方式,比如手机还是浏览器

    tips:

    这个请求头也是,如果你自己添加了,库就不会再给你添加了

总结:
  1. 将我们的请求进行了包装,添加了一些请求头的信息
  2. 对服务器返回的数据进行解压缩(如果服务端返回的响应的编码请求头(Content-Encoding)是(gzip)格式的话,如果不是则直接返回).
  3. Cookie需要自己去实现,OkHttp只提供了接口
  4. 结束了!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
E/AndroidRuntime: FATAL EXCEPTION: Thread-18 Process: com.example.read, PID: 22568 java.lang.RuntimeException: java.net.UnknownServiceException: CLEARTEXT communication to 192.168.210.113 not permitted by network security policy at com.example.read.upload_serverActivity$1.run(upload_serverActivity.java:111) at java.lang.Thread.run(Thread.java:920) Caused by: java.net.UnknownServiceException: CLEARTEXT communication to 192.168.210.113 not permitted by network security policy at okhttp3.internal.connection.RealRoutePlanner.planConnectToRoute$okhttp(RealRoutePlanner.kt:195) at okhttp3.internal.connection.RealRoutePlanner.planConnect(RealRoutePlanner.kt:152) at okhttp3.internal.connection.RealRoutePlanner.plan(RealRoutePlanner.kt:67) at okhttp3.internal.connection.FastFallbackExchangeFinder.launchTcpConnect(FastFallbackExchangeFinder.kt:118) at okhttp3.internal.connection.FastFallbackExchangeFinder.find(FastFallbackExchangeFinder.kt:62) at okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.kt:267) at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:32) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:84) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:65) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:205) at okhttp3.internal.connection.RealCall.execute(RealCall.kt:158) at com.example.read.upload_serverActivity$1.run(upload_serverActivity.java:106) at java.lang.Thread.run(Thread.java:920) 怎么解决
05-29

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值