OKHttp 4全面解析(二)——拦截器(一):RetryAndFollowUpInterceptor拦截器

10 篇文章 0 订阅
2 篇文章 0 订阅

1. 作用

此拦截器的主要功能:

  • 重试
  • 重定向跟踪
  • 验证

2. 错误重试

发生错误后,首先会判断该请求是不是可恢复的
有以下情况之一,则该请求是不可恢复的,否则是可恢复的

  1. 配置了retryOnConnectionFailurefalse,即指定错误时不恢复
  2. 请求只能发送一次,并且已经发送了(已经发送是指,请求已经经过了所有的拦截器处理,数据已经写入到Socket中。在源码中,只有传输文件限定了只能发送一次)
  3. 当错误类型为:底层协议出错、使用Https协议时,证书认证出错,证书锁定出错。
  4. Sockert 连接超时(不是HTTP超时)
  5. 没有可用路由
  • 如果是可恢复的,则会在记录下当前错误后,将该请求重新发送
  • 如果是不可恢复的,则抛出IO异常

3. 重定向

当接收到响应后,会根据响应的状态码来判断是否需要重定向。
重定向的状态码为:

  • 307:临时重定向
  • 308:永久重定向
  • 300:请选择
  • 301:永久移动
  • 302:临时移动
  • 303:see other

如果需要重定向,会重新构建一个Request,然后发送

4. 验证

根据响应的状态码判断是否需要向服务器(或理服务器)发送验证信息。
当接收到的响应状态码为:

  • 407:代理服务器验证
  • 401:服务器验证

会构建一个新的包含验证信息的Request,再次发送。

5. 其他

当接收到诸如:

  • 503服务器当前不可用
  • 408:http超时

等状态码时。

会检查Retry-After的值来判断是否可以重新发送请求。
判断标准: 有Retry-After头信息,并且Retry-After表明了可以立刻再次请求

有一个方法:

 val followUp = followUpRequest(response, exchange)

这个方法的作用是:根据response,判断需不需要再次发送请求。如果需要,就返回一个待发送的新请求,如果不需要,就返回null。(ps:上面的重定向和验证都是需要重新发送新的请求的)。

方法详细信息

 /********************//
 /**
   * Figures out the HTTP request to make in response to receiving [userResponse]. This will
   * either add authentication headers, follow redirects or handle a client request timeout. If a
   * follow-up is either unnecessary or not applicable, this returns null.
   */
  @Throws(IOException::class)
  private fun followUpRequest(userResponse: Response, exchange: Exchange?): Request? {
    val route = exchange?.connection?.route()
    val responseCode = userResponse.code

    val method = userResponse.request.method
    when (responseCode) {
      HTTP_PROXY_AUTH -> { //代理服务器要求验证
        val selectedProxy = route!!.proxy
        if (selectedProxy.type() != Proxy.Type.HTTP) {
          throw ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy")
        }
        return client.proxyAuthenticator.authenticate(route, userResponse)
      }
	//服务器要求验证
      HTTP_UNAUTHORIZED -> return client.authenticator.authenticate(route, userResponse)
	//重定向
      HTTP_PERM_REDIRECT, HTTP_TEMP_REDIRECT, HTTP_MULT_CHOICE, HTTP_MOVED_PERM, HTTP_MOVED_TEMP, HTTP_SEE_OTHER -> {
        return buildRedirectRequest(userResponse, method)
      }
	//HTTP 超时
      HTTP_CLIENT_TIMEOUT -> {
        // 408's are rare in practice, but some servers like HAProxy use this response code. The
        // spec says that we may repeat the request without modifications. Modern browsers also
        // repeat the request (even non-idempotent ones.)
        if (!client.retryOnConnectionFailure) {
          // The application layer has directed us not to retry the request.
          return null
        }

        val requestBody = userResponse.request.body
        if (requestBody != null && requestBody.isOneShot()) {
          return null
        }
        val priorResponse = userResponse.priorResponse
        if (priorResponse != null && priorResponse.code == HTTP_CLIENT_TIMEOUT) {
          // We attempted to retry and got another timeout. Give up.
          return null
        }

        if (retryAfter(userResponse, 0) > 0) {
          return null
        }

        return userResponse.request
      }
	//503 Server Unavailable
      HTTP_UNAVAILABLE -> {
        val priorResponse = userResponse.priorResponse
        if (priorResponse != null && priorResponse.code == HTTP_UNAVAILABLE) {
          // We attempted to retry and got another timeout. Give up.
          return null
        }

        if (retryAfter(userResponse, Integer.MAX_VALUE) == 0) {
          // specifically received an instruction to retry without delay
          return userResponse.request
        }

        return null
      }
	//未知
      HTTP_MISDIRECTED_REQUEST -> {
        // OkHttp can coalesce HTTP/2 connections even if the domain names are different. See
        // RealConnection.isEligible(). If we attempted this and the server returned HTTP 421, then
        // we can retry on a different connection.
        val requestBody = userResponse.request.body
        if (requestBody != null && requestBody.isOneShot()) {
          return null
        }

        if (exchange == null || !exchange.isCoalescedConnection) {
          return null
        }

        exchange.connection.noCoalescedConnections()
        return userResponse.request
      }

      else -> return null
    }
  }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值