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

来源:互联网 发布:新闻联播配音软件 编辑:程序博客网 时间:2024/05/19 09:37

我们看下它的intercept

public Response intercept(Chain chain) throws IOException {    Request request = chain.request();    streamAllocation = new StreamAllocation(        client.connectionPool(), createAddress(request.url()));    int followUpCount = 0;    Response priorResponse = null;    while (true) {      if (canceled) {        streamAllocation.release();        throw new IOException("Canceled");      }      Response response = null;      boolean releaseConnection = true;      try {        response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);        releaseConnection = false;      } catch (RouteException e) {        // The attempt to connect via a route failed. The request will not have been sent.        if (!recover(e.getLastConnectException(), true, request)) throw e.getLastConnectException();        releaseConnection = false;        continue;      } catch (IOException e) {        // An attempt to communicate with a server failed. The request may have been sent.        if (!recover(e, false, request)) throw e;        releaseConnection = false;        continue;      } finally {        // We're throwing an unchecked exception. Release any resources.        if (releaseConnection) {          streamAllocation.streamFailed(null);          streamAllocation.release();        }      }      // Attach the prior response if it exists. Such responses never have a body.      if (priorResponse != null) {        response = response.newBuilder()            .priorResponse(priorResponse.newBuilder()                .body(null)                .build())            .build();      }      Request followUp = followUpRequest(response);      if (followUp == null) {        if (!forWebSocket) {          streamAllocation.release();        }        return response;      }      closeQuietly(response.body());      if (++followUpCount > MAX_FOLLOW_UPS) {        streamAllocation.release();        throw new ProtocolException("Too many follow-up requests: " + followUpCount);      }      if (followUp.body() instanceof UnrepeatableRequestBody) {        throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());      }      if (!sameConnection(response, followUp.url())) {        streamAllocation.release();        streamAllocation = new StreamAllocation(            client.connectionPool(), createAddress(followUp.url()));      } else if (streamAllocation.stream() != null) {        throw new IllegalStateException("Closing the body of " + response            + " didn't close its backing stream. Bad interceptor?");      }      request = followUp;      priorResponse = response;    }  }

这里首先创建了一个StreamAllocation,StreamAllocation是用来做连接分配的,传递的参数有两个,一个是前面创建的连接池,另外一个是调用createAddress创建的Address

private Address createAddress(HttpUrl url) {    SSLSocketFactory sslSocketFactory = null;    HostnameVerifier hostnameVerifier = null;    CertificatePinner certificatePinner = null;    if (url.isHttps()) {      sslSocketFactory = client.sslSocketFactory();      hostnameVerifier = client.hostnameVerifier();      certificatePinner = client.certificatePinner();    }    return new Address(url.host(), url.port(), client.dns(), client.socketFactory(),        sslSocketFactory, hostnameVerifier, certificatePinner, client.proxyAuthenticator(),        client.proxy(), client.protocols(), client.connectionSpecs(), client.proxySelector());  }

 public Address(String uriHost, int uriPort, Dns dns, SocketFactory socketFactory,      SSLSocketFactory sslSocketFactory, HostnameVerifier hostnameVerifier,      CertificatePinner certificatePinner, Authenticator proxyAuthenticator, Proxy proxy,      List<Protocol> protocols, List<ConnectionSpec> connectionSpecs, ProxySelector proxySelector) {    this.url = new HttpUrl.Builder()        .scheme(sslSocketFactory != null ? "https" : "http")        .host(uriHost)        .port(uriPort)        .build();    if (dns == null) throw new NullPointerException("dns == null");    this.dns = dns;    if (socketFactory == null) throw new NullPointerException("socketFactory == null");    this.socketFactory = socketFactory;    if (proxyAuthenticator == null) {      throw new NullPointerException("proxyAuthenticator == null");    }    this.proxyAuthenticator = proxyAuthenticator;    if (protocols == null) throw new NullPointerException("protocols == null");    this.protocols = Util.immutableList(protocols);    if (connectionSpecs == null) throw new NullPointerException("connectionSpecs == null");    this.connectionSpecs = Util.immutableList(connectionSpecs);    if (proxySelector == null) throw new NullPointerException("proxySelector == null");    this.proxySelector = proxySelector;    this.proxy = proxy;    this.sslSocketFactory = sslSocketFactory;    this.hostnameVerifier = hostnameVerifier;    this.certificatePinner = certificatePinner;  }
更加client和请求的相关信息初始化了 Address

再看下 StreamAllocation的创建

  public StreamAllocation(ConnectionPool connectionPool, Address address) {    this.connectionPool = connectionPool;    this.address = address;    this.routeSelector = new RouteSelector(address, routeDatabase());  }
这里保存了前面传过来的连接池和地址,并创建了一个RouteSelector,并进行了路由的一个选择

回到intercept,进入while循环

1、首先查看请求是否已经取消

2、调用RealInterceptorChain的proceed处理这个请求并把刚创建的StreamAllocation传递进去

3、如果前面第二步没有出现异常,则说明请求完成,设置releaseConnection为false,出现异常则将releaseConnection置为true,并释放前面创建的StreamAllocation

4、priorResponse不为空,则说明前面已经获取到了响应,这里会结合当前获取的Response和先前的Response

5、调用followUpRequest查看响应是否需要重定向,如果不需要重定向则返回当前请求

6、重定向次数+1,并且判断StreamAllocation是否需要重新创建

7、重新设置request,并把当前的Response保存到priorResponse,继续while循环


我们看下对是否需要重定向的判断followUpRequest

private Request followUpRequest(Response userResponse) throws IOException {    if (userResponse == null) throw new IllegalStateException();    Connection connection = streamAllocation.connection();    Route route = connection != null        ? connection.route()        : null;    int responseCode = userResponse.code();    final String method = userResponse.request().method();    switch (responseCode) {      case HTTP_PROXY_AUTH:        Proxy selectedProxy = route != null            ? route.proxy()            : client.proxy();        if (selectedProxy.type() != Proxy.Type.HTTP) {          throw new ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy");        }        return client.proxyAuthenticator().authenticate(route, userResponse);      case HTTP_UNAUTHORIZED:        return client.authenticator().authenticate(route, userResponse);      case HTTP_PERM_REDIRECT:      case HTTP_TEMP_REDIRECT:        // "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 (!method.equals("GET") && !method.equals("HEAD")) {          return null;        }        // fall-through      case HTTP_MULT_CHOICE:      case HTTP_MOVED_PERM:      case HTTP_MOVED_TEMP:      case HTTP_SEE_OTHER:        // Does the client allow redirects?        if (!client.followRedirects()) return null;        String location = userResponse.header("Location");        if (location == null) return null;        HttpUrl url = userResponse.request().url().resolve(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(userResponse.request().url().scheme());        if (!sameScheme && !client.followSslRedirects()) return null;        // Redirects don't include a request body.        Request.Builder requestBuilder = userResponse.request().newBuilder();        if (HttpMethod.permitsRequestBody(method)) {          if (HttpMethod.redirectsToGet(method)) {            requestBuilder.method("GET", null);          } else {            requestBuilder.method(method, 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(userResponse, url)) {          requestBuilder.removeHeader("Authorization");        }        return requestBuilder.url(url).build();      case 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 (userResponse.request().body() instanceof UnrepeatableRequestBody) {          return null;        }        return userResponse.request();      default:        return null;    }  }
这里主要是根据响应码,查看是否需要重定向,并重新设置请求

这样RetryAndFollowUpInterceptor拦截器就分析完了,下一个拦截器的启动是通过调用RealInterceptorChain的proceed

public Response proceed(Request request, StreamAllocation streamAllocation, HttpStream httpStream,      Connection connection) throws IOException {    if (index >= interceptors.size()) throw new AssertionError();    calls++;    // If we already have a stream, confirm that the incoming request will use it.    if (this.httpStream != null && !sameConnection(request.url())) {      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)          + " must retain the same host and port");    }    // If we already have a stream, confirm that this is the only call to chain.proceed().    if (this.httpStream != null && calls > 1) {      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)          + " must call proceed() exactly once");    }    // Call the next interceptor in the chain.    RealInterceptorChain next = new RealInterceptorChain(        interceptors, streamAllocation, httpStream, connection, index + 1, request);    Interceptor interceptor = interceptors.get(index);    Response response = interceptor.intercept(next);    // Confirm that the next interceptor made its required call to chain.proceed().    if (httpStream != null && index + 1 < interceptors.size() && next.calls != 1) {      throw new IllegalStateException("network interceptor " + interceptor          + " must call proceed() exactly once");    }    // Confirm that the intercepted response isn't null.    if (response == null) {      throw new NullPointerException("interceptor " + interceptor + " returned null");    }    return response;  }
这里index为1,创建的RealInterceptorChain的index为2,获取到的拦截器是BridgeInterceptor,下一篇我们分析它的intercept方法


0 0
原创粉丝点击