Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
OKHttp的责任链可以说是非常经典的设计模式了。这里说一下责任链的最后一环。
通过源码分析,发现最后一环是在 CallServerInterceptor
/** This is the last interceptor in the chain. It makes a network call to the server. */
public final class CallServerInterceptor implements Interceptor {
}
具体的请求还是要看intercept方法。
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
.......
//写入请求头
realChain.eventListener().requestHeadersStart(realChain.call());
long sentRequestMillis = System.currentTimeMillis();
httpCodec.writeRequestHeaders(request);
realChain.eventListener().requestHeadersEnd(realChain.call(), request);
Response.Builder responseBuilder = null;
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
..........
//写入请求体
if (responseBuilder == null) {
// 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();
.........
}
httpCodec.finishRequest();
//读取响应头
if (responseBuilder == null) {
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(false);
}
//读取响应体
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
.......
return response;
}
可以看出关键的实现步骤,基本都封装在了httpCodec这个类中。
@Override public void writeRequestHeaders(Request request) throws IOException {
String requestLine = RequestLine.get(
request, streamAllocation.connection().route().proxy().type());
writeRequest(request.headers(), requestLine);
}
/** Returns bytes of a request header for sending on an HTTP transport. */
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;
}
可以看出,通过for循环不断读取header的内容,写入到sink中。
sink是OKio的类,也是square公司推出的框架。
继续深入这个sink:
public Http1Codec(OkHttpClient client, StreamAllocation streamAllocation, BufferedSource source,
BufferedSink sink) {
this.client = client;
this.streamAllocation = streamAllocation;
this.source = source;
this.sink = sink;
}
发现sink是在构建http1Codec的时候就传入进来了。继续找sink的初始化位置,经过一番寻找,发现是在
RealConnection这个类中,同时初始化了Sink和Source
/** Does all the work necessary to build a full HTTP or HTTPS connection on a raw socket. */
private void connectSocket(int connectTimeout, int readTimeout, Call call,
EventListener eventListener) throws IOException {
Proxy proxy = route.proxy();
Address address = route.address();
rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
? address.socketFactory().createSocket()
: new Socket(proxy);
eventListener.connectStart(call, route.socketAddress(), proxy);
rawSocket.setSoTimeout(readTimeout);
try {
Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
} catch (ConnectException e) {
ConnectException ce = new ConnectException("Failed to connect to " + route.socketAddress());
ce.initCause(e);
throw ce;
}
try {
source = Okio.buffer(Okio.source(rawSocket));
sink = Okio.buffer(Okio.sink(rawSocket));
} catch (NullPointerException npe) {
if (NPE_THROW_WITH_NULL.equals(npe.getMessage())) {
throw new IOException(npe);
}
}
}
我们知道http请求的底层实现是socket,这里就是socket创建和连接的地方,而我们所用到的sink和source也都是在这里进行的初始化。
source = Okio.buffer(Okio.source(rawSocket));
sink = Okio.buffer(Okio.sink(rawSocket));
有没有很熟悉的感觉,我们在学java基础的时候,模拟socket连接,也是获取一个输入流,输出流。
客户端socket:
通过outputStream向socket写入数据。--------> 等同于这里的sink
通过inputStream读取socket的数据。---------> 等同于这里的source。
当然,关于为什么能写入数据到socket,从socket读取数据,这里就是更底层的东西了,能力有限,暂时不做分析。
继续回到刚才的代码
long contentLength = request.body().contentLength();
CountingSink requestBodyOut =
new CountingSink(httpCodec.createRequestBody(request, contentLength));
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
request.body() 有两种实现方式,就是我们经常用到的formbody 和 MultipartBody.
这里看writeto()方法。
private long writeOrCountBytes(@Nullable BufferedSink sink, boolean countBytes) {
long byteCount = 0L;
Buffer buffer;
if (countBytes) {
buffer = new Buffer();
} else {
buffer = sink.buffer();
}
for (int i = 0, size = encodedNames.size(); i < size; i++) {
if (i > 0) buffer.writeByte('&');
buffer.writeUtf8(encodedNames.get(i));
buffer.writeByte('=');
buffer.writeUtf8(encodedValues.get(i));
}
if (countBytes) {
byteCount = buffer.size();
buffer.clear();
}
return byteCount;
}
最后调取了这个方法。
又看到了熟悉的for循环,可以猜测一下就知道,这里就是写入请求体的方法。
看了一下encodeNames的实现:
FormBody(List<String> encodedNames, List<String> encodedValues) {
this.encodedNames = Util.immutableList(encodedNames);
this.encodedValues = Util.immutableList(encodedValues);
}
可以看到就是key和value的集合。
这里实现了把请求体写入到了sink中(sink的buffer中)。
那么什么时候,是从buffer写入到sink中呢?
bufferedRequestBody.close();
close()的时候,把buffer的数据写入到sink中,也就是写入到socket连接中。
写数据的逻辑理清之后,从socket读数据的逻辑就特别清晰了,直接看关键代码:
if (responseBuilder == null) {
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(false);
}
继续看httpCodec.readResponseHeaders(false) 这句代码:
@Override public Response.Builder readResponseHeaders(boolean expectContinue) throws IOException {
.......
StatusLine statusLine = StatusLine.parse(readHeaderLine());
.......
}
看一下statusline的成员变量:
public final Protocol protocol;
public final int code;
public final String message;
封装了响应码等信息,具体的实现还是要到readHeaderline()方法中。
private String readHeaderLine() throws IOException {
String line = source.readUtf8LineStrict(headerLimit);
headerLimit -= line.length();
return line;
}
最后找到了source.read()方法,也就是从socket中读取数据。
最后一步解析响应体数据,还是要回到CallServerIntercept的intercept()方法中:
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
看了一下httpCodec.openResponseBody(response)里面的代码:
if ("chunked".equalsIgnoreCase(response.header("Transfer-Encoding"))) {
Source source = newChunkedSource(response.request().url());
return new RealResponseBody(contentType, -1L, Okio.buffer(source));
}
只是初始化了一个chunkSource,可以看出应该是分块获取数据的。但是并没有涉及读数据的方法。
之前我看到这一直找不到在哪里读取响应体,后来忽然想到,只是封装bufferSource到responseBody里面,只有真正用的时候才会从source的buffer中读取出来。
我们一般调取响应的时候,会用response.body.string(),继续看String()方法的源码:
public final String string() throws IOException {
BufferedSource source = source();
try {
Charset charset = Util.bomAwareCharset(source, charset());
return source.readString(charset);
} finally {
Util.closeQuietly(source);
}
}
这个source()回调的就是之前的 newChunkSource.
先通过Util.bomAwareCharset(source, charset()) 获取编码格式,然后调用source.readString("UTF_8例")的格式读取字符串。
里面又回调到了RealBufferSource.readString()方法,
@Override public String readString(Charset charset) throws IOException {
if (charset == null) throw new IllegalArgumentException("charset == null");
buffer.writeAll(source);
return buffer.readString(charset);
}
先看buffer.writeAll(source):
@Override public long writeAll(Source source) throws IOException {
if (source == null) throw new IllegalArgumentException("source == null");
long totalBytesRead = 0;
for (long readCount; (readCount = source.read(this(Sink), Segment.SIZE)) != -1; ) {
totalBytesRead += readCount;
}
return totalBytesRead;
}
这里是在Buffer里面实现的,又是熟悉的for循环。
source.read(sink) == sink.write(source)
sink.write()是一种高效的读写交互方式,底层是通过链表重指向,而不是数据拷贝实现的
@Override public void write(Buffer source, long byteCount) {
// Move bytes from the head of the source buffer to the tail of this buffer
}
然后继续回到RealBufferSource.readString()源码,buffer.readString(charset)
@Override public String readString(long byteCount, Charset charset) throws EOFException {
............
Segment s = head;
............
String result = new String(s.data, s.pos, (int) byteCount, charset);
............
}
如果只是文件的读写,不涉及socket交互就可以分析具体实现了,但是能力有限,再深入应该就到socket底层了,猜想实现原理是:
用户-----》sink写入请求-------》socket发送请求,将返回数据复制给buffer中的head-------》获取source------》读取head。
完