在同步和异步请求中都调用了getResponseWithInterceptorChain()
,它返回Response
getResponseWithInterceptorChain()
拦截器的入口。
将client中的拦截器和默认的拦截器都加入到集合中,将其传入创建了一个RealInterceptorChain拦截器链。最后返回chain的proceed()的返回值。
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
// interceptor(应用拦截器),networkInterceptor(网络)在client中初始化
interceptors.addAll(client.interceptors());
interceptors.add(new RetryAndFollowUpInterceptor(client));
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
originalRequest, this, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
boolean calledNoMoreExchanges = false;
try {
Response response = chain.proceed(originalRequest);
if (transmitter.isCanceled()) {
closeQuietly(response);
throw new IOException("Canceled");
}
return response;
} catch (IOException e) {
calledNoMoreExchanges = true;
throw transmitter.noMoreExchanges(e);
} finally {
if (!calledNoMoreExchanges) {
transmitter.noMoreExchanges(null);
}
}
}
拦截器队列的加入顺序:
request和response都经过了每一个拦截器
RealInterceptorChain
真正的拦截器链,里面的成员变量如下:
public final class RealInterceptorChain implements Interceptor.Chain {
private final List<Interceptor> interceptors; // 先前创建好的拦截器集合
private final Transmitter transmitter;
private final @Nullable Exchange exchange;
private final int index; // 当前拦截器在拦截器集合中的下标
private final Request request;
private final Call call;
private final int connectTimeout;
private final int readTimeout;
private final int writeTimeout;
private int calls;
}
RealInterceptorChain # proceed()
public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// 参数的一些判断
...
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
// 当前拦截器
Interceptor interceptor = interceptors.get(index);
// 调用intercept方法并传入下一个拦截器链
Response response = interceptor.intercept(next);
// 对下一个拦截器和response的一些判断
...
return response;
}
因为Interceptor是个接口,所以intercept()是一个抽象方法,先看第一个实现类RetryAndFollowUpInterceptor(它是OkHttp core的第一个拦截器)
调用步骤:
- RealInterceptorChain # proceed()
- RetryAndFollowUpInterceptor # intercept()
- RealInterceptorChain # proceed()
- BridgeInterceptor # intercept()
- …
RetryAndFollowUpInterceptor
RetryAndFollowUpInterceptor负责失败重试(处理一些连接异常)以及重定向
HTTP中的重定向
client向server发送一个请求,要求获取一个资源,server接收到这个请求后,发现请求的这个资源实际存放在另一个位置于是server在返回的response header的Location字段中写入那个请求资源的正确的URL,并设置reponse的状态码为30x,client接收到这个response后,发现状态码为重定向的状态吗,就会去解析到新的URL,根据新的URL重新发起请求。
请求转发
服务器在处理request的过程中将request先后委托多个servlet或jsp接替进行处理的过程,request和reponse始终在期间传递。
区别
- 重定向时,客户端发起两次请求,而请求转发时,客户端只发起一次请求
- 重定向后,浏览器地址栏url变成第二个url,而请求转发没有变(请求转发对于客户端是透明的)
重定向:
用户请求-----》服务器入口-------》组件------>服务器出口-------》用户----(重定向)—》新的请求
请求转发:
用户请求-----》服务器入口-------》组件1—(转发)----》组件2------->服务器出口-------》用户
RetryAndFollowUpInterceptor # intercept()
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Transmitter transmitter = realChain.transmitter();
int followUpCount = 0;
Response priorResponse = null;
while (true) {
// 准备创建一个流来承载{@code request}。如果存在连接,则优先使用现有连接。
transmitter.prepareToConnect(request);
if (transmitter.isCanceled()) {
throw new IOException("Canceled");
}
Response response;
boolean success = false;
try {
// 又回到了最初的realChain的proceed(),而proceed()中又调用intercept()传入下一个拦截器链
response = realChain.proceed(request, transmitter, null);
success = true;
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), transmitter, false, request)) {
throw e.getFirstConnectException();
}
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, transmitter, requestSendStarted, request)) throw e;
continue;
} finally {
// The network call threw an exception. Release any resources.
if (!success) {
// 释放资源
transmitter.exchangeDoneDueToException();
}
}
// Attach the prior response if it exists. Such responses never have a body.
// 如果之前已获得响应,其body为空
if (priorResponse != null) {
// 结合当前的response和之前的response获得新的response
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
// Exchange(数据交换层):传输单个http请求和响应对,Exchange 类可以将 ExchangeCodec 这个类的连接管理及事件进行分层,而 ExchangeCodec 是一个真正执行 I/O 的类
Exchange exchange = Internal.instance.exchange(response);
Route route = exchange != null ? exchange.connection().route() : null;
// 调用followUpRequest()查看响应是否需要重定向,不需要就返回当前请求,如果需要返回新的请求
Request followUp = followUpRequest(response, route);
// 不需要重定向或无法重定向
if (followUp == null) {
if (exchange != null && exchange.isDuplex()) {
transmitter.timeoutEarlyExit();
}
return response;
}
RequestBody followUpBody = followUp.body();
if (followUpBody != null && followUpBody.isOneShot()) {
return response;
}
closeQuietly(response.body());
if (transmitter.hasExchange()) {
exchange.detachWithViolence();
}
// 重试次数+1,如果超过MAX_FOLLOW_UPS(默认20),抛出异常
if (++followUpCount > MAX_FOLLOW_UPS) {
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
request = followUp;
// 把当前的Response保存到priorResponse
priorResponse = response;
}
}
Transmitter # prepareToConnect()
public void prepareToConnect(Request request) {
// 如果request不为空
if (this.request != null) {
// 判断是否是相同连接
if (sameConnection(this.request.url(), request.url()) && exchangeFinder.hasRouteToTry()) {
return; // Already ready.
}
if (exchange != null) throw new IllegalStateException();
// exchangeFinder用来寻找可用的connection
if (exchangeFinder != null) {
maybeReleaseConnection(null, true);
exchangeFinder = null;
}
}
this.request = request;
// 重点
this.exchangeFinder = new ExchangeFinder(this, connectionPool, createAddress(request.url()),
call, eventListener);
}
Transmitter # createAddress()
private Address createAddress(HttpUrl url) {
SSLSocketFactory sslSocketFactory = null;
HostnameVerifier hostnameVerifier = null;
CertificatePinner certificatePinner = null;
// 如果是https
if (url.isHttps()) {
sslSocketFactory = client.sslSocketFactory();
hostnameVerifier = client.hostnameVerifier();
certificatePinner = client.certificatePinner(); // 固定证书
}
// 初始化Address
return new Address(url.host(), url.port(), client.dns(), client.socketFactory(),
sslSocketFactory, hostnameVerifier, certificatePinner, client.proxyAuthenticator(),
client.proxy(), client.protocols(), client.connectionSpecs(), client.proxySelector());
}
ExchangeFinder
相当于之前版本的StreamAllocation
构造方法
ExchangeFinder(Transmitter transmitter, RealConnectionPool connectionPool,
Address address, Call call, EventListener eventListener) {
this.transmitter = transmitter;
this.connectionPool = connectionPool;
this.address = address;
this.call = call;
this.eventListener = eventListener;
// 路由选择器
this.routeSelector = new RouteSelector(
address, connectionPool.routeDatabase, call, eventListener);
}
RetryAndFollowUpInterceptor # recover()
private boolean recover(IOException e, Transmitter transmitter,
boolean requestSendStarted, Request userRequest) {
// The application layer has forbidden retries.
// 应用层禁止重试
if (!client.retryOnConnectionFailure()) return false;
// We can't send the request body again.
// 不能再发送请求体了
if (requestSendStarted && requestIsOneShot(e, userRequest)) return false;
// This exception is fatal.
// 这个异常无法重试
if (!isRecoverable(e, requestSendStarted)) return false;
// No more routes to attempt.
// 没有更多的路由
if (!transmitter.canRetry()) return false;
// For failure recovery, use the same route selector with a new connection.
// 上面条件都不满足就可以进行重试
return true;
}
RetryAndFollowUpInterceptor # followUpRequest()
查看响应是否需要重定向,不需要就返回当前请求,如果需要返回新的请求。
private Request followUpRequest(Response userResponse, @Nullable Route route) throws IOException {
if (userResponse == null) throw new IllegalStateException();
// 返回的响应码
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: // 永久重定向(所有将来的请求应该使用另一个URI重复)
case HTTP_TEMP_REDIRECT: // 临时重定向(后续的请求应仍使用原始的URI)
// "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"
// 如果接收到307或308状态码以响应除GET或HEAD以外的请求,则用户代理绝不能自动重定向请求
if (!method.equals("GET") && !method.equals("HEAD")) {
return null;
}
// fall-through
// 300多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择
case HTTP_MULT_CHOICE:
// 301永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。
case HTTP_MOVED_PERM:
// 302临时移动。与301类似。但资源只是临时被移动。
case HTTP_MOVED_TEMP:
// 303查看其它地址。与301类似。使用GET和POST请求查看
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;
// 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.request().url(), url)) {
requestBuilder.removeHeader("Authorization");
}
return requestBuilder.url(url).build();
// 408 超时
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;
}
RequestBody requestBody = userResponse.request().body();
if (requestBody != null && requestBody.isOneShot()) {
return null;
}
if (userResponse.priorResponse() != null
&& userResponse.priorResponse().code() == HTTP_CLIENT_TIMEOUT) {
// We attempted to retry and got another timeout. Give up.
return null;
}
if (retryAfter(userResponse, 0) > 0) {
return null;
}
return userResponse.request();
// 503 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中
case HTTP_UNAVAILABLE:
if (userResponse.priorResponse() != null
&& userResponse.priorResponse().code() == HTTP_UNAVAILABLE) {
// We attempted to retry and got another timeout. Give up.
return null;
}
if (retryAfter(userResponse, Integer.MAX_VALUE) == 0) {
// specifically received an instruction to retry without delay
return userResponse.request();
}
return null;
default:
return null;
}
}
流程图
BridgeInterceptor
它是一个连接桥,它负责把用户构造的请求转换为发送给服务器的请求,把服务器返回的响应转换为对用户友好的响应。转换的过程就是添加一些服务端需要的header
信息。在Request阶段配置用户信息,并添加一些请求头,看是否要填充Cookie。在Response阶段,进行gzip解压,添加响应头。
BridgeInterceptor # intercept()
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
RequestBody body = userRequest.body();
// 为request添加Content-Type(文档类型)、Content-Length(内容长度)或Transfer-Encoding(传输编码)
// (也就是说这些头信息不用我们手动添加,添加也会被覆盖)
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));
}
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive");
}
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", Version.userAgent());
}
// 默认支持gzip解压缩
// 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");
}
// 将userRequest中的cookie设置进builder
// loadForRequest()方法由开发者实现
List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));
}
// 调用下一个拦截器
Response networkResponse = chain.proceed(requestBuilder.build());
// 从response中解析出cookies,字符串变成List<Cookie>,交给cookiejar接口处理
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
// 若之前设置了gzip压缩且response中也包含了gzip压缩,则进行解压
if (transparentGzip
&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse)) {
GzipSource responseBody = new GzipSource(networkResponse.body().source());
// 对header进行一些处理
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();
}
HttpHeaders # receiveHeaders()
public static void receiveHeaders(CookieJar cookieJar, HttpUrl url, Headers headers) {
if (cookieJar == CookieJar.NO_COOKIES) return;
List<Cookie> cookies = Cookie.parseAll(url, headers);
if (cookies.isEmpty()) return;
// 解析出来的cookie交给cookieJar,让它决定怎么保存
cookieJar.saveFromResponse(url, cookies);
}
Cookie # parseAll()
从headers中解析出cookie
/** Returns all of the cookies from a set of HTTP response headers. */
public static List<Cookie> parseAll(HttpUrl url, Headers headers) {
// 可以解析headers中多个Set-Cookie字段
List<String> cookieStrings = headers.values("Set-Cookie");
List<Cookie> cookies = null;
for (int i = 0, size = cookieStrings.size(); i < size; i++) {
// parse()为核心方法
Cookie cookie = Cookie.parse(url, cookieStrings.get(i));
if (cookie == null) continue;
if (cookies == null) cookies = new ArrayList<>();
cookies.add(cookie);
}
return cookies != null
? Collections.unmodifiableList(cookies)
: Collections.emptyList();
}
CacheInterceptor
Okhttp是有自己的一套缓存机制的,CacheInterceptor就是用来负责读取缓存以及更新缓存的。
CacheInterceptor创建时传入的参数:
interceptors.add(new CacheInterceptor(client.internalCache()));
public CacheInterceptor(@Nullable InternalCache cache) {
this.cache = cache;
}
OkHttpClient # internalCache()
InternalCache internalCache() {
return cache != null ? cache.internalCache : internalCache;
}
在OkHttpClient.Builder中有设置,两个相抵消;若没有对Builder进行缓存设置,则两个都为null
/** Sets the response cache to be used to read and write cached responses. */
public Builder cache(@Nullable Cache cache) {
this.cache = cache;
this.internalCache = null;
return this;
}
CacheInterceptor # intercept()
@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();
// 如果为null表示不用网络请求
Request networkRequest = strategy.networkRequest;
// 如果为null表示不用缓存
Response cacheResponse = strategy.cacheResponse;
if (cache != null) {
// 记录请求次数、使用网络请求次数、使用缓存次数
cache.trackResponse(strategy);
}
// 缓存不可用,关闭
if (cacheCandidate != null && cacheResponse == null) {
closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
}
// If we're forbidden from using the network and the cache is insufficient, fail.
// 如果既无网络请求可用,也没有缓存可用,返回504错误
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();
}
// 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) {
// 响应码为304,请求的资源未修改,使用缓存,把两者信息合并
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();
networkResponse.body().close();
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
// 在合并头之后更新缓存,但是在剥离内容编码头之前(由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);
}
// 如果request无效
if (HttpMethod.invalidatesCache(networkRequest.method())) {
try {
// 从缓存删除
cache.remove(networkRequest);
} catch (IOException ignored) {
// The cache cannot be written.
}
}
}
return response;
}
CacheStrategy.Factory # Factory()
public Factory(long nowMillis, Request request, Response cacheResponse) {
this.nowMillis = nowMillis;
this.request = request;
this.cacheResponse = cacheResponse;
if (cacheResponse != null) {
this.sentRequestMillis = cacheResponse.sentRequestAtMillis();
this.receivedResponseMillis = cacheResponse.receivedResponseAtMillis();
Headers headers = cacheResponse.headers();
// 获取响应头的各种信息
for (int i = 0, size = headers.size(); i < size; i++) {
String fieldName = headers.name(i);
String value = headers.value(i);
if ("Date".equalsIgnoreCase(fieldName)) {
// 服务日期
servedDate = HttpDate.parse(value);
servedDateString = value;
} else if ("Expires".equalsIgnoreCase(fieldName)) {
// 失效日期
expires = HttpDate.parse(value);
} else if ("Last-Modified".equalsIgnoreCase(fieldName)) {
lastModified = HttpDate.parse(value);
lastModifiedString = value;
} else if ("ETag".equalsIgnoreCase(fieldName)) {
etag = value;
} else if ("Age".equalsIgnoreCase(fieldName)) {
// 到现在为止的时间
ageSeconds = HttpHeaders.parseSeconds(value, -1);
}
}
}
}
CacheStrategy.Factory # get()
/**
* Returns a strategy to satisfy {@code request} using the a cached response {@code response}.
*/
public CacheStrategy get() {
CacheStrategy candidate = getCandidate();
// 设置策略后的request不为null
if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) {
// We're forbidden from using the network and the cache is insufficient.
// 我们被禁止使用网络,缓存不足。
return new CacheStrategy(null, null);
}
return candidate;
}
CacheStrategy.Factory # getCandidate()
这个是决定最终缓存策略的方法
/** Returns a strategy to use assuming the request can use the network. */
private CacheStrategy getCandidate() {
// No cached response.
if (cacheResponse == null) {
return new CacheStrategy(request, null);
}
// Drop the cached response if it's missing a required handshake.
// 如果请求是https的并且响应没有握手,就重新请求
if (request.isHttps() && cacheResponse.handshake() == null) {
return new CacheStrategy(request, null);
}
// If this response shouldn't have been stored, it should never be used
// as a response source. This check should be redundant as long as the
// persistence store is well-behaved and the rules are constant.
// 如果response是不该做缓存的,就请求。
// isCacheable()内部是根据状态码判断的
if (!isCacheable(cacheResponse, request)) {
return new CacheStrategy(request, null);
}
// 如果请求指定不使用缓存或者是可选择的,就请求
CacheControl requestCaching = request.cacheControl();
if (requestCaching.noCache() || hasConditions(request)) {
return new CacheStrategy(request, null);
}
CacheControl responseCaching = cacheResponse.cacheControl();
// 计算当前age的时间戳:now - sent + age
long ageMillis = cacheResponseAge();
// 刷新时间,一般服务器设置为max-age
long freshMillis = computeFreshnessLifetime();
if (requestCaching.maxAgeSeconds() != -1) {
// 一般取max-age
freshMillis = Math.min(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds()));
}
long minFreshMillis = 0;
if (requestCaching.minFreshSeconds() != -1) {
// 一般取0
minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds());
}
long maxStaleMillis = 0;
if (!responseCaching.mustRevalidate() && requestCaching.maxStaleSeconds() != -1) {
maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds());
}
// 如果response有缓存,在过期时间内则直接返回上次缓存
if (!responseCaching.noCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
Response.Builder builder = cacheResponse.newBuilder();
if (ageMillis + minFreshMillis >= freshMillis) {
builder.addHeader("Warning", "110 HttpURLConnection \"Response is stale\"");
}
long oneDayMillis = 24 * 60 * 60 * 1000L;
if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {
builder.addHeader("Warning", "113 HttpURLConnection \"Heuristic expiration\"");
}
return new CacheStrategy(null, builder.build());
}
// 走到这,说明缓存已经过期了
// Find a condition to add to the request. If the condition is satisfied, the response body
// will not be transmitted.
String conditionName;
String conditionValue;
// etag与If-None-Match配合使用
// lastModified与If-Modified-Since配合使用
// 后者用于服务器进行资源对比,看资源是否改变
// 如果没有就算资源过期也可以用
if (etag != null) {
conditionName = "If-None-Match";
conditionValue = etag;
} else if (lastModified != null) {
conditionName = "If-Modified-Since"; // If-Modified-Since 本地浏览器存储的文件修改时间
conditionValue = lastModifiedString;
} else if (servedDate != null) {
conditionName = "If-Modified-Since";
conditionValue = servedDateString;
} else {
// 如果上面几个值都为null,做普通的请求,无法对比缓存为null
return new CacheStrategy(request, null); // No condition! Make a regular request.
}
Headers.Builder conditionalRequestHeaders = request.headers().newBuilder();
Internal.instance.addLenient(conditionalRequestHeaders, conditionName, conditionValue);
Request conditionalRequest = request.newBuilder()
.headers(conditionalRequestHeaders.build())
.build();
return new CacheStrategy(conditionalRequest, cacheResponse);
}
流程图
ConnectInterceptor
主要负责的是与服务器的连接的建立。
ConnectInterceptor # intercept()
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
// 在RetryAndFollowInterceptor中初始化过,到这里才使用
Transmitter transmitter = realChain.transmitter();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
// 判断是否为get请求
boolean doExtensiveHealthChecks = !request.method().equals("GET");
Exchange exchange = transmitter.newExchange(chain, doExtensiveHealthChecks);
// 将创建好的exchange,transmitter对象传递给下一个拦截器
return realChain.proceed(request, transmitter, exchange);
}
Transmitter # newExchange()
/** Returns a new exchange to carry a new request and response. */
Exchange newExchange(Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
synchronized (connectionPool) {
if (noMoreExchanges) {
throw new IllegalStateException("released");
}
if (exchange != null) {
throw new IllegalStateException("cannot make a new request because the previous response "
+ "is still open: please call response.close()");
}
}
// exchangeFinder的find方法中会从连接池中取出一条可用连接,如果没有的话会创建一条连接丢入连接池并进行TCP和TLS握手等准备工作
ExchangeCodec codec = exchangeFinder.find(client, chain, doExtensiveHealthChecks);
Exchange result = new Exchange(this, call, eventListener, exchangeFinder, codec);
synchronized (connectionPool) {
this.exchange = result;
this.exchangeRequestDone = false;
this.exchangeResponseDone = false;
return result;
}
}
CallServerInterceptor
负责将网络请求提交给服务器。接下来我们就是写入请求数据以及读出返回数据了。
http 100-continue用于客户端在发送POST数据给服务器前,征询服务器情况,看服务器是否处理POST的数据,如果不处理,客户端则不上传POST数据,如果处理,则POST上传数据。在现实应用中,在POST大数据时,才会使用100-continue协议。如果服务器端可以处理,则会返回100,否则会返回错误码
CallServerInterceptor # intercept()
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Exchange exchange = realChain.exchange();
Request request = realChain.request();
long sentRequestMillis = System.currentTimeMillis();
// 将请求头写入
// ExchangeCodec是一个接口,Http1ExchangeCodec中调用了writeRequest()
// 在sink(封装了socket输出流,涉及okio)中写入请求行和请求头
exchange.writeRequestHeaders(request);
boolean responseHeadersStarted = false;
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.
// 如果请求中存在“Expect:100-continue”标头,请在发送请求主体之前等待“HTTP / 1.1 100 Continue”响应。
// 如果我们没有得到,请返回我们得到的内容(例如4xx响应),而不发送请求主体。
// 如果有Expect:100-continue的请求头,就不去写入请求体,直接读取响应头
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
exchange.flushRequest();
responseHeadersStarted = true;
exchange.responseHeadersStart();
// 读取响应头(参数为true, 表明为100-continue)
responseBuilder = exchange.readResponseHeaders(true);
}
// 如果返回的结果为null 详见下面(说明服务端是想接受request body的)或者没有Expect:100-continue请求头
if (responseBuilder == null) {
if (request.body().isDuplex()) {
// Prepare a duplex body so that the application can send a request body later.
// 准备一个双工主体,以便应用程序稍后可以发送请求主体。
exchange.flushRequest();
BufferedSink bufferedRequestBody = Okio.buffer(
exchange.createRequestBody(request, true));
// 写响应体
request.body().writeTo(bufferedRequestBody);
} else {
// Write the request body if the "Expect: 100-continue" expectation was met.
BufferedSink bufferedRequestBody = Okio.buffer(
exchange.createRequestBody(request, false));
// 写响应体
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
}
// 返回的结果不为空
} else {
exchange.noRequestBody();
// 判断这个连接是否为http2.0(它可以多路复用),连接可以复用
if (!exchange.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.
// 不是就关闭连接
exchange.noNewExchangesOnConnection();
}
}
} else {
exchange.noRequestBody();
}
// 如果请求体为null或者不是双工
if (request.body() == null || !request.body().isDuplex()) {
exchange.finishRequest();
}
if (!responseHeadersStarted) {
exchange.responseHeadersStart();
}
// 读取响应头,参数为false
if (responseBuilder == null) {
responseBuilder = exchange.readResponseHeaders(false);
}
// 将请求的结果(可能是Expect:100-continue请求的结果,也可能是正常的情况下)包装成response
Response response = responseBuilder
.request(request)
.handshake(exchange.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
int code = response.code();
if (code == 100) {
// server sent a 100-continue even though we did not request one.
// try again to read the actual response
// 如果响应码是100,即使没有请求,再读一遍实际的响应
response = exchange.readResponseHeaders(false)
.request(request)
.handshake(exchange.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
code = response.code();
}
exchange.responseHeadersEnd(response);
// 判断是否是websocket并且响应码为101(切换协议),不是就读取响应体
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(exchange.openResponseBody(response))
.build();
}
// 从请求头和响应头判断其中是否有表明需要保持连接打开,如果不需要就释放连接和流
// 在http1.1中,client和server都是默认对方支持长连接的
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
exchange.noNewExchangesOnConnection();
}
// 204:空内容,服务器成功执行请求,但是没有返回信息。205:重置内容,服务器成功执行了请求,但是没有返回内容,与204不同,他需要请求者重置文档视图(比如,清除表单内容,重新输入)。
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
return response;
}
response返回给上一个拦截器。
Http1ExchangeCodec # readResponseHeaders()
从代码中可以看到获取响应头部信息包括获取响应行和响应头两部分,当服务器同意接收请求体时回返回100的响应码,可见此时该方法返回空,其他情况会返回非空的响应对象。
@Override public Response.Builder readResponseHeaders(boolean expectContinue) throws IOException {
if (state != STATE_OPEN_REQUEST_BODY && state != STATE_READ_RESPONSE_HEADERS) {
throw new IllegalStateException("state: " + state);
}
try {
StatusLine statusLine = StatusLine.parse(readHeaderLine());
Response.Builder responseBuilder = new Response.Builder()
.protocol(statusLine.protocol)
.code(statusLine.code)
.message(statusLine.message)
.headers(readHeaders());
if (expectContinue && statusLine.code == HTTP_CONTINUE) {
return null;
} else if (statusLine.code == HTTP_CONTINUE) {
state = STATE_READ_RESPONSE_HEADERS;
return responseBuilder;
}
state = STATE_OPEN_RESPONSE_BODY;
return responseBuilder;
} catch (EOFException e) {
//...
}
}