CallServerInterceptor
系列
OKHttp3–详细使用及源码分析系列之初步介绍【一】
OKHttp3–流程分析 核心类介绍 同步异步请求源码分析【二】
OKHttp3–Dispatcher分发器源码解析【三】
OKHttp3–调用对象RealCall源码解析【四】
OKHttp3–拦截器链RealInterceptorChain源码解析【五】
OKHttp3–重试及重定向拦截器RetryAndFollowUpInterceptor源码解析【六】
OKHttp3–桥接拦截器BridgeInterceptor源码解析及相关http请求头字段解析【七】
OKHttp3–缓存拦截器CacheInterceptor源码解析【八】
OKHttp3-- HTTP缓存机制解析 缓存处理类Cache和缓存策略类CacheStrategy源码分析 【九】
通过ConnectInterceptor源码掌握OKHttp3网络连接原理 呕心沥血第十弹【十】
前言
这篇文章将对OKHttp最后一个拦截器进行解析,总算快要结束了;上一篇文章讲到连接拦截器,即客户端已经与服务端进行了连接,那接下来的操作自然就是发送接收数据了,看看官网的注释
这是拦截器链上的最后一个拦截器,向服务器发起网络访问
那接下来就从源码看看它是如何实现发送请求数据,接收响应数据的
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();
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);
}
if (responseBuilder == null) {
// Write the request body if the "Expect: 100-continue" expectation was met.
Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
} 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) {
responseBuilder = httpCodec.readResponseHeaders(false);
}
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();
}
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
return response;
}
这里代码我们分开看
第一部分:获取网络组件
RealInterceptorChain realChain = (RealInterceptorChain) chain;
HttpCodec httpCodec = realChain.httpStream();
StreamAllocation streamAllocation = realChain.streamAllocation();
RealConnection connection = (RealConnection) realChain.connection();
Request request = realChain.request();
这里通过拦截器链拿到了四个对象
- HttpCodec : 封装I/O操作的对象,根据HTTP/1.x,HTTP/2版本,有两个对应的实现类Http1Codec、Http2Codec,可以简单理解为编码我们的请求,解码返回的响应
- StreamAllocation:封装了一次网络操作需要的组件,为一个请求分配一个stream
- RealConnection: 封装了一次Socket与服务器连接的对象,在OKHttp中将一次连接抽象为Connection,而它是其实现类
- Request:封装用户的请求信息的对象
第二部分:发送请求
long sentRequestMillis = System.currentTimeMillis();
httpCodec.writeRequestHeaders(request);
Response.Builder responseBuilder = null;
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
httpCodec.flushRequest();
responseBuilder = httpCodec.readResponseHeaders(true);
}
if (responseBuilder == null) {
Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
} else if (!connection.isMultiplexed()) {
streamAllocation.noNewStreams();
}
}
httpCodec.finishRequest();
这部分主要是用来发送请求
- 获取发送请求的时间戳,并将请求头信息写入
- 检测是否为有body的请求方法,接下来有两个判断,分开讲述
第一个if判断的意思是:如果请求头中包含【Expect: 100- Continue】,那么就发送100-continue的请求,然后获取服务器的响应,看服务器是否同意接受请求体,如果服务器的响应码是HTTP_CONTINUE,即100,那这个responseBuilder 即为null
第二个判断:上面一个判断可以知道如果服务器同意接受请求体数据,那么responseBuilder 为null,接下来就构建输出流BufferedSink对象,将请求体数据写到其缓冲区
第三个判断:也就是responseBuilder 不为null,意思是服务器不同意接受该请求体,且同时这个连接不支持多路复用,也就是说这是一个Http/1.x的请求,那么我们就需要标记该连接不能再被复用,同时关闭相关的Socket
接下来就是将缓存区的数据写到Socket中,发送出去,到这里发送请求已经完成
第三部分:构建响应
if (responseBuilder == null) {
responseBuilder = httpCodec.readResponseHeaders(false);
}
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
首先获取响应头部信息,然后构建响应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;
- 如果使用websocket且响应码是101,那么构建一个空响应体的response 对象;否则就根据响应体构建真正的响应对象
- 如果请求头或者响应头部包含【Connection: close】,意味着该连接不需要保活,那就关闭该连接以及释放相应的资源
- 如果响应码是204或者205,且响应体长度大于0,也就意味着该次请求成功,但是服务器只返回了响应行和响应头信息,没有响应体,可是这里拿到的响应体长度居然大于0,那就说明出问题了,所以需要抛出一个协议异常
总结
可以看到最后一个拦截器的代码比较简单,当然了你要是没有看前面的系列文章,可能会觉得有点懵;拦截器链上的五个拦截器分析完了,后续的文章将会介绍下OKHttp中有关WebSocket的知识点