在分析完ConnectInterceptor拦截器后,终于来到了最后一个拦截器CallServerInterceptor,其主要作用就是向服务器发送请求,最终返回response。
还是先看下它的intercept方法:
public Response intercept(Chain chain) throws IOException {
final RealInterceptorChain realChain = (RealInterceptorChain) chain;
// 获取对象
Call call = realChain.call();
final HttpCodec httpCodec = realChain.httpStream();
StreamAllocation streamAllocation = realChain.streamAllocation();
RealConnection connection = (RealConnection) realChain.connection();
Request request = realChain.request();
long sentRequestMillis = System.currentTimeMillis();
realChain.eventListener().requestHeadersStart(call);
// 发送状态行和头部数据到服务端
httpCodec.writeRequestHeaders(request);
realChain.eventListener().requestHeadersEnd(call, request);
Response.Builder responseBuilder = null;
// 判断是否有请求实体的请求(不是GET请求)
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",这个请求头字段的作用是在发送RequestBody前向服务器确认是否接受RequestBody,如果服务器不接受也就没有发送的必要了。
// 有这个字段,相当于一次简单的握手操作,会等待拿到服务器返回的ResponseHeaders之后再继续,如果服务器接收RequestBody,会返回null。
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
httpCodec.flushRequest();
realChain.eventListener().responseHeadersStart(call);
// 服务器返回
responseBuilder = httpCodec.readResponseHeaders(true);
}
// 服务器同意接收,开始向流中写入RequestBody
if (responseBuilder == null) {
if (request.body() instanceof DuplexRequestBody) {
// Prepare a duplex body so that the application can send a request body later.
httpCodec.flushRequest();
CountingSink requestBodyOut = new CountingSink(httpCodec.createRequestBody(request, -1L));
((DuplexRequestBody) request.body()).foldSink(requestBodyOut);
} else {
// Write the request body if the "Expect: 100-continue" expectation was met.
realChain.eventListener().requestBodyStart(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(call, requestBodyOut.successfulCount);
}
} 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();
}
}
if (!(request.body() instanceof DuplexRequestBody)) {
// 调用了sink.flush();
httpCodec.finishRequest();
}
// 读取头部信息、状态码等
if (responseBuilder == null) {
realChain.eventListener().responseHeadersStart(call);
responseBuilder = httpCodec.readResponseHeaders(false);
}
// 构建Response, 写入原请求,握手情况,请求时间,得到的响应时间
responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis());
Internal.instance.initCodec(responseBuilder, httpCodec);
Response response = responseBuilder.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);
responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis());
Internal.instance.initCodec(responseBuilder, httpCodec);
response = responseBuilder.build();
code = response.code();
}
realChain.eventListener().responseHeadersEnd(call, response);
// ResponseBody
if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
// 设置一个空的Body
response = response.newBuilder()
.body(Util.EMPTY_RESPONSE)
.build();
} else {
// 读取Body信息
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
// 如果设置了连接关闭,则断开连接
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();
}
//HTTP 204(no content) 代表响应报文中包含若干首部和一个状态行,但是没有实体的主体内容。
//HTTP 205(reset content) 表示响应执行成功,重置页面(Form表单),方便用户下次输入
//这里做了同样的处理,就是抛出协议异常。
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、获取request、httpCodec等对象。
2、通过httpCodec发送状态行和头部数据到服务端。
3、判断是否是有请求实体的请求,如果是,判断当前是否有设置Expect: 100-continue请求头。
4、往流中写入请求实体内容。
5、读取服务器返回的头部信息、状态码等,构建responseBuilder对象并返回。
6、通过responseBuilder对象来构建response对象。
7、判断当前状态码,如果是100,则重新请求一次,得到response对象。
8、给Response对象设置body。
9、判断是否需要断开连接。
发送状态行和头部数据到服务端
httpCodec.writeRequestHeaders(request);
这里我们拿Http1Codec来分析:
public void writeRequestHeaders(Request request) throws IOException {
// 获取状态行数据,返回类似"GET / HTTP/1.1"的字符串
String requestLine = RequestLine.get(
request, streamAllocation.connection().route().proxy().type());
writeRequest(request.headers(), requestLine);
}
public void writeRequest(Headers headers, String requestLine) throws IOException {
if (state != STATE_IDLE) throw new IllegalStateException("state: " + state);
// sink可看成是个封装了socket连接的输出流,将状态栏和头部数据输出到服务端
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;
}
这里使用了okio的Sink对象(可以看成封装了socket连接的输出流,详细可看okio)来将状态行和头部数据传到服务端。
读取服务器返回的头部信息、状态码并返回responseBuilder对象
@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对象
Response.Builder responseBuilder = new Response.Builder()
.protocol(statusLine.protocol)
.code(statusLine.code)
.message(statusLine.message)
.headers(readHeaders());
if (expectContinue && statusLine.code == HTTP_CONTINUE) {
return null;
} else if (statusLine.code == HTTP_CONTINUE) {
state = STATE_READ_RESPONSE_HEADERS;
return responseBuilder;
}
state = STATE_OPEN_RESPONSE_BODY;
return responseBuilder;
} catch (EOFException e) {
// Provide more context if the server ends the stream before sending a response.
throw new IOException("unexpected end of stream on " + streamAllocation, e);
}
}
获取响应行数据,然后用响应行的数据构建Response.Builder对象,如果状态码为100且expectContinue为true,则会返回个null对象。如果只是状态码为100,则直接返回个Response.Builder对象,返回后往下也会去处理100这个状态。
总结
该拦截器HttpCodec对象中的source、Sink对象来对向服务器发送和接收状态行或实体内容,进行些状态码判断、从而完成对Response对象的构建,再返回到上一级拦截器做处理。