ConnectionInterceptor 连接拦截器
用来(建立可用的链接)获取connection对象 以及Httpcodec对象
/*打开与目标服务器的连接,然后继续执行下一个拦截器。*/
public final class ConnectInterceptor implements Interceptor {
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
StreamAllocation streamAllocation = realChain.streamAllocation();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
// 1 创建httpcodec 对象 对应http1 和http2
// httpcodec 对HTTP请求进行编码并解码HTTP响应。 创建httpcodec是重点
// 因为再下一个拦截器里CallServiceInterceptor里进行网络访问
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
// 2 创建 connection 连接
RealConnection connection = streamAllocation.connection();
//调用下一个拦截器 一般是CallServerInterceptor
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
}
-
这里调用了 重定向拦截器中传递过来的streamAllocation的newStream(分配流)创建了一个 HttpCodec 的对象。
- HttpCodec 是一个抽象类,其实现类分别是 Http1Codec 和 Http2Codec 。相对应的就是 HTTP/1.1 和 HTTP/2.0 。
-
我们来看下 streamAllocation.newStream 做了什么:
public HttpCodec newStream(
OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
int connectTimeout = chain.connectTimeoutMillis();
int readTimeout = chain.readTimeoutMillis();
int writeTimeout = chain.writeTimeoutMillis();
int pingIntervalMillis = client.pingIntervalMillis();
boolean connectionRetryEnabled = client.retryOnConnectionFailure();
try {
//在连接池里找到一个可用连接 然后创建一个httpcodec
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);
synchronized (connectionPool) {
codec = resultCodec;
return resultCodec;
}
} catch (IOException e) {
throw new RouteException(e);
}
}
- 可看到是通过resultConnection.newCodec()创建httpcodec对象
看一下这个方法里执行了什么
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());
//类似inputStream
source.timeout().timeout(chain.readTimeoutMillis(), MILLISECONDS);
//类似WriteStream
sink.timeout().timeout(chain.writeTimeoutMillis(), MILLISECONDS);
return new Http1Codec(client, streamAllocation, source, sink);
}
}
- 在这里通过source 和sink 创建 http1codec 对象
- source 类似inputStream
- sink 类似WriteStream
总结
- 连接拦截器作用
- 通过streamAllocation 获取httpcodec 和 connection连接
- 调用proceed方法执行下一个拦截器,获取response 并返回
- 此时就到了 CallServerInterceptor(真正联网的步骤)
CallServerInterceptor 呼叫服务拦截器
-
CallServerInterceptor(真正联网的步骤)
-
将http请求写进网络的IO流当中,并且从网络IO流当中读取服务端返回给客户端的数据。
看看里面都做了什么
/** This is the last interceptor in the chain. It makes a network call to the server. */
public final class CallServerInterceptor implements Interceptor {
....简化代码
@Override 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吗 这里的写入最终就是通过sink
httpCodec.writeRequestHeaders(request);
realChain.eventListener().requestHeadersEnd(call, request);
Response.Builder responseBuilder = null;
//检查是否为有 body 的请求方法
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 在请求头中,那么要等服务器的响应
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
httpCodec.flushRequest();
realChain.eventListener().responseHeadersStart(call);
responseBuilder = httpCodec.readResponseHeaders(true);
}
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));
// 写入请求体
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
} 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)) {
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);
// 如果为 web socket 且状态码是 101 ,那么 body 为空
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 {
//读取body
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
// 如果请求头中有 close 那么断开连接
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;
}
}
作用总结
- 该拦截器主要是将请求 写入 网络io流
- 读取服务端返回给客户端的信息,构造response并返回response给上一个拦截器
Okhttp 底层实现
- 在 CallServerInterceptor 中关于请求和响应部分都是通过 HttpCodec 来实现的。
- 而在 HttpCodec 内部又是通过 sink 和 source 来实现的。
- 所以说到底还是 IO 流在起作用。是okio 对Socket流操作的封装 Okhttp 其实是基于Socket进行通讯的