okHttp3源码解析(三)-CallServerInterceptor

前言

本篇主要看下CallServerInterceptor, 关于他在整个请求中起到的作用, okHttp已经告诉我们, 可以看出它作为责任链中的最后一个环节, 承担了对服务端进行请求的工作.

This is the last interceptor in the chain. It makes a network call to the server.

正文

okHttp对于对服务钱的请求与相应, 底层都是通过okiosocket进行操作.
老样子, 我们直接上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
// 在 ConnectInterceptor创建
HttpCodec httpCodec = realChain.httpStream();
// 在 RetryAndFollowUpInterceptor创建
StreamAllocation streamAllocation = realChain.streamAllocation();
// 在 ConnectInterceptor获取
RealConnection connection = (RealConnection) realChain.connection();
Request request = realChain.request();
// 发送请求时间戳为当前时间戳
long sentRequestMillis = System.currentTimeMillis();

realChain.eventListener().requestHeadersStart(realChain.call());
// 发送请求头, 通过Okio
httpCodec.writeRequestHeaders(request);
realChain.eventListener().requestHeadersEnd(realChain.call(), request);

Response.Builder responseBuilder = null;
// GET or HEAD 不需要
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"))) {
// 请求刷新, okio处理
httpCodec.flushRequest();
realChain.eventListener().responseHeadersStart(realChain.call());
// 构建Response.Builder, 当response状态为100, 则返回null
responseBuilder = httpCodec.readResponseHeaders(true);
}

if (responseBuilder == null) {
// head成功响应的情况下
// 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()) {
// HTTP/1请求协议, 而且初次握手失败
// 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();
}
}

// flush
httpCodec.finishRequest();
// 如果是GET请求, 或者需要'100- continue'握手成功的情况下
if (responseBuilder == null) {
realChain.eventListener().responseHeadersStart(realChain.call());
// 构建responseBuilder
responseBuilder = httpCodec.readResponseHeaders(false);
}
// 获取响应
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
// 即使我们没有请求, 服务端也会发送一个100-continue
// 重新读取真正的响应
responseBuilder = httpCodec.readResponseHeaders(false);
// 构建response
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) {
// 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: No Content
// 205: Reset Content
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}

return response;
}

请求

我们来一步步看下代码

1
2
3
4
5
6
7
8
RealInterceptorChain realChain = (RealInterceptorChain) chain;
// 在 ConnectInterceptor创建
HttpCodec httpCodec = realChain.httpStream();
// 在 RetryAndFollowUpInterceptor创建
StreamAllocation streamAllocation = realChain.streamAllocation();
// 在 ConnectInterceptor获取
RealConnection connection = (RealConnection) realChain.connection();
Request request = realChain.request();

CallServerInterceptor在, 核心工具类就是HttpCodec, 他的初始化我们在上篇ConnectInterceptor解析中可以看到.这里的步骤其实就是工具的准备.

1
httpCodec.writeRequestHeaders(request);

我们以HTTP/1.1协议来看, 那么具体要看Http1Codec中的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Override public void writeRequestHeaders(Request request) throws IOException {
// requestLine 就是我们请求报文内容的首行, 譬如 "GET / HTTP/1.1"
String requestLine = RequestLine.get(
request, streamAllocation.connection().route().proxy().type());
writeRequest(request.headers(), requestLine);
}

final BufferedSink sink;
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;
}

可以看到在writeRequest中的方法, 就是针对BufferedSink对象的写操作, 在上篇ConnectInterceptor中进行三次握手连接的时候, 会进行初始化的工作, 我们会发现他是针对Socket的包装, 可以看做是Socket的输出流, 所以这里相当于是Socket的写入动作, 可以看出来, 这里会请求发送header.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
// 请求刷新, okio处理
httpCodec.flushRequest();
realChain.eventListener().responseHeadersStart(realChain.call());
// 构建Response.Builder, 当response状态为100, 则返回null
responseBuilder = httpCodec.readResponseHeaders(true);
}

if (responseBuilder == null) {
// head成功响应的情况下
// 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()) {
// HTTP/1请求协议, 而且初次握手失败
// 禁止同主机请求新流的分配
streamAllocation.noNewStreams();
}
}

当请求首部字段包含Expect:100-continue, 一般在请求上传大容量body或者是需要验证的时候, 这样避免大文件传送失败带来的带宽浪费。所以需要判断请求体不为空的情况下, 并且请求首部包含该字段的情况下, 刷新请求, 构建responseBuilder对象, 如果这个时候服务端响应成功, 则responseBuilder对象为null, 并进行body的请求;而如果第一次请求头响应失败的情况下, 那么如果是HTTP/1的请求协议下, 就会禁止同host的请求新流的分配.

响应

以上就是请求的过程, 然后看响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 如果是GET请求, 或者需要'100- continue'握手成功的情况下
if (responseBuilder == null) {
// 构建responseBuilder
responseBuilder = httpCodec.readResponseHeaders(false);
}
// 获取响应
Response response = responseBuilder
// 原请求
.request(request)
// 握手情况
.handshake(streamAllocation.connection().handshake())
// 请求时间
.sentRequestAtMillis(sentRequestMillis)
// 响应时间
.receivedResponseAtMillis(System.currentTimeMillis())
.build();

在请求成功的情况下, 会重新构建responseBuilder对象, 通过它来构建响应报文.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int code = response.code();
if (code == 100) {
// 即使我们没有请求, 服务端也会发送一个100-continue
// 重新读取真正的响应
responseBuilder = httpCodec.readResponseHeaders(false);
// 构建response
response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();

code = response.code();
}

如果客户端响应100状态码, 这个时候, 我们就需要重新读取获取真正的内容响应.

剩下的代码就是一些异常的处理, 这里就不做分析了.