目录
主要是去请求服务器返回对应的Response,对应代码如下:
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) {
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);
}
从代码中可以看到,其中就是加了一些不同功能的拦截器。
一、拦截器
1、用户自定义的拦截器
用在与服务器建立链接之前进行拦截
nterceptors.addAll(client.interceptors());
2、RetryAndFollowUpInterceptor
重试和失败重定向拦截器。
interceptors.add(retryAndFollowUpInterceptor);
3、BridgeInterceptor
桥接和适配拦截器。主要补充用户创建请求当中的一些请求头Content-Type等
interceptors.add(new BridgeInterceptor(client.cookieJar()));
4、CacheInterceptor
主要处理缓存。
interceptors.add(new CacheInterceptor(client.internalCache()));
5、 ConnectInterceptor
与服务器建立链接。创建可以用的RealConnection(对java.io和java.nio进行了封装)
interceptors.add(new ConnectInterceptor(client))
6、用户自定义的拦截器
用在与服务器建立链接之后进行拦截。只有非socket进行设置
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
7、CallServerInterceptor
向服务器发送请求和接收数据。将请求写入IO流,再从IO流中读取响应数据
interceptors.add(new CallServerInterceptor(forWebSocket));
8、RealInterceptorChain
主要就是将上述几个拦截器顺序执行。
从执行RetryAndFollowUpInterceptor->执行BridgeInterceptor->执行CacheInterceptor->执行ConnectInterceptor->执行CallServerInterceptor->依次将响应一一返回->返回到ConnectInterceptor->返回到CacheInterceptor->返回到BridgeInterceptor->返回到RetryAndFollowUpInterceptor。
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
二、实现过程
1、举例分析
实例化RealInterceptorChain,调用RealInterceptorChain的proceed()方法。
public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
HttpCodec httpCodec, RealConnection connection, int index, Request request, Call call,
EventListener eventListener, int connectTimeout, int readTimeout, int writeTimeout) {
this.interceptors = interceptors;
//。。。省略代码
this.index = index;
//。。。省略代码
}
将interceptors和0传入到RealInterceptorChain。进入到proceed()方法中,查看关键代码。先去执行集合中的第一个拦截器。。
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
// 。。。。 省略代码
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
// 。。。。 省略代码
return response;
}
我们从第一行代码中可以看到index+1进行访问下一个拦截器;
第二行代码中找到该第一个拦截器;
第三句代码就是调用第一个拦截器的intercept(),将创建的第二个拦截器传入当前第一个拦截器,然后该的intercept()的返回的第二个拦截器的response返回。
进入到第一个拦截器RetryAndFollowUpInterceptor中进行查看代码,可以看到
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
// 。。。。省略代码
response = realChain.proceed(request, streamAllocation, null, null);
// 。。。。省略代码
}
最后return就是返回的第二个拦截器的response。
2、综述
每个拦截器都是这种流程,当前一个拦截器调用下一个,然后返回下一个的response。
三、简单分析几个比较重要的拦截器
1、ConnectInterceptor
将request和建立的链接connect传到了CallServerInterceptor拦截器中
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
StreamAllocation streamAllocation = realChain.streamAllocation();
// 。。。省略代码
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
2、CallServerInterceptor
向服务器发送请求,将返回的response进行返回到上一个interceptor
1)首先就是从传过来的RealInterceptorChain得到httpCodec、request、connect
RealInterceptorChain realChain = (RealInterceptorChain) chain;
//1、返回一个HttpCodec:Http的链接方式。根据不同链接方式返回Http2Codec或者Http1Codec。具体实现过程是在ConnectInterceptor中实例化。
//最终代码实现是在RealConnect中
HttpCodec httpCodec = realChain.httpStream();
// 。。。。省略代码
RealConnection connection = (RealConnection) realChain.connection();
//获取request请求
Request request = realChain.request();
2)向服务器写入header
//2、开始向服务器写入头信息,查看Http2Codec或者Http1Codec
realChain.eventListener().requestHeadersStart(realChain.call());
httpCodec.writeRequestHeaders(request);
realChain.eventListener().requestHeadersEnd(realChain.call(), request);
Response.Builder responseBuilder = null;
3)首先会先去判断是否为100-continue,如果接收的话,返回responseBuild 为null;
//3、判断是否有请求实体?
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
//4、100-continue的情况:在发送requestBody前向服务器确认是否接收request。只有拿到服务器的结果之后在继续
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
httpCodec.flushRequest();
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(true);
}
4)如果100-continue继续接收或者不是100-continue,则将数据写入到requestBody中
//5、开始写"Expect: 100-continue"的response的内容。如果100-continue需要握手但是握手失败的话,
//返回的responseBuilder不为空
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()) {
//。。。省略代码
}
}
5)执行流的刷新
//6、结束写request
httpCodec.finishRequest();
6)开始根据request构建response,然后根据不同的code来决定是否进行将服务器返回的数据写入到response中
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.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
responseBuilder = httpCodec.readResponseHeaders(false);
response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
code = response.code();
}
realChain.eventListener()
.responseHeadersEnd(realChain.call(), response);
//切换协议
if (forWebSocket && code == 101) {
//。。。省略代码
} else {
//读取返回的IO数据流
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
//。。。。省略代码
return response;
备注:
(1)http 100-continue用户客户端在发送post数据给服务器前,先确认服务器是否处理post数据,如果不处理,则不上传。常用于post大数据。
(2)客户端如果有post数据需要上传,则可以考虑使用100-continue协议。只要在头加入{“Expect”:”100-continue”} ,如果没有post数据,不能使用该协议
(3)并不是所有的server都会实现该协议,若超出时间无响应,则客户端需要立刻上传
四、HttpCodec
从CallServerInterceptor中可以看到,HttpCodec提供I/O操作,完成对服务器发送和接收数据 。
1、获得HttpCodec对象
HttpCodec httpCodec = realChain.httpStream();
进入到RealInterceptorChain,看到该对象是从ConnectInterceptor中传入的。在进入到ConnectInterceptor中可以看到
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
最终跟到RealConnection中,可以看到就是根据不同的情况实例化了Http1Codec或者Http2Codec,对应分别Http1.1协议以及Http2.0协议
public HttpCodec newCodec(OkHttpClient client, Interceptor.Chain chain,
StreamAllocation streamAllocation) throws SocketException {
if (http2Connection != null) {
return new Http2Codec(client, chain, streamAllocation, http2Connection);
} else {
socket.setSoTimeout(chain.readTimeoutMillis());
source.timeout().timeout(chain.readTimeoutMillis(), MILLISECONDS);
sink.timeout().timeout(chain.writeTimeoutMillis(), MILLISECONDS);
return new Http1Codec(client, streamAllocation, source, sink);
}
}
2、写request的header
httpCodec.writeRequestHeaders(request);
进入到代码中,拿Http1Codec举例
@Override public void writeRequestHeaders(Request request) throws IOException {
String requestLine = RequestLine.get(
request, streamAllocation.connection().route().proxy().type());
writeRequest(request.headers(), requestLine);
}
/** Returns bytes of a request header for sending on an HTTP transport. */
public void writeRequest(Headers headers, String requestLine) throws IOException {
if (state != STATE_IDLE) throw new IllegalStateException("state: " + state);
sink.writeUtf8(requestLine).writeUtf8("\r\n");
for (int i = 0, size = headers.size(); i < size; i++) {
sink.writeUtf8(headers.name(i))
.writeUtf8(": ")
.writeUtf8(headers.value(i))
.writeUtf8("\r\n");
}
sink.writeUtf8("\r\n");
state = STATE_OPEN_REQUEST_BODY;
}
写入请求头
3、读response的header
responseBuilder = httpCodec.readResponseHeaders(true);
进入到Http1Codec
@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());
//。。。省略代码 根据不同的情况来返回responseBuilder
}
4、执行流的刷新
httpCodec.finishRequest();
进入到Http1Codec
@Override public void finishRequest() throws IOException {
sink.flush();
}
5、读取response的内容
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
进入到Http1Codec
@Override public ResponseBody openResponseBody(Response response) throws IOException {
streamAllocation.eventListener.responseBodyStart(streamAllocation.call);
String contentType = response.header("Content-Type");
if (!HttpHeaders.hasBody(response)) {
Source source = newFixedLengthSource(0);
return new RealResponseBody(contentType, 0, Okio.buffer(source));
}
if ("chunked".equalsIgnoreCase(response.header("Transfer-Encoding"))) {
Source source = newChunkedSource(response.request().url());
return new RealResponseBody(contentType, -1L, Okio.buffer(source));
}
long contentLength = HttpHeaders.contentLength(response);
if (contentLength != -1) {
Source source = newFixedLengthSource(contentLength);
return new RealResponseBody(contentType, contentLength, Okio.buffer(source));
}
return new RealResponseBody(contentType, -1L, Okio.buffer(newUnknownLengthSource()));
}