OkHttp解析系列-重定向和出错重试

原创 2015年11月18日 22:21:52

&emps;这是OkHttp系列博文的第一篇,之前写过一篇草稿,介绍OkHttp的整体框架,但是感觉涉及的知识太多,无法在一篇中讲述清楚,所以,之后的博文都只关注某一方面的知识,争取文章短小精悍。
 今天主要研究一下OkHttp发送Http请求过程中的重定向和出错重试,主要涉及的源码文件有Call.java``HttpEngine.java
 我们今天研究CallResponse getResponse(Request request, boolean forWebSocket) throws IOException函数,它是你调用Call.execute()返回Response所调用的核心函数,主要功能是新建一个HttpEngine发送Request然后处理出错重试和重定向问题。

设置Headers

  // Copy body metadata to the appropriate request headers.
    RequestBody body = request.body();
    if (body != null) {
      Request.Builder requestBuilder = request.newBuilder();//拷贝了内部数据

      MediaType contentType = body.contentType();
      if (contentType != null) {
        requestBuilder.header("Content-Type", contentType.toString());
      }

      long contentLength = body.contentLength();
      if (contentLength != -1) {
        requestBuilder.header("Content-Length", Long.toString(contentLength));
        requestBuilder.removeHeader("Transfer-Encoding");
      } else {
        requestBuilder.header("Transfer-Encoding", "chunked");
        requestBuilder.removeHeader("Content-Length");
      }

      request = requestBuilder.build();
    }

 这是函数的第一部分,主要是将RequestBody的一些元数据拷贝到Header的首部中,主要是Content-TypeTransfer-EncodingContent-Type相信大家都了解,标示RequestBodyMime-Type,格式为主类型/子类型,比如text/xml。而Transfer-Encoding是表示一种网络传输的方式,想具体了解的同学可以看一下这个链接点我.

出错重试

