本次源码解读基于OkHttp3.9.1,要解读源码,先从使用实例开始,下面是OK上的一个基本用例:
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
try (ResponseBody responseBody = response.body()) {
if (response.isSuccessful()) {
System.out.println(responseBody.string());
}
}
}
});
}
从上述基本使用上,可以看到,要使用OK,必须先new一个OkHttpClient对象出来,然后构造一个Request对象,
最后调用client的newCall方法,创建一个RealCal对象,RealCal对象可以直接执行同步或者异步的网络请求 execute/enqueue;
如此,就找到OkHttpClient的构造函数,看看里面具体做了什么:
public OkHttpClient() {
this(new Builder());
}
public Builder() {
dispatcher = new Dispatcher();//请求管理调度程序
protocols = DEFAULT_PROTOCOLS;//http协议,包含了http/1.1以及http2.0(可自由配置支持的协议版本(在服务器支持的前提下))
connectionSpecs = DEFAULT_CONNECTION_SPECS;//支持的连接规范(TLS版本、http未加密连接)
eventListenerFactory = EventListener.factory(EventListener.NONE);//事件监听函数
proxySelector = ProxySelector.getDefault();//系统默认代理管理类
cookieJar = CookieJar.NO_COOKIES;//空实现的cookie管理工具(如需管理cookie,需要执行实现该cookie管理类)
socketFactory = SocketFactory.getDefault();//Socket创建工厂
hostnameVerifier = OkHostnameVerifier.INSTANCE;//域名验证器
certificatePinner = CertificatePinner.DEFAULT;//自定义补充证书锁定验证平台
proxyAuthenticator = Authenticator.NONE;//代理基础认证(www-Authenticate)
authenticator = Authenticator.NONE;//服务器基础认证
connectionPool = new ConnectionPool();//连接池(含自动清理功能)
dns = Dns.SYSTEM;//DNS解析器
followSslRedirects = true;//遵循SSL重定向
followRedirects = true;//遵循重定向
retryOnConnectionFailure = true;//重试连接错误
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;//ping时间间隔
}
这里有几个对象要解析下:
protocols:该对象配置支持的http协议版本,优先适配高版本(在服务器支持的前提下)
eventListenerFactory:该监听函数,默认是空实现,使用者可以自定义实现,以接受详尽的事件流程;
cookieJar:cookie管理工具,有个默认的空实现,如需保存cookie,需自行实现保存接口;
下面对一些重要对象稍作介绍:
Dispatcher,该对象主要负责管理和调度连接,我们看下该类的关键代码:
public final class Dispatcher {
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private @Nullable Runnable idleCallback;
/** Executes calls. Created lazily. */
private @Nullable ExecutorService executorService;
/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
public Dispatcher() {
}
这里解析下:
maxRequests:表示最大的连接数量;
maxRequestsPerHost:表示同一个域名,支持最大的连接数为5;
idleCallback:任务执行状态监听;
executorService:线程池;
readyAsyncCalls:异步请求队列(等待中的);
runningAsyncCalls:异步请求队列(请求中的);
runningSyncCalls:同步请求队列(请求中的);
任务调度器的最大并行任务数以及但域名最大请求连接数都可以自定义,这里采用双端队列来处理等待中与进行中的队列,我们看源码:
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
这是一个线程池,没有限制线程数量上限,但在加入线程池的函数中有判断(线程池用来执行异步请求任务):
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
可以看到,会对线程的最大数量,与同一域名最大请求连接数做限制,这里会判断是否直接加入请求队列还是等待队列,每一个请求执行完毕,
会调用promoteCalls方法,来顺序执行等待队列中的请求:
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
该方法把请求Call移出等待队列,移入请求队列;
至此,调度器Dispatcher的源码就介绍完了。
接下来,关于cookieJar,在这里,要想使用cookie管理功能,必须自定义CookieJar:
new CookieJar() {
@Override public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
}
@Override public List<Cookie> loadForRequest(HttpUrl url) {
return Collections.emptyList();
}
};
在cookieJar中,管理对应的URL的cookie的存取问题
socketFactory,在创建连接的时候会用来创建一个未连接的套接字,本章后续会讲到;
在这里需要提到sslSocketFactory,该SSL套接字工厂,只在okhttp默认构造函数中会有一个默认实现
OkHostnameVerifier.INSTANCE:okhttp中提供的一个默认的域名与SSLSession验证类(也可以自定义)
certificatePinner:除了主平台的证书认证之外,okhttp提供了一套额外的域名证书锁定机制(在同一证书授予多平
台的情况下(被攻击))使用方式如下:
new CertificatePinner.Builder().add("tdw.com", "sha1/...")
.add("tuandai.com","sha256/...").build();
authenticator、proxyAuthenticator:http协议中基本认证(前者服务器认证、后者代理服务器认证),详见http基本认证
关于okhttpClient的一些配置信息就介绍到这里(这里先不介绍拦截器链)
OkHttpClient中构造函数的一些初始化配置已经介绍完毕;
根据使用案例,接下来会创建一个Request:
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tag = builder.tag != null ? builder.tag : this;
}
可以看到,Request信息比较简单,只有url、method、headers(该heads从代码逻辑可知只针对该请求)、RequestBody;
其中RequestBody中保存了需要发送的信息,在连接建立之后,会调用RequestBody的writeTo(BufferedSink sink)方法
发送数据流(打开的连接会传入BufferedSink),后续会详细讲到;
在OkhttpClient调用newCall方法后会根据传递进来的request创建一个RealCall对象;
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
这是RealCall的构造函数,其中创建了一个RetryAndFollowUpInterceptor对象是拦截器队列之一,后续将详细讲解;
在该realCall对象中,就可以直接执行网络请求方法了,我们首先来看看同步请求:
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;//不可重复开启请求同一个Request
}
captureCallStackTrace();
eventListener.callStart(this);//请求开始
try {
client.dispatcher().executed(this);//把同步请求添加到队列管理器
Response result = getResponseWithInterceptorChain();//获取响应体
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);//失败回调
throw e;
} finally {
client.dispatcher().finished(this);//通知请求队列管理器,请求执行完毕
}
}
对于client.dispatcher().executed(this)、client.dispatcher().finished(this),是同步请求任务到管理队列,具体实现可以查看源码;
可以看到,获取响应的关键代码就在于getResponseWithInterceptorChain()了,接下来继续看看异步请求:
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));//把异步请求任务添加到请求管理队列
}
这里有一个区别就是,同步任务是直接执行的,异步任务,需要任务调度器管理任务的执行时间,具体的调度机制,在上面已经介绍过,这里看看
AsyncCall这个类,
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
...
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
以及它的父类NamedRunnable
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = Util.format(format, args);
}
@Override public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
protected abstract void execute();
}
有两点需要注意:可以看到在AsyncCall中已经重命名线程名称了,以便于信息查看,在AsyncCall类的execute中执行了网络请求,这里也可以看到,
跟同步请求一致,响应体同样是通过getResponseWithInterceptorChain()方法获取的;
至此,我们已经找到整个OK网络请求的一个节点了,接下来将详细分析该方法的具体实现;
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {//非websocket请求才会调用并执行这层拦截器
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
可以说这个方法中已经集中了整个OK框架的精髓所在了,通过一系列的拦截器,完成了报文拼装、连接建立、报文发送与获取、报文解析、错误处理
等一些列的链式操作,是责任链模式的很好的实例;接下来,只要分析完成这一些列的责任链,也就把OK框架也分析的差不多了,下面进入正题:
如果用户有通过new OkHttpClient.Builder().addInterceptor()方法添加拦截器,则预先执行的将是用户添加的拦截器,不过需要注意的是,此时,OK自有的拦截器还没有开始对请求进行处理(包涵OK自行对报文请求头部的一些封装等)
好了,这里开始解析RealInterceptorChain对象,该对象,在后续的代码中我们可以看到,会被重复的创建,并把拦截链中处理好的信息一步一步塞进去,最后再把自己传递给下一个拦截器,完善请求所需的信息后,由CallServerInterceptor最终发起请求,下面就看看RealInterceptorChain的proceed方法:
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection 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.httpCodec != null && !this.connection.supportsUrl(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.httpCodec != 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, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
if (httpCodec != 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");
}
if (response.body() == null) {
throw new IllegalStateException(
"interceptor " + interceptor + " returned a response with no body");
}
return response;
}
可以看到,该方法中,除了一些必要的验证外,最主要做的事情就是创建一个新的实例,并传递给下一个拦截器,调用下一个拦截器的intercept方法;既然是按顺序执行
的拦截器链,就从第一个拦截器开始分析:retryAndFollowUpInterceptor
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Call call = realChain.call();
EventListener eventListener = realChain.eventListener();
streamAllocation = new StreamAllocation(client.connectionPool(), createAddress(request.url()),
call, eventListener, callStackTrace);//主要提供http编码器(http/1.1、http/2.0)
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(), false, request)) {
throw e.getLastConnectException();//检查是否具备重试条件,不具备则抛出异常(这里的RouteException异常主要包涵握手失败)
}
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, 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);//处理重定向、基础认证、超时后,返回的新的请求request或者null
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.release();//如果不可重用连接,关闭之,重新创建http编码器
streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(followUp.url()), call, eventListener, callStackTrace);
} 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拦截器主要处理了重定向、基础认证、超时的重试,这里有个关键的方法,那就是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;//请求方式,SSL重试请求
// 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();//返回重构后的请求request
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 (!client.retryOnConnectionFailure()) {
// The application layer has directed us not to retry the request.
return null;//不重试
}
if (userResponse.request().body() instanceof UnrepeatableRequestBody) {
return null;
}
if (userResponse.priorResponse() != null
&& userResponse.priorResponse().code() == HTTP_CLIENT_TIMEOUT) {
// We attempted to retry and got another timeout. Give up.
return null;前一个响应体有,且是超时,这次也是超时,则不再重试
}
return userResponse.request();//返回请求request
default:
return null;
}
}
支持retryAndFollowUpInterceptor拦截器我们就分析完毕了,这里有一个类StreamAllocation留待后续继续分析;
BridgeInterceptor拦截器,该拦截器主要作用就是拼装请求报文头部,由于功能非常简单,下面只做简单解析:
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
RequestBody body = userRequest.body();
if (body != null) {
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");//有使用传输编码,清除实体主体长度首部
}
}
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", hostHeader(userRequest.url(), false));增加Host
}
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive");//默认长连接
}
// If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
// the transfer stream.
boolean transparentGzip = false;
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true;
requestBuilder.header("Accept-Encoding", "gzip");//如果不指定可接受的内容编码格式,默认支持gzip内容编码
}
List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));//cookies管理
}
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", Version.userAgent());//用户标识
}
Response networkResponse = chain.proceed(requestBuilder.build());//获取响应体
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());//重置cookies
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
if (transparentGzip
&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse)) {
GzipSource responseBody = new GzipSource(networkResponse.body().source());
Headers strippedHeaders = networkResponse.headers().newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build();//删除响应内容编码头部及实体长度,因为已经在下一步解码了
responseBuilder.headers(strippedHeaders);
String contentType = networkResponse.header("Content-Type");
responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));//解码实体主体
}
return responseBuilder.build();//返回响应体给retryAndFollowUpInterceptor(处理重定向等问题)
}
BridgeInterceptor拦截器到这里就结束了,接下来分析CacheInterceptor拦截器:
@Override public Response intercept(Chain chain) throws IOException {
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;//获取已缓存的响应体
long now = System.currentTimeMillis();
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();//缓存策略(网络/本地缓存)
Request networkRequest = strategy.networkRequest;//网络请求体
Response cacheResponse = strategy.cacheResponse;//本地有效缓存(如果传递进去缓存策略的缓存已经无效(需要再验证的也视为有效),这里返回空)
if (cache != null) {
cache.trackResponse(strategy);//缓存命中计数
}
if (cacheCandidate != null && cacheResponse == null) {
closeQuietly(cacheCandidate.body());// 如果本地缓存无效,关闭本地缓存
}
// If we're forbidden from using the network and the cache is insufficient, fail.
if (networkRequest == null && cacheResponse == null) {
return new Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(504)
.message("Unsatisfiable Request (only-if-cached)")
.body(Util.EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();//如果请求头部中包涵only-if-chached头部(只读取本地缓存,会置networkrequest为空),并且本地没有有效缓存,则返回504响应体
}
// If we don't need the network, we're done.
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();//使用本地有效缓存返回
}
Response networkResponse = null;
try {
networkResponse = chain.proceed(networkRequest);//调用责任链,获取下一个响应体
} finally {
// If we're crashing on I/O or otherwise, don't leak the cache body.
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());//关闭本地缓存
}
}
// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) {
if (networkResponse.code() == HTTP_NOT_MODIFIED) {
Response response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers(), networkResponse.headers()))//整理头部
.sentRequestAtMillis(networkResponse.sentRequestAtMillis())//重置时间戳
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())//重置时间戳
.cacheResponse(stripBody(cacheResponse))//响应体加入空实体的缓存响应
.networkResponse(stripBody(networkResponse))//响应体加入空实体的网络响应
.build();//服务器返回304,表明本地缓存是有效的,可以继续使用本地缓存
networkResponse.body().close();//关闭网络响应体实体I/O
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
cache.trackConditionalCacheHit();//缓存命中计数
cache.update(cacheResponse, response);//更新缓存
return response;
} else {
closeQuietly(cacheResponse.body());
}
}
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();//构建网络响应体
if (cache != null) {
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
// Offer this request to the cache.
CacheRequest cacheRequest = cache.put(response);//如果有实体主体,并且状态是成功的,而且有缓存头部策略(同事满足响应和请求的缓存头部要求),则缓存
return cacheWritingResponse(cacheRequest, response);//重新返回一个新的响应体(重新封装I/O流,具体细节暂时未分析清楚)
}
if (HttpMethod.invalidatesCache(networkRequest.method())) {
try {
cache.remove(networkRequest);//如果是POST等请求,则忽略缓存头部,一律删除缓存
} catch (IOException ignored) {
// The cache cannot be written.
}
}
}
return response;
}
CacheInterceptor拦截器,至此就分析完毕,有些许未分析清除的细节,后续将补充,这里继续分析下一个拦截器ConnectInterceptor:
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
StreamAllocation streamAllocation = realChain.streamAllocation();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);//建立连接(如果是https,则已经握手成功)
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);//调用下一个个拦截器,处理报文的发送与接收
}
这个应该是拦截器里代码量最少的,但是做的事情却并不少,这里建立了一条连接,具体的关键代码就在StreamAllocation 的newStream方法中了,至于这个类
里面究竟做了什么,因为也是OK的核心代码所在,我们先留着,等待后续讲解;
接下来看看下一个拦截器CallServerInterceptor:
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
HttpCodec httpCodec = realChain.httpStream();
StreamAllocation streamAllocation = realChain.streamAllocation();
RealConnection connection = (RealConnection) realChain.connection();
Request request = realChain.request();
long sentRequestMillis = System.currentTimeMillis();
realChain.eventListener().requestHeadersStart(realChain.call());//监听函数
httpCodec.writeRequestHeaders(request);//写入报文头部到缓冲
realChain.eventListener().requestHeadersEnd(realChain.call(), request);
Response.Builder responseBuilder = null;
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
// If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
// Continue" response before transmitting the request body. If we don't get that, return
// what we did get (such as a 4xx response) without ever transmitting the request body.
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
httpCodec.flushRequest();//100-continue是一个特殊头部,详见http://www.cnblogs.com/tekkaman/archive/2013/04/03/2997781.html
realChain.eventListener().responseHeadersStart(realChain.call());//如果包含了这个头部,则会先发送报文头部,等待服务器响应
responseBuilder = httpCodec.readResponseHeaders(true);//读取服务器响应头部
}
//如果服务器支持100-continue头部,此处的responseBuilder为null,具体可以看上面的readResponseHeaders(true)源码
if (responseBuilder == null) {
// Write the request body if the "Expect: 100-continue" expectation was met.
realChain.eventListener().requestBodyStart(realChain.call());
long contentLength = request.body().contentLength();
CountingSink requestBodyOut =
new CountingSink(httpCodec.createRequestBody(request, contentLength));
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);//发送报文实体主体(不含任何头部,包括实体头部)
bufferedRequestBody.close();
realChain.eventListener()
.requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
} else if (!connection.isMultiplexed()) {
// If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
// from being reused. Otherwise we're still obligated to transmit the request body to
// leave the connection in a consistent state.
streamAllocation.noNewStreams();
}
}
httpCodec.finishRequest();//发送缓冲区的请求报文
if (responseBuilder == null) {
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(false);//获取响应首部
}
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())//保存握手信息
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
realChain.eventListener()
.responseHeadersEnd(realChain.call(), response);
int code = response.code();
if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
response = response.newBuilder()
.body(Util.EMPTY_RESPONSE)
.build();//网页请求特殊返回
} else {
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();//解析报文实体主体
}
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();//关闭长连接
}
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());//报文实体头部跟实体主体有冲突
}
return response;
}
至此,整个okhttp网络框架请求流程就结束了,在这里还有连接打开的那一部分源码,以及长连接的连接复用,http2的解码规则,等等,留待后续篇章继续解读