上一篇文章我们分析了 OkHttpClient 创建请求,以及相关队列操作的一些方法。具体可查看
探究Okhttp的运行原理(1)
此篇文章我们继续来看 OkHttpClient 另外一个重要的流程getResponseWithInterceptorChain() 方法去获取请求响应的。
getResponseWithInterceptorChain 方法
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);// RetryAndFollowUpInterceptor 拦截器
interceptors.add(new BridgeInterceptor(client.cookieJar())); // BridgeInterceptor 拦截器
interceptors.add(new CacheInterceptor(client.internalCache())); // CacheInterceptor 拦截器
interceptors.add(new ConnectInterceptor(client)); // ConnectInterceptor 拦截器
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket)); // CallServerInterceptor 拦截器
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis()); // 初始化 RealInterceptorChain 内部变量
return chain.proceed(originalRequest); // 执行
}
getResponseWithInterceptorChain 加入了五个主要的拦截器,拦截器即拦截请求,做一些对应的处理;
了解拦截器工作原理之前,首先看下 RealInterceptorChain 类,Okhttp 通过 RealInterceptorChain 使用责任链模式处理下发的请求;
RealInterceptorChain 类内部维护了 Request 请求、interceptors 拦截器容器、当前拦截器(初始为0);
RealInterceptorChain
public final class RealInterceptorChain implements Interceptor.Chain {
private final List<Interceptor> interceptors;
private final StreamAllocation streamAllocation;
private final HttpCodec httpCodec;
private final RealConnection connection;
private final int index;
private final Request request;
private final Call call;
private final EventListener eventListener;
private final int connectTimeout;
private final int readTimeout;
private final int writeTimeout;
private int calls;
public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
HttpCodec httpCodec, RealConnection connection, int index, Request request, Call call,
EventListener eventListener, int connectTimeout, int readTimeout, int writeTimeout) {
this.interceptors = interceptors;
this.connection = connection;
this.streamAllocation = streamAllocation;
this.httpCodec = httpCodec;
this.index = index;
this.request = request;
this.call = call;
this.eventListener = eventListener;
this.connectTimeout = connectTimeout;
this.readTimeout = readTimeout;
this.writeTimeout = writeTimeout;
}
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
......
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout); // 创建下一个RealInterceptorChain
Interceptor interceptor = interceptors.get(index);// 获取到 index 的拦截器
Response response = interceptor.intercept(next); // 执行拦截器的 intercept
......
return response;
}
拦截器调用 proceed 方法主要做了一下操作:
1 、创建下一个拦截器(传参为 index + 1);
2、 获取当前 index 值(初始为 0)的拦截器,第一个即为 RetryAndFollowUpInterceptor 拦截器
3、执行 RetryAndFollowUpInterceptor 的 intercept 方法;
intercept 方法具有传递效果(即链式传递给下一个拦截器),在 RetryAndFollowUpInterceptor 内部,会再次调用 RealInterceptorChain (即传入的 next 参数)的 proceed 方法,index 会继续自增,拿到下一个拦截器,从而完成责任传递效果;
直到最终返回结果的处理,过程如下图所示:
接下来,分析第一个拦截器的 intercept 方法 RetryAndFollowUpInterceptor
RetryAndFollowUpInterceptor 请求重定向拦截器
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request(); // 获取请求
RealInterceptorChain realChain = (RealInterceptorChain) chain;// 获取拦截器
Call call = realChain.call(); // 获取 RealCall 对象
EventListener eventListener = realChain.eventListener();// 获取监听事件
StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(request.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation; // 创建 streamAllocation 实例
int followUpCount = 0;
Response priorResponse = null;
while (true) {
if (canceled) { // 请求取消 释放资源
streamAllocation.release();
throw new IOException("Canceled");
}
Response response;
boolean releaseConnection = true;
try {
response = realChain.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(), streamAllocation, false, request)) {
throw e.getLastConnectException();
}
releaseConnection = false;
continue;
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, streamAllocation, requestSendStarted, 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, streamAllocation.route()); // 如果地址被重定向,重新组装重定向的请求
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) {
streamAllocation.release();
throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
}
if (!sameConnection(response, followUp.url())) { // 不是同一个连接,重定向操作 重新创建 StreamAllocation 实例
streamAllocation.release();
streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(followUp.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
} else if (streamAllocation.codec() != null) {
throw new IllegalStateException("Closing the body of " + response
+ " didn't close its backing stream. Bad interceptor?");
}
request = followUp;
priorResponse = response;
}
}
RetryAndFollowUpInterceptor 拦截器主要去处理请求的重定向相关操作,拦截器先创建了 StreamAllocation 实例对象,而后通过调用 followUpRequest 方法去查看相应返回 response 数据,判断是否需要进行重定向;
private Request followUpRequest(Response userResponse, Route route) throws IOException {
if (userResponse == null) throw new IllegalStateException();
int responseCode = userResponse.code();
final String method = userResponse.request().method();
switch (responseCode) {
......
case HTTP_MULT_CHOICE: // 300
case HTTP_MOVED_PERM:// 301
case HTTP_MOVED_TEMP:// 302
case HTTP_SEE_OTHER: // 302
// 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;
// Most redirects don't include a request body.
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");
}
}
// 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();
......
}
}
当返回码 300、301、302、303 时代表请求需要重定向,此时重新构建 Request 请求并进行返回;
当 RetryAndFollowUpInterceptor 拦截器发现请求需要重定向的时候。即 followUpRequest 返回的 Requset 不为空的时候,会重新创建 StreamAllocation 实例对象;
这里分析下 StreamAllocation 对象,后续的拦截器会使用到;
StreamAllocation
实例方法
new StreamAllocation(client.connectionPool(),createAddress(request.url()), call, eventListener, callStackTrace);
client.connectionPool() ------主要维护 RealConnection 类的队列;
createAddress ------- 维护请求地址的相关信息
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()); // 包括 host 、port 、dns 等数据
}
call ------ RealCall 实例
eventListener ------ 监听实例
callStackTrace ------ 记录
看下 StreamAllocation 类;
public final class StreamAllocation {
public final Address address;
private RouteSelector.Selection routeSelection;
private Route route;
private final ConnectionPool connectionPool;
public final Call call;
public final EventListener eventListener;
private final Object callStackTrace;
// State guarded by connectionPool.
private final RouteSelector routeSelector;
private int refusedStreamCount;
private RealConnection connection;
private boolean reportedAcquired;
private boolean released;
private boolean canceled;
private HttpCodec codec;
public StreamAllocation(ConnectionPool connectionPool, Address address, Call call,
EventListener eventListener, Object callStackTrace) {
this.connectionPool = connectionPool;
this.address = address;
this.call = call;
this.eventListener = eventListener;
this.routeSelector = new RouteSelector(address, routeDatabase(), call, eventListener);
this.callStackTrace = callStackTrace;
}
}
StreamAllocation 主要维护了上述变量的相关方法,这里先存储着,等到后续拦截器进行使用;
RetryAndFollowUpInterceptor 的拦截器到这里就分析完了,总结 RetryAndFollowUpInterceptor 主要做了以下事情:
1、 创建 StreamAllocation 实例进行保存,内部维护了 RealConnection 类的队列池,同时保存请求相关的 Host、Port、DNS 等信息供后续拦截器使用;
2、 对请求返回的重定向进行重新创建 Request 、StreamAllocation ;