当前位置: 代码迷 >> 综合 >> OKHttp 4全面解析(二)——拦截器(一):RetryAndFollowUpInterceptor拦截器
  详细解决方案

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

热度:92   发布时间:2023-10-13 11:34:06.0

文章目录

  • 1. 作用
  • 2. 错误重试
  • 3. 重定向
  • 4. 验证
  • 5. 其他

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.codeval method = userResponse.request.methodwhen (responseCode) {
    HTTP_PROXY_AUTH -> {
     //代理服务器要求验证val selectedProxy = route!!.proxyif (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.bodyif (requestBody != null && requestBody.isOneShot()) {
    return null}val priorResponse = userResponse.priorResponseif (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 UnavailableHTTP_UNAVAILABLE -> {
    val priorResponse = userResponse.priorResponseif (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 delayreturn 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.bodyif (requestBody != null && requestBody.isOneShot()) {
    return null}if (exchange == null || !exchange.isCoalescedConnection) {
    return null}exchange.connection.noCoalescedConnections()return userResponse.request}else -> return null}}
  相关解决方案