源码分析基于 3.14.4
关键字:拦截器CallServerInterceptor
https://www.jianshu.com/p/4b0fde7f6b4c上一篇,讲了ConnectInterceptor拦截器的作用。
这次分析CallServerInterceptor
访问服务器拦截器,这是真正处理请求以及响应的地方。
还是看CallServerInterceptor.intercept
(1)exchange.writeRequestHeaders(request),发送request(请求行以及Heads),本质上就是把request请求行以及Heads信息拼接成字符串写入通信管道,拼接的字符串如下;
/***
* GET /user HTTP/1.1 -------------------请求行
* Host:api.github.com -------------------Heads
* Content-Type:text/plain -------------------Heads
* Content-Length:400 -------------------Heads
*/
(2)接下来以GET请求为例
因为GET请求没有body,所以HttpMethod.permitsRequestBody(request.method()) && request.body() != null条件不成立,则执行exchange.noRequestBody(),把上面拼接的字符串flush出去;
(3)exchange.readResponseHeaders(false),读取响应头;
(4)responseBuilder.build(),构建response;
(5)exchange.openResponseBody(response),读取解析响应体body,解析后的body只是一个Buffer,连字符串都不是;
(16)如果请求头或者响应头带有Connection:closed信息,那么 exchange.noNewExchangesOnConnection()标记这个Connection可以被回收了;如果客户端希望keep-alive,但是服务器不希望,Connection也是要被标记的;
(7)如果响应码是204或者205,但是响应体body有内容,也就是(code == 204 || code == 205) && response.body().contentLength() > 0成立,则抛出ProtocolException异常;
Tips:204、205类似交互信息,主要是服务端通知客户端做什么,不会带有body的,例如204,告知客户端数据没有发生变化,可以用原来的数据;
(8)CallServerInterceptor没有下一个拦截器也没有后置处理,所以最后把response返回给ConnectInterceptor;
@Override public Response intercept(Chain chain) throws IOException {
......
exchange.writeRequestHeaders(request);
......
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
......
} else {
exchange.noRequestBody();
}
......
if (responseBuilder == null) {
responseBuilder = exchange.readResponseHeaders(false);
}
Response response = responseBuilder
.request(request)
.handshake(exchange.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
......
response = response.newBuilder()
.body(exchange.openResponseBody(response))
.build();
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
exchange.noNewExchangesOnConnection();
}
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.writeRequestHeaders(request),这个方法是把请求行以及请求头拼接成字符串发给服务器,它内部会调到Http1ExchangeCodec.writeRequestHeaders
Http1ExchangeCodec是处理HTTP 1.1的,Http2ExchangeCodec是处理HTTP 2.0的;
Http1ExchangeCodec.writeRequestHeaders
主要看RequestLine.get方法;
@Override public void writeRequestHeaders(Request request) throws IOException {
String requestLine = RequestLine.get(
request, realConnection.route().proxy().type());
writeRequest(request.headers(), requestLine);
}
RequestLine.get
如果使用了代理并且是HTTP请求,那么请求行path路径是包含host(request.url()),否则path路径是不包含host的(requestPath(request.url()));
public static String get(Request request, Proxy.Type proxyType) {
......
if (includeAuthorityInRequestLine(request, proxyType)) {
result.append(request.url());
} else {
result.append(requestPath(request.url()));
}
......
}
//普通请求行
GET /user HTTP/1.1
//使用代理并且是HTTP的请求行
GET www.baidu.com/user HTTP/1.1
如果请求带Expect:100-continue如何处理?
Expect:100-continue,表示客户端期望发送body,如果响应码为100,表示可以发,否则不可以发。
(1)调用exchange.writeRequestHeaders(request)发送请求以后,如果是POST请求并且带Body,那么HttpMethod.permitsRequestBody(request.method()) && request.body() != null条件成立,进入if逻辑;
(2)请求带Expect:100-continue,那么"100-continue".equalsIgnoreCase(request.header("Expect"))条件成立, responseBuilder = exchange.readResponseHeaders(true)读取响应头;
(3)如果服务器返回响应码100,表示可以接收数据,那么上面获取的responseBuilder 就是null,调用request.body().writeTo(bufferedRequestBody)把body发送出去;
(4)如果服务器返回响应码不是100,表示不可以接收数据,那么后续读取响应信息就可以了;
exchange.writeRequestHeaders(request);
boolean responseHeadersStarted = false;
Response.Builder responseBuilder = null;
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
exchange.flushRequest();
......
responseBuilder = exchange.readResponseHeaders(true);
}
if (responseBuilder == null) {
......
request.body().writeTo(bufferedRequestBody);
} else {
......
}
} else {
exchange.noRequestBody();
}
总结
(1)CallServerInterceptor,访问服务器拦截器,这是真正处理请求以及响应的地方;
(2)GET或POST不带body请求访问流程,发送请求Request(拼接请求行以及请求头)、读取响应头、解析响应体;
(3)POST带body访问流程,发送请求Request(拼接请求行以及请求头),如果请求头带有Expect:100-continue,那么读取响应头,响应码为100那就body,否则读取响应头、解析响应体;如果不带Expect:100-continue,那就发送请求体、读取响应头、解析响应体;
以上分析有不对的地方,请指出,互相学习,谢谢哦!
网友评论