在上一篇 OkHttp 请求流程分析中,分别介绍了同步和异步请求的具体流程,也了解到具体的的网路请求时经过 OkHttp 中的 RealCall 的拦截器链实现的,还没了解的朋友建议去看下上篇文章。java
OkHttp 的拦截器总共分为如下几种: 缓存
绿色的部分是咱们用户能够本身去定义添加的,不设置就没有,蓝色的部分是 OkHttp 内部实现的,不设置也有,而且不可更改。服务器
下面依次进行讲解分析:cookie
应用拦截器的使用:网络
设置拦截器app
这样就添加了一个很普通的应用拦截器。less
官方对应用拦截器的描述异步
中文:socket
RetryAndFollowUpInterceptor 拦截器是应用拦截器以后的第一个拦截器,主要是负责失败重连和重定向的。ide
可是并非全部的网络请求能够进行重连的,在RetryAndFollowUpInterceptor 拦截器的内部会经过必定的逻辑进行判断。具体来看下源码是怎么实现的:
根据上一篇文章中讲述的,主要逻辑存在 intercept 方法中:
@Override public Response intercept(Chain chain) throws IOException {
// 获取原始请求
Request request = chain.request();
// 建立 StreamAllocation对象,这里只是建立,具体处理交给后面的拦截器处理
// 主要用户获取链接服务端的 Connect链接 和 用于服务端用于数据传输的输入输出流
// 主要给 ConnectIntercept 使用的
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(request.url()), callStackTrace);
int followUpCount = 0;
Response priorResponse = null;
// 开启死循环
while (true) {
// 若是取消,就释放资源
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response = null;
boolean releaseConnection = true;
try {
// 传入建立的StreamAllocation对象,交给下个拦截器处理,至于下个拦截器处理不处理,不用管
// 在 catch 和 finally 中作了一些异常处理和资源释放
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (RouteException e) {
// 若是进入 RouteException 路由异常,则尝试是否能够从新进行请求,若能够则从头开始新的请求
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), false, request)) {
throw e.getLastConnectException();
}
releaseConnection = false;
continue;
} catch (IOException e) {
// 如果进入 IOException IO异常,若能够从新尝试请求,则从头开始新的请求
// 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();
}
}
//若是以前发生太重定向,而且 priorResponse 不为空,则建立新的 响应对象,并将其 body 置位空
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
// 等到一个接收 Response 的HTTP新请求。
// 这将添加身份验证、遵循重定向、处理客户端请求超时。若是后续操做没必要要或不适用,则返回null。
Request followUp = followUpRequest(response);
// 若 followUp 重试请求为空,返回当前的响应
if (followUp == null) {
if (!forWebSocket) {
streamAllocation.release();
}
return response;
}
//关闭响应结果
closeQuietly(response.body());
// 若是重试次数超过最大值MAX_FOLLOW_UPS,释放资源,抛出异常
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
// 若是新请求的 body 属于不可重复请求的 body,释放资源
if (followUp.body() instanceof UnrepeatableRequestBody) {
streamAllocation.release();
throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
}
// 判断是不是相同的链接,若不相同,释放 streamAllocation,建立新的 streamAllocation 对象
if (!sameConnection(response, followUp.url())) {
streamAllocation.release();
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(followUp.url()), 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;
}
}
复制代码
总结主要做用下:
BridgeInterceptor 主要做用就是添加头部、设置压缩等。
仍是主要看下 Intercept 中的代码:
@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));
}
// 设置链接方式,这里看到默认是开启持久链接的 使用 Keep-Alive
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.
// 默认不使用gzip 传输, 若是咱们添加 gzip 压缩头字段,会对传输的数据进行编码解码处理
boolean transparentGzip = false;
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true;
requestBuilder.header("Accept-Encoding", "gzip");
}
// 设置 cookies
List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));
}
// 设置 ua
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", Version.userAgent());
}
// 调用下个拦截器,等待下个拦截器返回结果
Response networkResponse = chain.proceed(requestBuilder.build());
// 执行到这里,下面都是对返回值进行处理
// 将网络请求返回的 Response 转换成用户可使用的 Response
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());
Headers strippedHeaders = networkResponse.headers().newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build();
responseBuilder.headers(strippedHeaders);
responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));
}
return responseBuilder.build();
}
复制代码
总结主要做用:
缓存拦截器主要是对网络请求的缓存进行处理。使用很简单,主要经过 Cache 类实现的,在建立 OkHttpClient 的时候设置 cache 就行。
// 建立 Cache 类 "app/cache"是缓存的路径 20*1024*1024 是缓存大小
Cache cache = new Cache(new File("app/cache"), 20 * 1024 * 1024);
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.addInterceptor(new ApplicationIntercept())
//设置缓存
.cache(cache)
.build();
复制代码
这样就可以借助于 OkHttp 帮助咱们实现缓存。
具体看下 CacheInterceptor 的 Intercept 方法:
public final class CacheInterceptor implements Interceptor {
// 实现缓存功能的 InternalCache 类,下面讲解
final InternalCache cache;
public CacheInterceptor(InternalCache cache) {
this.cache = cache;
}
@Override public Response intercept(Chain chain) throws IOException {
// 若是 cache 为空,则用户没设置缓存,直接返回 null
// 不为空,设置了缓存,经过 cache 对象的 get 方法去获取缓存,称之为候选缓存
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
long now = System.currentTimeMillis();
// 建立缓存策略,而且分别获得 请求 networkRequest,和响应 cacheResponse
// 里面维护了一个 Request 和 Response ,关于这个策略,下面分析
// public final Request networkRequest; 若是不须要网络访问,为 null,不为 null,须要网络请求
// public final Response cacheResponse; 不使用缓存,为 null,不为 null,表示可使用本地缓存
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()); // The cache candidate wasn't applicable. Close it.
}
// If we're forbidden from using the network and the cache is insufficient, fail.
// 若是网络不可用,而且没有缓存,经过构建者模式构建一个 504 的 Response。并返回
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 {
// 调用下一个拦截器去获取 Response
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.
// 此时经过网络请求拿到了 Response ,而且本地有缓存的 response,
if (cacheResponse != null) {
// 若是响应码为 未修改,直接使用缓存的 Response做为最终的返回 Response
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()).
cache.trackConditionalCacheHit();
cache.update(cacheResponse, response);
return response;
} else {
closeQuietly(cacheResponse.body());
}
}
// 上面都没返回,根据网络请求响应生成最终的响应
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
// 若是返回的 Response 有请求体
if (HttpHeaders.hasBody(response)) {
// 经过调用 maybeCache() 方法,进而调用 cache 的 put 写入用户缓存
CacheRequest cacheRequest = maybeCache(response, networkResponse.request(), cache);
// 写入缓存
response = cacheWritingResponse(cacheRequest, response);
}
return response;
}
}
复制代码
缓存策略CacheStrategy:
前面在 Intercept 方法中调用了
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
// 看下静态内部类的 Factory 构造方法,主要是读取传入的 缓存 Response 的 headers 信息
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++) {
// 响应头 key
String fieldName = headers.name(i);
// 响应头 value
String value = headers.value(i);
// 有 Date 头,服务器返回时间信息
if ("Date".equalsIgnoreCase(fieldName)) {
servedDate = HttpDate.parse(value);
servedDateString = value;
// 有 Expires 头,获取有效期信息
} else if ("Expires".equalsIgnoreCase(fieldName)) {
expires = HttpDate.parse(value);
// 有 Last-Modified 头,获取最后修改时间,
} else if ("Last-Modified".equalsIgnoreCase(fieldName)) {
lastModified = HttpDate.parse(value);
lastModifiedString = value;
// 有 ETag 头,获取资源的版本
} else if ("ETag".equalsIgnoreCase(fieldName)) {
etag = value;
// 有 Age 头,获取到如今的经历了多少时间
} else if ("Age".equalsIgnoreCase(fieldName)) {
ageSeconds = HttpHeaders.parseSeconds(value, -1);
}
}
}
}
// 而后调用 get 方法
/** * Returns a strategy to satisfy {@code request} using the a cached response {@code response}. */
public CacheStrategy get() {
// 返回一个假设可使用网络请求的缓存策略,也就是 networkRequest 不为 null
CacheStrategy candidate = getCandidate();
// 若是networkRequest 不为 null,表示须要使用网络,而且 Request 的 CacheControl 字段设置为只使用缓存
// 就须要设置 都为 null,而后再 CacheInterceptor 中构建错误的返回。
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;
}
复制代码
前面讲到的将响应 Response 添加到本地,主要是经过 CacheInterceptor 内部的 InternalCache 实现的,
public interface InternalCache {
Response get(Request request) throws IOException;
CacheRequest put(Response response) throws IOException;
/** * Remove any cache entries for the supplied {@code request}. This is invoked when the client * invalidates the cache, such as when making POST requests. */
void remove(Request request) throws IOException;
/** * Handles a conditional request hit by updating the stored cache response with the headers from * {@code network}. The cached response body is not updated. If the stored response has changed * since {@code cached} was returned, this does nothing. */
void update(Response cached, Response network);
/** Track an conditional GET that was satisfied by this cache. */
void trackConditionalCacheHit();
/** Track an HTTP response being satisfied with {@code cacheStrategy}. */
void trackResponse(CacheStrategy cacheStrategy);
}
复制代码
而 InternalCache 是个接口,内部都是调用的 Cache 这个类中的方法,先看下 Cache 类:
public final class Cache implements Closeable, Flushable {
private static final int VERSION = 201105;
private static final int ENTRY_METADATA = 0;
private static final int ENTRY_BODY = 1;
private static final int ENTRY_COUNT = 2;
// 这个 InternalCache 是个接口,里面定义了经常使用的增删改查等操做
// 全部的 实现都是经过 外部的 Cache 类的对应方法实现的
final InternalCache internalCache = new InternalCache() {
@Override public Response get(Request request) throws IOException {
return Cache.this.get(request);
}
@Override public CacheRequest put(Response response) throws IOException {
return Cache.this.put(response);
}
@Override public void remove(Request request) throws IOException {
Cache.this.remove(request);
}
@Override public void update(Response cached, Response network) {
Cache.this.update(cached, network);
}
@Override public void trackConditionalCacheHit() {
Cache.this.trackConditionalCacheHit();
}
@Override public void trackResponse(CacheStrategy cacheStrategy) {
Cache.this.trackResponse(cacheStrategy);
}
};
// 硬盘缓存实现
final DiskLruCache cache;
/* read and write statistics, all guarded by 'this' */
int writeSuccessCount;
int writeAbortCount;
private int networkCount;
// 命中次数
private int hitCount;
// 请求次数
private int requestCount;
public Cache(File directory, long maxSize) {
this(directory, maxSize, FileSystem.SYSTEM);
}
Cache(File directory, long maxSize, FileSystem fileSystem) {
this.cache = DiskLruCache.create(fileSystem, directory, VERSION, ENTRY_COUNT, maxSize);
}
复制代码
前面在讲 LruCache 的时候,既然是缓存就必然伴随着 插入缓存、更新缓存、删除缓存、查询缓存。
先来看下 插入缓存的 put 方法:
CacheRequest put(Response response) {
// 获取请求方法
String requestMethod = response.request().method();
// 判断若是是 POST、PATCH、PUT、DELETE、MOVE几种请求方法,则不支持缓存,移除缓存,返回 null
if (HttpMethod.invalidatesCache(response.request().method())) {
try {
remove(response.request());
} catch (IOException ignored) {
// The cache cannot be written.
}
return null;
}
// 若是不是 get 请求,返回 null。好比 post 不必缓存
if (!requestMethod.equals("GET")) {
// Don't cache non-GET responses. We're technically allowed to cache
// HEAD requests and some POST requests, but the complexity of doing
// so is high and the benefit is low.
return null;
}
if (HttpHeaders.hasVaryAll(response)) {
return null;
}
// 生成 Entry 实体,Entry 封装了全部的返回值信息
Entry entry = new Entry(response);
// 缓存的实际操做,使用DiskLruCache实现硬盘缓存
DiskLruCache.Editor editor = null;
try {
// 建立写入的 editor。
// key 值就是 key() 方法生成的,md5 加密,而后换算成 16 进制。
// ByteString.encodeUtf8(url.toString()).md5().hex();
editor = cache.edit(key(response.request().url()));
if (editor == null) {
return null;
}
// 将缓存写入磁盘,在 writeTo 方法中,将头部等信息进行缓存,若是是 https 请求,将会作特殊的处理
entry.writeTo(editor);
// 传入 editor 建立一个 CacheRequestImpl
return new CacheRequestImpl(editor);
} catch (IOException e) {
abortQuietly(editor);
return null;
}
}
复制代码
删除缓存的 remove 方法:
void remove(Request request) throws IOException {
// 这里调用 DiskLruCache 中的 remove 方法移除缓存
cache.remove(key(request.url()));
}
复制代码
更新缓存的 update 方法:
void update(Response cached, Response network) {
Entry entry = new Entry(network);
DiskLruCache.Snapshot snapshot = ((CacheResponseBody) cached.body()).snapshot;
DiskLruCache.Editor editor = null;
try {
editor = snapshot.edit(); // Returns null if snapshot is not current.
if (editor != null) {
entry.writeTo(editor);
editor.commit();
}
} catch (IOException e) {
abortQuietly(editor);
}
}
复制代码
这里是先生成一个实体,而后找到保存的快照,而后用执行写入
查找缓存的 get 方法:
Response get(Request request) {
//先得到通过 md5 加密后的 url
String key = key(request.url());
// DiskLruCache 的快照
DiskLruCache.Snapshot snapshot;
// 保存缓存的实体
Entry entry;
try {
// 从 DiskLruCache 中的 get 方法,查找到 快照。
snapshot = cache.get(key);
//为空,则没缓存
if (snapshot == null) {
return null;
}
} catch (IOException e) {
// Give up because the cache cannot be read.
return null;
}
try {
// 建立Entry
entry = new Entry(snapshot.getSource(ENTRY_METADATA));
} catch (IOException e) {
Util.closeQuietly(snapshot);
return null;
}
// 从 Entry 照片中得到 Response
Response response = entry.response(snapshot);
if (!entry.matches(request, response)) {
Util.closeQuietly(response.body());
return null;
}
return response;
}
复制代码
做用主要是打开与服务器之间的连接,正式开始网络请求。
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
// 拿到 RealInterceptorChain 中建立的 StreamAllocation 对象。
StreamAllocation streamAllocation = realChain.streamAllocation();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
// 经过 newStream 建立一个 HttpCodec 对象,主要用来编码 Request 和解码 Response 的
HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);
// 得到 RealConnection 的对象 connection。 用来进行实际的 io 传输
RealConnection connection = streamAllocation.connection();
// 调用下个拦截器
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
//来看下 newStream 方法
public HttpCodec newStream(OkHttpClient client, boolean doExtensiveHealthChecks) {
// 设置超时
int connectTimeout = client.connectTimeoutMillis();
int readTimeout = client.readTimeoutMillis();
int writeTimeout = client.writeTimeoutMillis();
// 是否重试
boolean connectionRetryEnabled = client.retryOnConnectionFailure();
try {
// 构建 RealConnection 对象
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);
// 构建一个 HttpCodec 对象
HttpCodec resultCodec = resultConnection.newCodec(client, this);
synchronized (connectionPool) {
codec = resultCodec;
return resultCodec;
}
} catch (IOException e) {
throw new RouteException(e);
}
}
// 看下 findHealthyConnection
private RealConnection findHealthyConnection(int connectTimeout, int readTimeout, int writeTimeout, boolean connectionRetryEnabled, boolean doExtensiveHealthChecks) throws IOException {
while (true) {
RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
connectionRetryEnabled);
// If this is a brand new connection, we can skip the extensive health checks.
synchronized (connectionPool) {
if (candidate.successCount == 0) {
return candidate;
}
}
// Do a (potentially slow) check to confirm that the pooled connection is still good. If it
// isn't, take it out of the pool and start again.
if (!candidate.isHealthy(doExtensiveHealthChecks)) {
noNewStreams();
continue;
}
return candidate;
}
复制代码
在 findHealthyConnection 方法 内部调用了 findConnection 方法,在findConnection 方法内部主要是去找有没有能够服用的连接
这个拦截器和应用拦截器都是用户本身设置的拦截器。使用方法可应用拦截器差很少,这里就不在讲了,看下官方对其描述:
Able to operate on intermediate responses like redirects and retries.
Not invoked for cached responses that short-circuit the network.
Observe the data just as it will be transmitted over the network.
Access to the Connection
that carries the request.
可以对重定向和重试的时候进行操做(很好理解,重定向什么的在第二层,应用拦截器不会调用,可是网络拦截器能够调用)。
若是读取缓存中的数据,就不会执行网络拦截器
能够检测到全部须要网络传输的数据
能够访问完整的Request 请求
因为是在重定向拦截器以后的,因此在发生重定向的时候,网络拦截器可能会执行屡次,能够较为完整的检测网络请求的情况。
而应用拦截器在请求重试拦截器以前,因此检测不到重定向的请求,只能检测到最初的请求和最终的返回。
CallServerInterceptor拦截器 是拦截器链中的最后一个拦截器,主要负责向服务器发送真正的网络请求和接收服务器返回的响应。
@Override public Response intercept(Chain chain) throws IOException {
// 获取在 ConnectInterceptor 拦截器中建立的 httpCodec 对象
HttpCodec httpCodec = ((RealInterceptorChain) chain).httpStream();
// 获取 streamAllocation
StreamAllocation streamAllocation = ((RealInterceptorChain) chain).streamAllocation();
Request request = chain.request();
long sentRequestMillis = System.currentTimeMillis();
// 向 Socket 中写入请求的头部信息
httpCodec.writeRequestHeaders(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();
responseBuilder = httpCodec.readResponseHeaders(true);
}
// Write the request body, unless an "Expect: 100-continue" expectation failed.
if (responseBuilder == null) {
Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
// 向 Socket 中写入请求的 body 信息
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
}
}
// 完成网络请求的写入工做
httpCodec.finishRequest();
// 下面是读取操做
if (responseBuilder == null) {
// 读取响应的头部信息
responseBuilder = httpCodec.readResponseHeaders(false);
}
// 构建返回的 Response
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
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();
}
// 若是响应的状态码为 204 和 205 而且响应体不为空,则抛出异常
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
return response;
}
复制代码
HttpCodec 是一个接口,主要用来编码 Request 和解码 Response 的,主要有两个实现类:
/**A socket connection that can be used to send HTTP/1.1 messages. */
public final class Http1Codec implements HttpCodec {}
/** Encode requests and responses using HTTP/2 frames. */
public final class Http2Codec implements HttpCodec {}
复制代码
都是如今主流的 Http 协议规范。
CallServerInterceptor拦截器主要就是完成最终的网络请求工做,遵循 HTTP 协议规范,经过 HttpCodec
对象写入请求头、请求主体,而且读取响应头和响应主体,写入 Response 中,依次返回给上级拦截器,最终传递到调用的地方,就完成了依次网络请求。
到这里 OkHttp 源码就分析完了,可能有些地方理解的还不是很透彻,继续加油吧。
欢迎关注个人公众号: