源码地址:https://github.com/square/okhttp
不知不觉已经来到了最后一个拦截器,前面做了各种处理,也建立了连接。接下来的CallServerInterceptor,应该就是对数据进行交换、读取以及构建结果的类了。
不过其实在这个拦截器之前,还可以自定义networkInterceptors。
在Okhttp的拦截器链条里面有两个地方可以自定义拦截:
最开始的时候(Interceptors):对发出去的请求做最初的处理,以及在拿到最后Reponse时候做最后的处理;
最后数据交换前(networkInterceptors):对发出去的请求做最后的处理,以及在拿到结果时候做最初的处理。
我们可以自定义拦截器,去处理我们需要做的事情。
下面言归正传:
intercept
和其他拦截器一样,最重要的方法就是这个intercept方法,拦截方法。
这个拦截方法主要步骤:
获取几个前面已经创建的重要类(httpCodec, streamAllocation, request);
先向sink(OutputStream)中写头信息(sink, 在创建连接时候已经创建好);
判断是否有请求体,如有,走4,5的操作,没有直接到6;
如果头部添加了"100-continue", 相对于一次见到的握手操作,只有拿到服务的结果再继续;
当“100-continue”成功或者不需要这个简单握手的,写入请求实体;
finishRequest( 实际是调用了 sink.flush(), 来刷数据 )
读取头部信息(通过source(InputStream), 读取头部信息,状态码等)
构建Response, 写入原请求,握手情况,请求时间,得到的结果时间
通过Response 状态码判断以及是否webSocket判断,是否返回一个空的body, 或者读取Body信息(通过 source(InputStream) 读取);
读取到请求时的连接close ,或者服务器返回的 close, 进行断开操作;
对于204,205的特殊状态码进行处理。
有两个比较重要的类,就是sink以及source, 这个两个是Okio里面的一个重要概念,相当于我们熟悉的 OutputStream 以及 InputStream输入输出流。这里就不展开,有兴趣的可以了解一下 Okio这个 I/O 库。这两个类都是在创建连接的时候就已经建立,而在个拦截器里面就是对数据进行交换。
下面是源码阅读:
@Override public Response intercept(Chain chain) throws IOException {
// 1.获取几个前面拦截方法创建的,重要类
HttpCodec httpCodec = ((RealInterceptorChain) chain).httpStream();
StreamAllocation streamAllocation = ((RealInterceptorChain) chain).streamAllocation();
Request request = chain.request();
long sentRequestMillis = System.currentTimeMillis();
//2. 先向sink(OutputStream)中写头信息
httpCodec.writeRequestHeaders(request);
Response.Builder responseBuilder = null;
// 3.判断是否有请求实体的请求,用method判断
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.
//4. 如果头部添加了"100-continue", 相对于一次见到的握手操作,只有拿到服务的结果再继续
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
httpCodec.flushRequest();
responseBuilder = httpCodec.readResponseHeaders(true);
}
//5. 当前面的"100-continue",需要握手,但又握手失败,这个时候responseBuilder不是空的
// Write the request body, unless an "Expect: 100-continue" expectation failed.
if (responseBuilder == null) {
Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
// 回调RequestBody的writeTo,写相应的数据
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
}
}
//6. 这里也是调用了一次 sink.flush()
httpCodec.finishRequest();
//7. 读取头部信息,状态码,信息等
if (responseBuilder == null) {
responseBuilder = httpCodec.readResponseHeaders(false);
}
//8. 构建Response, 写入原请求,握手情况,请求时间,得到的结果时间
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
int code = response.code();
//9. 通过状态码判断以及是否webSocket判断,是否返回一个空的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();
}
//10 .如果设置了连接 close ,断开连接
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();
}
//11. 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;
}
总结:
这个拦截器主要是通过连接好的通道进行数据的交换,有些细节没有展开,只对基本的流程进行了学习记录。留下两个可研究的点:
HttpCodec是一个接口,其实有两个实现类,分别是HttpCodec1、HttpCodec2, 对应着http1 的读写数据和 http2的 读写数据。两者是有差异的;
还有前面提到的Okio,可以去了解一下。
系列:
OKhttp源码学习(一)—— 基本请求流程
OKhttp源码学习(二)—— OkHttpClient
OKhttp源码学习(三)—— Request, RealCall
OKhttp源码学习(四)—— RetryAndFollowUpInterceptor拦截器
OKhttp源码学习(五)—— BridgeInterceptor拦截器
OKhttp源码学习(六)—— CacheInterceptor拦截器
OKhttp源码学习(七)—— ConnectInterceptor拦截器
OKhttp源码学习(九)—— 任务管理(Dispatcher)