五大拦截器
一、重定向与重试拦截器




catch (IOException e) {
// todo 请求发出去了,但是和服务器通信失败了。(socket 流正在读/写数据时断开连接)
// HTTP/2 才会抛出 ConnectionShutdownException,所以对于 HTTP/1,requestSendStarted 一定是 true
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, streamAllocation, requestSendStarted, request)) {
throw e;
}
releaseConnection = false;
continue;
}
两个异常都是根据 recover 方法判断是否能够进行重试,如果返回 true ,则表示允许重试。
private boolean recover(IOException e,
StreamAllocation streamAllocation,
boolean requestSendStarted,
Request userRequest) {
streamAllocation.streamFailed(e);
// todo 1. 如果在 OkHttpClient 配置中关闭了重试(默认允许),则失败后不再重试
if (!client.retryOnConnectionFailure()) return false;
// todo 2. 如果是 RouteException 不用看这个条件;
// 如果是 IOException,且请求体不可重复,则不能重试
if (requestSendStarted && userRequest.body() instanceof UnrepeatableRequestBody) {
return false;
}
// todo 3. 判断异常是否属于可重试的异常
if (!isRecoverable(e, requestSendStarted)) return false;
// todo 4. 是否还有可用的路由路径
if (!streamAllocation.hasMoreRoutes()) return false;
// 失败恢复时,使用相同的路由选择器并建立新连接
return true;
}

1、协议异常,如果是那么直接判定不能重试;(你的请求或者服务器的响应本身就存在问题,没有按照http协议来 定义数据,再重试也没用)
2、超时异常,可能由于网络波动造成了Socket连接的超时,可以使用不同路线重试。
3、SSL证书异常/SSL****验证失败异常,前者是证书验证失败,后者可能就是压根就没证书,或者证书数据不正确, 那还怎么重试?
经过了异常的判定之后,如果仍然允许进行重试,就会再检查当前有没有可用路由路线来进行连接。简单来说,比 如 DNS 对域名解析后可能会返回多个 IP,在一个IP失败后,尝试另一个IP进行重试。

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) {
// 407 代理认证
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);
}
// 401 服务器认证
case HTTP_UNAUTHORIZED:
return client.authenticator().authenticate(route, userResponse);
// 308/307 保持方法
case HTTP_PERM_REDIRECT:
case HTTP_TEMP_REDIRECT: {
if (!method.equals("GET") && !method.equals("HEAD")) return null;
// fall-through 到重定向处理
}
// 300/301/302/303 各类重定向
case HTTP_MULT_CHOICE:
case HTTP_MOVED_PERM:
case HTTP_MOVED_TEMP:
case HTTP_SEE_OTHER: {
if (!client.followRedirects()) return null;
String location = userResponse.header("Location");
if (location == null) return null;
HttpUrl url = userResponse.request().url().resolve(location);
if (url == null) return null;
// 跨 Scheme 的重定向是否允许
boolean sameScheme = url.scheme().equals(userResponse.request().url().scheme());
if (!sameScheme && !client.followSslRedirects()) return null;
Request.Builder requestBuilder = userResponse.request().newBuilder();
// 是否保留请求体
if (HttpMethod.permitsRequestBody(method)) {
final boolean maintainBody = HttpMethod.redirectsWithBody(method);
if (HttpMethod.redirectsToGet(method)) {
requestBuilder.method("GET", null);
} else {
RequestBody requestBody = maintainBody ? userResponse.request().body() : null;
requestBuilder.method(method, requestBody);
}
if (!maintainBody) {
requestBuilder.removeHeader("Transfer-Encoding");
requestBuilder.removeHeader("Content-Length");
requestBuilder.removeHeader("Content-Type");
}
}
// 跨主机时移除认证头
if (!sameConnection(userResponse, url)) {
requestBuilder.removeHeader("Authorization");
}
return requestBuilder.url(url).build();
}
// 408 客户端超时
case HTTP_CLIENT_TIMEOUT: {
if (!client.retryOnConnectionFailure()) return null;
if (userResponse.request().body() instanceof UnrepeatableRequestBody) return null;
// 如果前一个响应也是 408,则不再重试
if (userResponse.priorResponse() != null
&& userResponse.priorResponse().code() == HTTP_CLIENT_TIMEOUT) {
return null;
}
// Respect Retry-After
if (retryAfter(userResponse, 0) > 0) return null;
return userResponse.request();
}
// 503 服务不可用
case HTTP_UNAVAILABLE: {
if (userResponse.priorResponse() != null
&& userResponse.priorResponse().code() == HTTP_UNAVAILABLE) {
return null;
}
if (retryAfter(userResponse, Integer.MAX_VALUE) == 0) {
return userResponse.request();
}
return null;
}
default:
return null;
}
}
private boolean sameConnection(Response response, HttpUrl url) {
HttpUrl priorUrl = response.request().url();
return priorUrl.host().equals(url.host())
&& priorUrl.port() == url.port()
&& priorUrl.scheme().equals(url.scheme());
}
private int retryAfter(Response response, int defaultDelaySeconds) {
String header = response.header("Retry-After");
if (header == null) return defaultDelaySeconds;
// 仅支持秒的简单实现;非法值按很大值处理表示不应立刻重试
try {
return Integer.parseInt(header.trim());
} catch (NumberFormatException ignore) {
return Integer.MAX_VALUE;
}
}


二、桥接拦截器
BridgeInterceptor ,连接应用程序和服务器的桥梁,我们发出的请求将会经过它的处理才能发给服务器,比如设 置请求内容长度,编码,gzip压缩,cookie等,获取响应后保存Cookie等操作。这个拦截器相对比较简单。
补全请求头:

三、缓存拦截器

3、交给下一个责任链继续处理
4、后续工作,返回304则用缓存的响应;否则使用网络响应并缓存本次响应(只缓存Get请求的响应)
缓存拦截器的工作说起来比较简单,但是具体的实现,需要处理的内容很多。在缓存拦截器中判断是否可以使用缓 存,或是请求服务器都是通过 CacheStrategy 判断。
缓存策略



缓存检测


四、连接拦截器






五、请求服务器拦截器




六、OkHttp****总结

