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的責(zé)任鏈可以說是非常經(jīng)典的設(shè)計(jì)模式了英岭。這里說一下責(zé)任鏈的最后一環(huán)。
通過源碼分析,發(fā)現(xiàn)最后一環(huán)是在 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();
//讀取響應(yīng)頭
if (responseBuilder == null) {
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(false);
}
//讀取響應(yīng)體
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
.......
return response;
}
可以看出關(guān)鍵的實(shí)現(xiàn)步驟,基本都封裝在了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循環(huán)不斷讀取header的內(nèi)容,寫入到sink中谎替。
sink是OKio的類偷溺,也是square公司推出的框架。
繼續(xù)深入這個sink:
public Http1Codec(OkHttpClient client, StreamAllocation streamAllocation, BufferedSource source,
BufferedSink sink) {
this.client = client;
this.streamAllocation = streamAllocation;
this.source = source;
this.sink = sink;
}
發(fā)現(xiàn)sink是在構(gòu)建http1Codec的時候就傳入進(jìn)來了钱贯。繼續(xù)找sink的初始化位置挫掏,經(jīng)過一番尋找,發(fā)現(xiàn)是在
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請求的底層實(shí)現(xiàn)是socket尉共,這里就是socket創(chuàng)建和連接的地方,而我們所用到的sink和source也都是在這里進(jìn)行的初始化弃锐。
source = Okio.buffer(Okio.source(rawSocket));
sink = Okio.buffer(Okio.sink(rawSocket));
有沒有很熟悉的感覺袄友,我們在學(xué)java基礎(chǔ)的時候,模擬socket連接霹菊,也是獲取一個輸入流剧蚣,輸出流。
客戶端socket:
通過outputStream向socket寫入數(shù)據(jù)旋廷。--------> 等同于這里的sink
通過inputStream讀取socket的數(shù)據(jù)鸠按。---------> 等同于這里的source。
當(dāng)然柳洋,關(guān)于為什么能寫入數(shù)據(jù)到socket待诅,從socket讀取數(shù)據(jù),這里就是更底層的東西了熊镣,能力有限卑雁,暫時不做分析。
繼續(xù)回到剛才的代碼
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() 有兩種實(shí)現(xiàn)方式绪囱,就是我們經(jīng)常用到的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;
}
最后調(diào)取了這個方法。
又看到了熟悉的for循環(huán)鬼吵,可以猜測一下就知道扣甲,這里就是寫入請求體的方法。
看了一下encodeNames的實(shí)現(xiàn):
FormBody(List<String> encodedNames, List<String> encodedValues) {
this.encodedNames = Util.immutableList(encodedNames);
this.encodedValues = Util.immutableList(encodedValues);
}
可以看到就是key和value的集合。
這里實(shí)現(xiàn)了把請求體寫入到了sink中(sink的buffer中)琉挖。
那么什么時候启泣,是從buffer寫入到sink中呢?
bufferedRequestBody.close();
close()的時候示辈,把buffer的數(shù)據(jù)寫入到sink中寥茫,也就是寫入到socket連接中。
寫數(shù)據(jù)的邏輯理清之后矾麻,從socket讀數(shù)據(jù)的邏輯就特別清晰了纱耻,直接看關(guān)鍵代碼:
if (responseBuilder == null) {
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(false);
}
繼續(xù)看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;
封裝了響應(yīng)碼等信息,具體的實(shí)現(xiàn)還是要到readHeaderline()方法中险耀。
private String readHeaderLine() throws IOException {
String line = source.readUtf8LineStrict(headerLimit);
headerLimit -= line.length();
return line;
}
最后找到了source.read()方法弄喘,也就是從socket中讀取數(shù)據(jù)。
最后一步解析響應(yīng)體數(shù)據(jù)甩牺,還是要回到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蘑志,可以看出應(yīng)該是分塊獲取數(shù)據(jù)的。但是并沒有涉及讀數(shù)據(jù)的方法柴灯。
之前我看到這一直找不到在哪里讀取響應(yīng)體卖漫,后來忽然想到,只是封裝bufferSource到responseBody里面赠群,只有真正用的時候才會從source的buffer中讀取出來。
我們一般調(diào)取響應(yīng)的時候旱幼,會用response.body.string(),繼續(xù)看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()回調(diào)的就是之前的 newChunkSource.
先通過Util.bomAwareCharset(source, charset()) 獲取編碼格式查描,然后調(diào)用source.readString("UTF_8例")的格式讀取字符串。
里面又回調(diào)到了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里面實(shí)現(xiàn)的冬三,又是熟悉的for循環(huán)。
source.read(sink) == sink.write(source)
sink.write()是一種高效的讀寫交互方式缘缚,底層是通過鏈表重指向勾笆,而不是數(shù)據(jù)拷貝實(shí)現(xiàn)的
@Override public void write(Buffer source, long byteCount) {
// Move bytes from the head of the source buffer to the tail of this buffer
}
然后繼續(xù)回到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交互就可以分析具體實(shí)現(xiàn)了窝爪,但是能力有限,再深入應(yīng)該就到socket底層了齐媒,猜想實(shí)現(xiàn)原理是:
用戶-----》sink寫入請求-------》socket發(fā)送請求蒲每,將返回?cái)?shù)據(jù)復(fù)制給buffer中的head-------》獲取source------》讀取head。
完