OkHttp是一个高效的HTTP客户端,它有以下默认特性:
- 支持HTTP/2,允许所有同一个主机地址的请求共享同一个socket连接
- 连接池减少请求延时
- 透明的GZIP压缩减少响应数据的大小
- 缓存响应内容,避免一些完全重复的请求
OKhttp的一般使用方法如下:
//异步执行
private void asyncGetRequests() {
String url = "https://www.alibaba.com";
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
.url(url)
.get()//默认就是GET请求
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() { //执行异步请求
@Override
public void onFailure(Call call, IOException e) {
do.....
}
@Override
public void onResponse(Call call, Response response) throws IOException {
do.....
}
});
}
//同步执行
private void syncGetRequests() {
String url = "https://wwww.alibaba.com";
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
.url(url)
.get()//默认就是GET请求
.build();
final Call call = okHttpClient.newCall(request);
new Thread(() -> {
try {
Response response = call.execute(); //执行同步请求
String result = response.body().string();
runOnUiThread(() -> tvContent.setText("synGetRequests run: " + result));
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
主要步骤
- 创建OkHttpClient和Request实例
- 通过OkHttpClient和Request的实例创建Call对象
- 通过调用call对象的enqueue或execute方法执行异步或同步调用
整体流程
下图展示了同步/异步请求的整体流程
类说明
- OkHttpClient:主要配置了网络请求的很多参数,最好是单例的形式存在,因为里面可以复用线程池和连接池。
- Call:一个接口类,具体去执行一个请求,只能被执行一次,它由RealCall实现。
- RealCall:定义了一个请求执行的具体过程,包括同步/异步,并且定义了拦截器的排列顺序。
- AsyncCall:是RealCall的内部类,它集成自NamedRunnable实现了Runnable,所以它是在子线程运行,里面具体处理了异步请求网络执行过程。
- Transmitter:意为发射器,是应用层和网络层的桥梁,在进行连接、真正发出请求和读取响应中起到很重要的作用,它里面保存了OkHttpClient、连接池、call、事件监听器、call超时时间等信息。
- Dispatcher:调度器,主要是异步请求的并发控制、把异步请求放入线程池执行,实现方法是promoteAndExecute()。 promoteAndExecute()有两处调用:添加异步请求时、同步/异步请求 结束时,里面使用了几个数组维护当前包含了那些Call,包括readyAsyncCalls、runningAsyncCalls(最大值为64,这里限制了线程池产生线程的个数)和runningSyncCalls。
- RealInterceptorChain:实现自Interceptor.Chain,它是一个拦截器链,它整合了所有拦截器,它具体实现了proceed方法,在这个方法里面依次获取了所有拦截器并执行了拦截器的intercept方法,并传入了下一个拦截器的索引。
流程中主要通过getResponseWithInterceptorChain方法获取Response返回结果,并且在这个方法里面整合了所有拦截器,我们具体看看整个过程。
拦截器组装过程
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());//OkHttpClient.Build#addInterceptor添加自定义拦截器
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());//OkHttpClient.Build#addNetworkInterceptor添加自定义网络网络拦截器
}
interceptors.add(new CallServerInterceptor(forWebSocket));//负责写请求和读响应的拦截器,Okio主场
//拦截器链,这里index为0,所以默认是先执行interceptors中的第一个拦截器
Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
originalRequest, this, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
boolean calledNoMoreExchanges = false;
try {
//拦截器链 开始执行第一个index = 0 的拦截器
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);
}
}
}
通过上面的代码我们可以知道,我们是通过一下顺序加入的拦截器:
- 通过addInterceptor 方式加入的自定义拦截器
- RetryAndFollowUpInterceptor 重试和重定向拦截器
- BridgeInterceptor 桥拦截器
- CacheInterceptor 缓存拦截器
- ConnectInterceptor 连接拦截器
- 通过addNetworkInterceptor 方式加入的自定义拦截器
- CallServerInterceptor 请求服务拦截器
所有拦截器都实现Interceptor接口
Interceptor {
//把拦截器链传递下去,实现请求的处理过程,返回response
Response intercept(Chain chain) throws IOException;
//拦截器链,负责把所有拦截器串联起来
interface Chain {
Request request();
//Chain的核心方法,进行拦截器的层次调用
Response proceed(Request request) throws IOException;
//返回请求执行的 连接. 仅网络拦截器可用; 应用拦截器就是null.
@Nullable Connection connection();
Call call();
int connectTimeoutMillis();
Chain withConnectTimeout(int timeout, TimeUnit unit);
int readTimeoutMillis();
Chain withReadTimeout(int timeout, TimeUnit unit);
int writeTimeoutMillis();
Chain withWriteTimeout(int timeout, TimeUnit unit);
}
}
Interceptor是个接口类,只有一个intercept方法,参数是Chain对象。内部接口类Chain拦截器链,有个proceed方法,参数是Request对象,返回值是Response,这个方法的实现就是请求的处理过程了。
public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
throws IOException {
//省略部分代码。。。。
// Call the next interceptor in the chain.
//获取下一个拦截器的RealInterceptorChain对象,并执行当前拦截器的intercept方法,并将chain作为参数传入
RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
//省略部分代码。。。。
return response;
}
综上所述,拦截器通过chain的执行过程类似于洋葱,从最外层,层层传入,并重最内层,层层传出。所有拦截器都可以处理当前获取的request,并将结果传入内层,也可以处理当前获取的response,并将结果传入最外层,除了最后一个拦截器,重这里我们也可以看出通过addInterceptor和addNetworkInterceptor的区别,前者更外层所以它只能处理最开始的request和最后的response,整个请求过程调用一次,而后者更内层,所以它可以被调用多次,比如重试或者重定向后都会调用它,并且它可以拿到很多connection和net相关的数据。
下面我们就依次分析每个拦截器。
重试/重定向拦截器
RetryAndFollowUpInterceptor,意为“重试和重定向拦截器”,作用是连接失败后进行重试、对请求结果跟进后进行重定向,我们先看看它整个执行过程,如下图:
@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) {
// 准备连接请求
// 主机、端口、协议都相同时,连接可复用
transmitter.prepareToConnect(request);
if (transmitter.isCanceled()) {
throw new IOException("Canceled");
}
Response response;
boolean success = false;
try {
// 执行其他(下一个->下一个->...)拦截器的功能,获取Response;
response = realChain.proceed(request, transmitter, null);
success = true;
} catch (RouteException e) {
// 连接路由异常,此时请求还未发送。尝试恢复
// 返回true,continue,继续下一个while循环
// 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) {
// IO异常,请求可能已经发出。尝试恢复
// 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.
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
Exchange exchange = Internal.instance.exchange(response);
Route route = exchange != null ? exchange.connection().route() : null;
// 后面的拦截器执行完了,拿到Response
// 解析看下是否需要重试或重定向,需要则返回新的Request
Request followUp = followUpRequest(response, route);
if (followUp == null) {
if (exchange != null && exchange.isDuplex()) {
transmitter.timeoutEarlyExit();
}
// 如果followUpRequest返回的Request为空,那边就表示不需要执行重试或者重定向,直接返回数据
return response;
}
RequestBody followUpBody = followUp.body();
if (followUpBody != null && followUpBody.isOneShot()) {
// 如果followUp不为null,请求体不为空,但只需要请求一次时,那么就返回response;
return response;
}
closeQuietly(response.body());
if (transmitter.hasExchange()) {
exchange.detachWithViolence();
}
// 判断重试或者重定向的次数是否超过最大的次数20,是的话则抛出异常
if (++followUpCount > MAX_FOLLOW_UPS) {
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
// 将需要重试或者重定向的请求赋值给新的请求
// 继续执行新请求
request = followUp;
priorResponse = response;
}//...while (true)
}
总结
- 通过realChain.proceed发起请求获取response
- 如果发生异常,则执行recover方法判断是否可以重试
- 如果请求成功,则通过followUpRequest判断response是否需要重定向
- 如果不需要重定向,则请求成功返回
桥拦截器
BridgeInterceptor ,意为 桥拦截器,相当于 在 请求发起端 和 网络执行端 架起一座桥,把应用层发出的请求 变为 网络层认识的请求,把网络层执行后的响应 变为 应用层便于应用层使用的结果。
public final class BridgeInterceptor implements Interceptor {
//Cookie管理器,初始化OkhttpClient时创建的
//默认是CookieJar.NO_COOKIES
private final CookieJar cookieJar;
public BridgeInterceptor(CookieJar cookieJar) {
this.cookieJar = cookieJar;
}
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
RequestBody body = userRequest.body();
// 处理封装"Content-Type", "Content-Length","Transfer-Encoding","Host","Connection",
// "Accept-Encoding","Cookie","User-Agent"等请求头
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");
}
// 在服务器支持gzip压缩的前提下,客户端不设置Accept-Encoding=gzip的话,
// okhttp会自动帮我们开启gzip和解压数据,如果客户端自己开启了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");
}
//从cookieJar中获取cookie,添加到header
List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));
}
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", Version.userAgent());
}
// 把处理好的新请求往下传递,执行后续的拦截器的逻辑
Response networkResponse = chain.proceed(requestBuilder.build());
//从networkResponse中获取 header "Set-Cookie" 存入cookieJar
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
// 获取返回体的Builder
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
// 处理返回的Response的"Content-Encoding"、"Content-Length"、"Content-Type"等返回头
// 如果我们没有手动添加"Accept-Encoding: gzip",这里会创建 能自动解压的responseBody--GzipSource
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)));
}
//然后新构建的response返回出去
return responseBuilder.build();
}
}
总结
主要就是添加了一些header、如果有cookie就添加、如果需要gzip就做压缩和解压缩。
缓存拦截器
CacheInterceptor,缓存拦截器,提供网络请求缓存的存取。合理使用本地缓存,有效地减少网络开销、减少响应延迟。
public final class CacheInterceptor implements Interceptor {
final @Nullable InternalCache cache;
public CacheInterceptor(@Nullable InternalCache cache) {
this.cache = cache;
}
@Override public Response intercept(Chain chain) throws IOException {
// 先获取候选缓存,前提是有配置缓存,也就是cache不为空;
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
long now = System.currentTimeMillis();
// 执行获取缓存策略的逻辑
// 缓存策略决定是否使用缓存:
// strategy.networkRequest为null,不使用网络
// strategy.cacheResponse为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.
}
// networkRequest == null 不能用网络
// 如果不使用网络数据且缓存数据为空,那么返回一个504的Response,并且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();
}
// 如果不需要使用网络数据,那么就直接返回缓存的数据
// If we don't need the network, we're done.
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
/*
* 到这里,networkRequest != null (cacheResponse可能null,可能!null)
* 没有命中强缓存的情况下,进行网络请求,获取response
* 先判断是否是协商缓存(304)命中,命中则更新缓存返回response
* 未命中使用网络请求的response返回并添加缓存
*/
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) {
// 如果缓存数据不为空并且code为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()).
cache.trackConditionalCacheHit();
// 更新缓存数据
cache.update(cacheResponse, response);
return response;
} else {
//如果是非304,说明服务端资源有更新,就关闭缓存body
closeQuietly(cacheResponse.body());
}
}
// 协商缓存也未命中,获取网络返回的response
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
if (cache != null) {
//网络响应可缓存(请求和响应的 头 Cache-Control都不是'no-store')
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
// 将网络数据保存到缓存中
// InternalCache接口,实现在Cache类中
// Offer this request to the cache.
CacheRequest cacheRequest = cache.put(response);
return cacheWritingResponse(cacheRequest, response);
}
//OkHttp默认只会对get请求进行缓存
//不是get请求就移除缓存
if (HttpMethod.invalidatesCache(networkRequest.method())) {
try {
// 缓存失效,那么就移除缓存
cache.remove(networkRequest);
} catch (IOException ignored) {
// The cache cannot be written.
}
}
}
return response;
}
...
}
总结
- 通过缓存策略决定是使用缓存数据还是网络数据
- 默认只缓存GET请求
- 缓存保存在用户指定的目录,内部通过DiskLruCache实现,使用URL的md5做为key
连接拦截器
ConnectInterceptor,连接拦截器,主要负责连接的创建和复用。
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
Transmitter transmitter = realChain.transmitter();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
// 实现了复杂的网络请求,Exchange用于下一个拦截器CallServerInterceptor进行网络请求使用;
/*
1:调用transmitter.newExchange方法;
2:通过Transmitter的ExchangeFinder调用了find方法;
3:ExchangeFinder里调用了findHealthyConnection方法;
4:ExchangeFinder里findHealthyConnection调用了findConnection方法创建了RealConnection;
5:调用了RealConnection的connect方法,实现了TCP + TLS 握手,底层通过socket来实现的;
6: 通过RealConnection的newCodec方法创建了两个协议类,一个是Http1ExchangeCodec,对应着HTTP1.1,
一个是Http2ExchangeCodec,对应着HTTP2.0;
*/
//创建一个交换器Exchange
Exchange exchange = transmitter.newExchange(chain, doExtensiveHealthChecks);
// 执行后续的拦截器逻辑,注意是三个参数
return realChain.proceed(request, transmitter, exchange);
}
类说明
- Transmitter:意为发射器,是把请求从应用端发射到网络层,它持有请求的连接、请求、响应和流,一个请求对应一个Transmitter实例,一个数据流。
- Exchange:request、responseIO管理,对ExchangeCodec的包装,增加了事件回调,一个请求对应一个Exchange实例。
- ExchangeCodec:接口类,负责真正的IO写请求、读响应,实现类有Http1ExchangeCodec、Http2ExchangeCodec,对应HTTP1.1、HTTP2.0。
- ExchangeFinder:从连接池中寻找可用TCP连接,然后通过连接得到ExchangeCodec。
- Route:一个具体连接服务器的路由,包括address(IP地址通过DNS可能获取多个)和proxy(通过OKhttpclient配置没有则使用默认的)
- RouteSelector:配置代理服务器以及通过DNS获取IP地址resetNextInetSocketAddress。
- ConnectionPool:连接池,用于管理http1.1/http2.0连接重用,以减少网络延迟。
相同Address的http请求可以共享一个连接,ConnectionPool就是实现了连接的复用。
1.默认最多保存5个空闲连接,并且空闲5分钟后移除。
2.通过put方法给连接池添加连接,首先通过cleanupRunning线程移除空闲并过期的连
接,这个线程在整个线程池里面只会存在一个。
3.通过transmitterAcquirePooledConnection取一个可以复用的连接。 - RealConnection:一个具体的连接,通过connect闯将一个连接通道,供后续IO操作使用。
总结
- 主要是获取一个Exchange对象并通过realChain.proceed方法传递给后续拦截器做具体的IO操作使用。
- 通过ExchangeFinder去获取一个ExchangeCodec,即具体操作request和response的协议。
- 通过RealConnectionPool连接池去复用连接,连接池复用逻辑如下图所示:
请求服务连接器
CallServerInterceptor,请求服务拦截器,也就是真正地去进行网络IO读写了——写入http请求的header和body数据、读取响应的header和body。
public final class CallServerInterceptor implements Interceptor {
private final boolean forWebSocket;
public CallServerInterceptor(boolean forWebSocket) {
this.forWebSocket = forWebSocket;
}
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
//ConnectInterceptor拦截器传入的exchange
Exchange exchange = realChain.exchange();
Request request = realChain.request();
long sentRequestMillis = System.currentTimeMillis();
// 将请求头写入到socket中,底层通过ExchangeCodec协议类
// (对应Http1ExchangeCodec和Http2ExchangeCodec)
// 最终是通过Okio来实现的,具体实现在RealBufferedSink这个类里面
exchange.writeRequestHeaders(request);
// 如果有body的话,通过Okio将body写入到socket中,用于发送给服务器
boolean responseHeadersStarted = false;
Response.Builder responseBuilder = null;
//含body的请求
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"的响应,然后再发送请求body.
// 如果没有收到这个响应(例如收到的响应是4xx),那就不发送body了。
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
exchange.flushRequest();
responseHeadersStarted = true;
exchange.responseHeadersStart();
//读取响应头
responseBuilder = exchange.readResponseHeaders(true);
}
// responseBuilder为null说明服务端返回了100,也就是可以继续发送body了
// 底层通过ExchangeCodec协议类(对应Http1ExchangeCodec和Http2ExchangeCodec)来读取返回头header的数据
if (responseBuilder == null) {
if (request.body().isDuplex()) {//默认是false不会进入
// 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.
// 满足了 "Expect: 100-continue" ,写请求body
BufferedSink bufferedRequestBody = Okio.buffer(
exchange.createRequestBody(request, false));
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
}
} else {
//没有满足 "Expect: 100-continue" ,请求发送结束
exchange.noRequestBody();
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 {
//没有body,请求发送结束
exchange.noRequestBody();
}
//请求发送结束
if (request.body() == null || !request.body().isDuplex()) {
//真正将写到socket输出流的http请求数据发送。
exchange.finishRequest();
}
//回调 读响应头开始事件(如果上面没有)
if (!responseHeadersStarted) {
exchange.responseHeadersStart();
}
//读响应头(如果上面没有)
if (responseBuilder == null) {
responseBuilder = exchange.readResponseHeaders(false);
}
// 创建返回体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);
//这里就是获取响应body了
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 {
// 读取返回体body的数据
// 底层通过ExchangeCodec协议类(对应Http1ExchangeCodec和Http2ExchangeCodec)
response = response.newBuilder()
.body(exchange.openResponseBody(response))
.build();
}
//请求头中Connection是close,表示请求完成后要关闭连接
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
exchange.noNewExchangesOnConnection();
}
//204(无内容)、205(重置内容),body应该是空
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
return response;
}
}
类说明
- Exchange:对外提供了writeXX、openXX、flushXX等方法通过ExchangeCodec操作request和response的传递。
- ExchangeCodec:它有两个实现类Http1ExchangeCodec(HTTP/1.1)和Http2ExchangeCodec(HTTP/2),里面根据不同的协议规范制定了具体的格式和IO过程。
- Okio:具体执行IO操作的框架
总结
- 通过上面的connection通道,具体执行发送request和接受response过程。
- 这个拦截器最终返回了response,并且没有执行chain的proceed方法,所以它是最里层节点,然后一层层把response返回出去。