// Create the initial HTTP engine. Retries and redirects need new engine for each attempt.
    // 建立一个初始的http 引擎,每次重试和重定向都需要新的引擎
    engine = new HttpEngine(client, request, false, false, forWebSocket, null, null, null, null);

    int followUpCount = 0; //连续发送请求
    while (true) {
      if (canceled) { //如果被取消啦
        engine.releaseConnection();
        throw new IOException("Canceled");
      }

      try {
        engine.sendRequest();
        engine.readResponse();
      } catch (RequestException e) {
        // The attempt to interpret the request failed. Give up.
        throw e.getCause();
      } catch (RouteException e) {
        // The attempt to connect via a route failed. The request will not have been sent.
        HttpEngine retryEngine = engine.recover(e); //重试引擎
        if (retryEngine != null) {
          engine = retryEngine;
          continue;
        }
        // Give up; recovery is not possible.
        throw e.getLastConnectException();
      } catch (IOException e) {
        // An attempt to communicate with a server failed. The request may have been sent.
        HttpEngine retryEngine = engine.recover(e, null);
        if (retryEngine != null) {
          engine = retryEngine;
          continue;
        }

        // Give up; recovery is not possible.
        throw e;
      }
      .......

 在这段代码中,OkHttp建立一个HttpEngine对象来负责Http层级的请求的发送和回复的接收,HttpEngine会在之后的博文中详细讲解。然后进入了一个while循环,这个循环其实主要是处理重定向问题的。我们在这一节中主要关注catch中的逻辑,这是用于处理出错重试的逻辑。由于外层有一个while循环,所以在catch中尝试获得retryEngine,如果有就continue,没有就抛出异常。

重定向处理

Response response = engine.getResponse();
      // followUp这个是优化http connection的使用率的吗?
      Request followUp = engine.followUpRequest();

      if (followUp == null) {
        if (!forWebSocket) { //如果没有followup并且不是为了websocket
          engine.releaseConnection();//关闭连接
        }
        return response;
      }

      if (++followUpCount > MAX_FOLLOW_UPS) {
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      }

      if (!engine.sameConnection(followUp.httpUrl())) { //如果followup的httpUrl不是同一个连接,也就是
        //schema,host or port 有一个不同
        engine.releaseConnection();
      }
      //复用了上一次的connection啊!!!!
      Connection connection = engine.close();
      request = followUp;
      //继续处理,有可能是重定向啦
      engine = new HttpEngine(client, request, false, false, forWebSocket, connection, null, null,
          response);

 这里我们可以看到Http重定向的机制。Request request = engine.followUpRequest()来获得重定向需要发送的Request,如果没有或者重定向次数大于MAX_FOLLOW_UPS就不会重新发送重定向请求。然后判断重定向请求和原请求的HttpUrl是否相同,否则也不会发送重定向请求。然后Connection connection = engine.close()会释放资源并且复用上次的连接,然后新建一个HttpEngine然后继续While循环发送请求。

重定向状态码解析

  public Request followUpRequest() throws IOException {
    if (userResponse == null) throw new IllegalStateException();
    Proxy selectedProxy = getRoute() != null
        ? getRoute().getProxy()
        : client.getProxy();
    int responseCode = userResponse.code();

    switch (responseCode) {
      case HTTP_PROXY_AUTH: //407 Proxy authentication required 要先经过代理服务器认证
        if (selectedProxy.type() != Proxy.Type.HTTP) {
          throw new ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy");
        }
        // fall-through
      case HTTP_UNAUTHORIZED: //401 没有身份认证
        return OkHeaders.processAuthHeader(client.getAuthenticator(), userResponse, selectedProxy);

      case HTTP_PERM_REDIRECT:// 308
      case HTTP_TEMP_REDIRECT: //307
        // "If the 307 or 308 status code is received in response to a request other than GET
        // or HEAD, the user agent MUST NOT automatically redirect the request"
        if (!userRequest.method().equals("GET") && !userRequest.method().equals("HEAD")) {
            return null;
        } //如果不是get和head 那么就不能自动转发
        // fall-through
      case HTTP_MULT_CHOICE: //300
      case HTTP_MOVED_PERM:// 301
      case HTTP_MOVED_TEMP://302
      case HTTP_SEE_OTHER: //303
        // Does the client allow redirects?
        if (!client.getFollowRedirects()) return null;//如果不允许重定向

        String location = userResponse.header("Location");//从response的头部获得的location
        if (location == null) return null;
        HttpUrl url = userRequest.httpUrl().resolve(location);//使用request的解析location

        // Don't follow redirects to unsupported protocols.
        if (url == null) return null;

        // If configured, don't follow redirects between SSL and non-SSL.
        boolean sameScheme = url.scheme().equals(userRequest.httpUrl().scheme());
        if (!sameScheme && !client.getFollowSslRedirects()) return null;

        // Redirects don't include a request body.
        Request.Builder requestBuilder = userRequest.newBuilder();
        if (HttpMethod.permitsRequestBody(userRequest.method())) {
          requestBuilder.method("GET", null);
          requestBuilder.removeHeader("Transfer-Encoding");
          requestBuilder.removeHeader("Content-Length");
          requestBuilder.removeHeader("Content-Type");
        }

        // When redirecting across hosts, drop all authentication headers. This
        // is potentially annoying to the application layer since they have no
        // way to retain them.
        if (!sameConnection(url)) {
          requestBuilder.removeHeader("Authorization");
        }

        return requestBuilder.url(url).build();

      default:
        return null;
    }

 这一段就是根据回复的状态码生成重定向请求的代码逻辑。

  • HTTP_PROXY_AUTH 407 表示需要经过代理服务器认证 ,这时抛出异常,不进行重定向
  • HTTP_UNAUTHORIZED 401 身份未认证
  • HTTP_PERM_REDIRECT 308 HTTP_TEMP_REDIRECT 307 这两种状态码时,只有当请求的method不为GETHEAD时不进行重定向,否则按照下边一列状态码的方式处理
  • HTTP_MULT_CHOICE 300 HTTP_MOVED_PERM 301 HTTP_MOVED_TEMP 302 HTTP_SEE_OTHER 303 当是这些状态码时,先判断是否运行重定向,然后获得Response中的Location首部的值,然后用HttpUrl去解析,如果是host不同,那么去掉所有的认证首部,这是为了安全。

结语

 今天所总结的只是Http的重定向部分和OkHttp中的关于重定向的逻辑部分。之后会陆陆续续的继续总结关于Http的知识。
 本文也在我的独立博文中同时发布点我

版权声明:本文为博主原创文章,未经博主允许不得转载。

OkHttp自定义重试次数

Interceptors (主角)OkHttp自定义重试次数public class RetryInterceptor implements Interceptor { private sta...
  • CSDNno
  • CSDNno
  • 2017年05月20日 23:23
  • 1701

OkHttp完全解析(六)拦截器

拦截器是一种能够监控,重写,重试调用的强大机制。 调用chain.proceed(request)是每个拦截器实现的关键部分。这个看似简单的方法是所有HTTP 工作发生的地方, 在这里产...
  • OyangYujun
  • OyangYujun
  • 2015年11月25日 17:15
  • 21199

okHttp的简单运用以及cookie操作,302重定向死循环

为了完成学期实训,需要对新浪新闻网站进行读取,所以学习并封装了okhttp简单get和post的工具类。 本文参考了以下两篇文章: http://www.jcodecraeer.com/a/anz...
  • huage2580
  • huage2580
  • 2015年12月15日 15:08
  • 3181

okhttp http 重定向到https

package com.adups.wql.httpredirecttohttps; import android.util.Log; import com.squareup.okhttp.OkH...
  • wenql209
  • wenql209
  • 2017年02月20日 14:10
  • 712

浅析 OkHttp 拦截器之 RetryAndFollowUpInterceptor

概述 1 核心功能 2 活动图 失败重试 1 异常捕获 2 重试判断 3 死循环问题 继续请求 1 未授权 2 重定向 3 超时 QA1 概述除了用户自定义的拦截器(比如打打日志),该拦截器处于链条的...
  • firefile
  • firefile
  • 2017年07月19日 10:44
  • 216

OKhttp源码解析---拦截器之RetryAndFollowUpInterceptor

我们看下它的intercept public Response intercept(Chain chain) throws IOException { Request request = c...
  • new_abc
  • new_abc
  • 2016年11月02日 16:22
  • 1574

java.net.ProtocolException:Too many follow-up requests:21

这几天在android 上用jsoup爬取网页数据,先使用Okhttp获取到目标网页html,然后使用jsoup解析网页,结果昨天还好好地,今天早上就出了问题,说是java.net.ProtocolE...
  • qingdaohaoyunpeng
  • qingdaohaoyunpeng
  • 2016年03月17日 10:46
  • 3384

如何使用OkHttp/Retrofit重试HTTP请求

我在我的Android项目中使用Retrofit /OkHttp(1.6)。 我没有找到任何请求重试机制内置。在搜索更多,我看到OkHttp似乎有silent-retries。我没有看到发生在任...
  • xqkillua
  • xqkillua
  • 2017年09月21日 09:53
  • 281

okhttp之自定义Interceptor:请求失败切换IP重试拦截器

经过一段时间的挣扎,终于把新公司项目的网络框架换成了retrofit,由于项目是有失败重试和重定向需求的,所以需要在新的网络框架上增加这个功能,大家都知道retrofit的网络请求部分是基于okhtt...
  • gengqiquan
  • gengqiquan
  • 2016年08月13日 17:12
  • 8877

OKhttp 302 死循环

今天同事发给我这张图 这是因为okhttp中重定向次数超过21次以后就会报错。 访问一个接口的时候为什么会重定向,我用finder抓包发现一切正常,是不是因为cookie,于是我把浏览器cooki...
  • qq_22706515
  • qq_22706515
  • 2017年11月29日 16:13
  • 126
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:OkHttp解析系列-重定向和出错重试
举报原因:
原因补充:

(最多只允许输入30个字)