OkHttp請(qǐng)求的核心處理就是這一系列的攔截鏈
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
//建立一個(gè)完整的攔截器堆棧。
List<Interceptor> interceptors = new ArrayList<>();
//OkHttp初始化添加的攔截器
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);
return chain.proceed(originalRequest);
}
這里面看到 ,第一個(gè)add的是client里面自己定義的攔截鏈集合和如果不是webSocket的話能添加客戶端網(wǎng)絡(luò)的攔截鏈,然后后面依次添加的攔截器有
RetryAndFollowUpInterceptor 重試和跟進(jìn)攔截器
BridgeInterceptor 橋攔截器
CacheInterceptor 緩存攔截器
ConnectInterceptor 鏈接攔截器
CallServerInterceptor 呼叫服務(wù)攔截器
RealInterceptorChain 實(shí)際攔截鏈,其中攜帶整個(gè)攔截器鏈:所有應(yīng)用攔截器,OkHttp核心,所有網(wǎng)絡(luò)攔截器,最后是網(wǎng)絡(luò)呼叫者。
這種鏈?zhǔn)秸{(diào)用在設(shè)計(jì)模式里有個(gè)叫責(zé)任鏈模式,這個(gè)的話我們進(jìn)去看下源碼印證一下,因?yàn)樗@個(gè)集合是一個(gè)帶有泛型的,所以直接看他的泛型
/**
* Observes, modifies, and potentially short-circuits requests going out and the corresponding
* responses coming back in. Typically interceptors add, remove, or transform headers on the request
* or response.
* 觀察,修改和潛在地短路請(qǐng)求以及相應(yīng)的響應(yīng)。通常,攔截器會(huì)在請(qǐng)求或響應(yīng)中添加,刪除或變換頭文件。
*/
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
Connection connection();
}
}
然后再看RealInterceptorChain的代碼因?yàn)楹竺嬲{(diào)用的是他的proceed方法,可以知道他肯定是實(shí)現(xiàn)了Interceptor的Chain,我們先來看他的初始化的過程
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest);
直接看他的構(gòu)造函數(shù)
public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
HttpCodec httpCodec, RealConnection connection, int index, Request request) {
this.interceptors = interceptors;
this.connection = connection;
this.streamAllocation = streamAllocation;
this.httpCodec = httpCodec;
this.index = index;
this.request = request;
}
為null的先不管,看那些不為null的, 傳過來了一個(gè)請(qǐng)求鏈,index是0,還有request 接著調(diào)用了RealInterceptorChain的proceed方法
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// If we already have a stream, confirm that the incoming request will use it.
//如果我們已經(jīng)有一個(gè)流,請(qǐng)確認(rèn)傳入的請(qǐng)求將使用它
if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}
// If we already have a stream, confirm that this is the only call to chain.proceed().
//如果我們已經(jīng)有一個(gè)流,確認(rèn)這是唯一的call在chain.proceed()中
if (this.httpCodec != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// Call the next interceptor in the chain.
// 調(diào)用鏈中的下一個(gè)攔截器
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
// 確認(rèn)下一個(gè)攔截器將其所需的調(diào)用鏈接到chain.proceed()
if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
return response;
}
這里面可以看到最后調(diào)用的是proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,RealConnection connection)
首先是幾個(gè)判斷,流程如下
- httpCodec不為空并且connection的url不支持這個(gè)請(qǐng)求的Url,那么拋出異常
- httpCodec不為空且當(dāng)前的請(qǐng)求數(shù)大于1,拋出異常
- 開吃調(diào)用攔截鏈的邏輯
- httpCodec不為空并且當(dāng)前索引加1小于請(qǐng)求鏈的長度,下一個(gè)請(qǐng)求的call不為1 ,拋出異常。
- response為空,拋出異常
- return response
這塊邏輯 會(huì)不停的去調(diào)用下一個(gè)
// Call the next interceptor in the chain.
// 調(diào)用鏈中的下一個(gè)攔截器
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
第一次運(yùn)行到這里的時(shí)候,調(diào)用的是RetryAndFollowUpInterceptor這個(gè)攔截器的intercept方法
RetryAndFollowUpInterceptor 重試和跟進(jìn)攔截器
之前有簡單介紹過這個(gè)攔截器,所以我們直接看他的intercept方法
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(request.url()), callStackTrace);
int followUpCount = 0;
Response priorResponse = null;
while (true) {
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response = null;
boolean releaseConnection = true;
try {
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), false, request)) {
throw e.getLastConnectException();
}
releaseConnection = false;
continue;
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, requestSendStarted, request)) throw e;
releaseConnection = false;
continue;
} finally {
// We're throwing an unchecked exception. Release any resources.
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}
// Attach the prior response if it exists. Such responses never have a body.
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
Request followUp = followUpRequest(response);
if (followUp == null) {
if (!forWebSocket) {
streamAllocation.release();
}
return response;
}
closeQuietly(response.body());
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
if (followUp.body() instanceof UnrepeatableRequestBody) {
streamAllocation.release();
throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
}
if (!sameConnection(response, followUp.url())) {
streamAllocation.release();
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(followUp.url()), callStackTrace);
} else if (streamAllocation.codec() != null) {
throw new IllegalStateException("Closing the body of " + response
+ " didn't close its backing stream. Bad interceptor?");
}
request = followUp;
priorResponse = response;
}
}
這邊的流程屢一下
- 獲取request
- new 一個(gè)StreamAllocation
- 然后再去調(diào)用當(dāng)前傳過來的chain的proceed方法
- 然后返回response
- 組裝response和一些善后處理
敏銳的我們看到了streamAllocation 按照字面的意思是 流分配 傳入的值是連接池,url,和一個(gè)callStackTrace
SteamAllocation上面的描述我們看下,直接中文版翻譯過來:
* 該類協(xié)調(diào)三個(gè)實(shí)體之間的關(guān)系
*
* Connections: 到遠(yuǎn)程服務(wù)器的物理套接字連接。 這些建立可能很慢,所以有必要能夠取消當(dāng)前連接的連接。
* Streams: 在連接上分層的邏輯HTTP請(qǐng)求/響應(yīng)對(duì)。 每個(gè)連接都有自己的分配限制,它定義了連接可以攜帶多少個(gè)并發(fā)流。
* HTTP / 1.x連接一次可以攜帶1個(gè)流,HTTP / 2通常攜帶多個(gè)。
* Calls: 流的邏輯順序,通常是初始請(qǐng)求及其后續(xù)請(qǐng)求。 我們更喜歡將單個(gè)Call的所有流保留在同一個(gè)連接上,以獲得更好的行為和位置。
*
* 該類的實(shí)例代表呼叫,在一個(gè)或多個(gè)連接上使用一個(gè)或多個(gè)流。 該類有API可以釋放以上每種資源:
* {@link #noNewStreams()} 將阻止連接被用于新的流。在{@code Connection: close}header之后或當(dāng)連接可能不一致時(shí)使用此選項(xiàng)。
* {@link #streamFinished streamFinished()}從此分配中釋放活動(dòng)流。
* 請(qǐng)注意,在給定時(shí)間只有一個(gè)流可能處于活動(dòng)狀態(tài),因此在使用{@link #newStream newStream()}
* 創(chuàng)建后續(xù)流之前,有必要調(diào)用{@link #streamFinished streamFinished()}。
* {@link #release()}刪除連接上的呼叫保持。 請(qǐng)注意,這不會(huì)立即釋放連接,如果有一個(gè)流仍然揮之不去。
* 這種情況發(fā)生在通話完成但其響應(yīng)體尚未完全消耗的情況下。
*
* 此類支持{@linkplain # asynchronous canceling}。
* 這是為了具有最小的爆炸半徑。
* 如果HTTP / 2流處于活動(dòng)狀態(tài),則取消將取消該流,但不會(huì)將其他流分享其連接。
* 但是如果TLS握手仍在進(jìn)行中,則取消可能會(huì)破壞整個(gè)連接。
這個(gè)我們大致就知道了這個(gè)類是來干嘛的了,在RetryAndFollowUpInterceptor里面他只進(jìn)行了 realease和streamFailed操作 ,推測(cè)他的調(diào)用應(yīng)該在下一個(gè)攔截鏈里面,我們根據(jù)上面的攔截器集合知道 下一個(gè)調(diào)用的是BridgeInterceptor攔截器
BridgeInterceptor
這個(gè)攔截器主要的職責(zé)是
從應(yīng)用程序代碼到網(wǎng)絡(luò)代碼的橋梁。 首先,它根據(jù)用戶請(qǐng)求構(gòu)建網(wǎng)絡(luò)請(qǐng)求。 然后它繼續(xù)呼叫網(wǎng)絡(luò)。 最后,它從網(wǎng)絡(luò)響應(yīng)中構(gòu)建用戶響應(yīng)。 主要構(gòu)件的是網(wǎng)絡(luò)請(qǐng)求的header的部分和組裝返回來的body
(昨天睡的晚,今天好困啊。。。。)
這個(gè)類不大,直接放上來講
public final class BridgeInterceptor implements Interceptor {
private final CookieJar cookieJar;
//放進(jìn)去構(gòu)建的時(shí)候的CookieJar
public BridgeInterceptor(CookieJar cookieJar) {
this.cookieJar = cookieJar;
}
@Override public Response intercept(Chain chain) throws IOException {
//老套路,拿到chain的request
Request userRequest = chain.request();
//利用request的Builder新建一個(gè)request
Request.Builder requestBuilder = userRequest.newBuilder();
//初始化requestBody
RequestBody body = userRequest.body();
//獲取contentType 因?yàn)槭窃赥estMain里面設(shè)置的body,用的FormBody,他的CONTENT_TYPE是 MediaType.parse("application/x-www-form-urlencoded");
if (body != null) {
MediaType contentType = body.contentType();
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString());
}
//設(shè)置Content-Length
long contentLength = body.contentLength();
if (contentLength != -1) {
requestBuilder.header("Content-Length", Long.toString(contentLength));
requestBuilder.removeHeader("Transfer-Encoding");
} else {
requestBuilder.header("Transfer-Encoding", "chunked");
requestBuilder.removeHeader("Content-Length");
}
}
//設(shè)置Host
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", hostHeader(userRequest.url(), false));
}
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive");
}
// If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
// the transfer stream.
// 如果我們添加了一個(gè)“Accept-Encoding:gzip”頭域,我們也負(fù)責(zé)解壓縮傳輸流。
boolean transparentGzip = false;
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true;
requestBuilder.header("Accept-Encoding", "gzip");
}
//頭部加入cookie
List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));
}
//頭部加入U(xiǎn)A頭
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", Version.userAgent());
}
/**
*----------------------------------------------調(diào)用下一個(gè)鏈條的proceed方法 責(zé)任鏈模式---------------------------------
**/
Response networkResponse = chain.proceed(requestBuilder.build());
//后面的是請(qǐng)求回來的
//保存cookiejar
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
//構(gòu)建response
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
//處理gzip和encode
if (transparentGzip
&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse)) {
GzipSource responseBody = new GzipSource(networkResponse.body().source());
Headers strippedHeaders = networkResponse.headers().newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build();
responseBuilder.headers(strippedHeaders);
responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));
}
//返回response
return responseBuilder.build();
}
/** Returns a 'Cookie' HTTP request header with all cookies, like {@code a=b; c=d}. */
private String cookieHeader(List<Cookie> cookies) {
StringBuilder cookieHeader = new StringBuilder();
for (int i = 0, size = cookies.size(); i < size; i++) {
if (i > 0) {
cookieHeader.append("; ");
}
Cookie cookie = cookies.get(i);
cookieHeader.append(cookie.name()).append('=').append(cookie.value());
}
return cookieHeader.toString();
}
}
這里截止到調(diào)用下個(gè)攔截鏈的時(shí)候,做了以下幾件事情:
-
構(gòu)建請(qǐng)求體
-
設(shè)置content-type和content-length
-
設(shè)置Host
-
設(shè)置Connection
-
處理gzip
-
設(shè)置cookie
-
設(shè)置User-Agent
-
調(diào)用下一個(gè)攔截鏈
按照上面的順序,下一個(gè)攔截鏈?zhǔn)荂acheInterceptor,CacheInterceptor是個(gè)比較復(fù)雜的攔截鏈,如果我們沒設(shè)置的話里面的判斷都是不進(jìn)去的,然后調(diào)用下一個(gè)chain,下一個(gè)是ConnectInterceptor
ConnectInterceptor
這個(gè)攔截器做的事情看起來簡單,實(shí)際上是比較復(fù)雜的,他的主要職責(zé)是:
打開與目標(biāo)服務(wù)器的連接,并進(jìn)入下一個(gè)攔截器
代碼量很少
public final class ConnectInterceptor implements Interceptor {
public final OkHttpClient client;
public ConnectInterceptor(OkHttpClient client) {
this.client = client;
}
@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.
//如果是get請(qǐng)求 不做健康檢查
boolean doExtensiveHealthChecks = !request.method().equals("GET");
HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
}
這里重要的就是streamAllocation這個(gè)類,這邊調(diào)用了他的newStream()方法和connection()方法 ,我們點(diǎn)進(jìn)去分析這兩個(gè)方法
//在retryAndFollowUp里面已經(jīng)初始化過 我們這邊就直接看他的方法里面
public HttpCodec newStream(OkHttpClient client, boolean doExtensiveHealthChecks) {
int connectTimeout = client.connectTimeoutMillis();
int readTimeout = client.readTimeoutMillis();
int writeTimeout = client.writeTimeoutMillis();
boolean connectionRetryEnabled = client.retryOnConnectionFailure();
//從連接池里尋找一個(gè)健康的connection
try {
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);
HttpCodec resultCodec = resultConnection.newCodec(client, this);
synchronized (connectionPool) {
codec = resultCodec;
return resultCodec;
}
} catch (IOException e) {
throw new RouteException(e);
}
}
這里先初始化了幾個(gè)時(shí)間 ,然后去find了一個(gè)健康的connection 返回RealConnection
我們看這個(gè)小蝌蚪找媽媽的方法
/**
* Finds a connection and returns it if it is healthy. If it is unhealthy the process is repeated until a healthy connection is found.
*
* 找到一個(gè)健康的連接并返回它。 如果不健康,則重復(fù)該過程直到發(fā)現(xiàn)健康的連接。
*/
private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
int writeTimeout, boolean connectionRetryEnabled, boolean doExtensiveHealthChecks)
throws IOException {
while (true) {
RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
connectionRetryEnabled);
// If this is a brand new connection, we can skip the extensive health checks.
//如果這是一個(gè)全新的聯(lián)系,我們可以跳過廣泛的健康檢查。
synchronized (connectionPool) {
if (candidate.successCount == 0) {
return candidate;
}
}
// Do a (potentially slow) check to confirm that the pooled connection is still good. If it
// isn't, take it out of the pool and start again.
//做一個(gè)(潛在的慢)檢查,以確認(rèn)匯集的連接是否仍然良好。 如果不是,請(qǐng)將其從池中取出并重新開始。
if (!candidate.isHealthy(doExtensiveHealthChecks)) {
noNewStreams();
continue;
}
return candidate;
}
}
這個(gè)方法的參數(shù)有必要說明一下
doExtensiveHealthChecks = true //是否做健康檢查 get的話不做
connectTimeout = 10000 //鏈接超時(shí)時(shí)間
readTimeout = 10000 //讀取超時(shí)時(shí)間
writeTimeout = 10000 //寫入超時(shí)時(shí)間
connectionRetryEnabled = true //連接重試啟用
這個(gè)方法首先就是進(jìn)入了一個(gè)while循環(huán),里面又調(diào)用了一個(gè)findConnection的方法去尋找RealConnection,又轉(zhuǎn)到findConnection,這個(gè)方法比較長,慢慢來看
/**
* Returns a connection to host a new stream. This prefers the existing connection if it exists,
* then the pool, finally building a new connection.
*
* 返回一個(gè)連接以托管新流。最好是現(xiàn)有的連接,如果它存在,然后是池,最后建立一個(gè)新的連接。
*/
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
boolean connectionRetryEnabled) throws IOException {
Route selectedRoute;
//二話不說先上鎖,避免線程問題
synchronized (connectionPool) {
// 確保狀態(tài)的正常
if (released) throw new IllegalStateException("released");
if (codec != null) throw new IllegalStateException("codec != null");
if (canceled) throw new IOException("Canceled");
// Attempt to use an already-allocated connection.
// 嘗試使用已分配的連接。如果之前沒有請(qǐng)求的話就是null,如果有的話就用 沒有的話往下走
RealConnection allocatedConnection = this.connection;
if (allocatedConnection != null && !allocatedConnection.noNewStreams) {
return allocatedConnection;
}
// Attempt to get a connection from the pool.
// 嘗試從池中獲取連接。
Internal.instance.get(connectionPool, address, this, null);
if (connection != null) {
return connection;
}
selectedRoute = route;
}
// If we need a route, make one. This is a blocking operation.
//如果我們需要一條路線,做一個(gè)路線。這是一個(gè)阻止操作。
if (selectedRoute == null) {
selectedRoute = routeSelector.next();
}
//真正的請(qǐng)求
RealConnection result;
synchronized (connectionPool) {
if (canceled) throw new IOException("Canceled");
// Now that we have an IP address, make another attempt at getting a connection from the pool.
// This could match due to connection coalescing.
//現(xiàn)在我們有一個(gè)IP地址,再嘗試從池中獲取連接。
//這可能由于連接聚結(jié)而匹配。
Internal.instance.get(connectionPool, address, this, selectedRoute);
if (connection != null) return connection;
// Create a connection and assign it to this allocation immediately. This makes it possible
// for an asynchronous cancel() to interrupt the handshake we're about to do.
//
//創(chuàng)建連接并立即將其分配給此分配。這使得異步cancel()可以中斷我們即將做的握手。
//
route = selectedRoute;
refusedStreamCount = 0;
//到這里發(fā)現(xiàn)沒有,新建一個(gè)RealConnection
result = new RealConnection(connectionPool, selectedRoute);
//添加計(jì)數(shù)
acquire(result);
}
// Do TCP + TLS handshakes. This is a blocking operation.
//做TCP + TLS握手。 這是一個(gè)阻止操作。這一步進(jìn)行的是sokcet的初始化和鏈接
result.connect(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled);
//假如這個(gè)請(qǐng)求之前有失敗過 ,從失敗隊(duì)列里移除
routeDatabase().connected(result.route());
Socket socket = null;
synchronized (connectionPool) {
// Pool the connection.
// 加入連接池。
Internal.instance.put(connectionPool, result);
// If another multiplexed connection to the same address was created concurrently, then
// release this connection and acquire that one.
// 如果同時(shí)創(chuàng)建了與同一地址的多路復(fù)用連接,則釋放此連接并獲取該連接。
if (result.isMultiplexed()) {
socket = Internal.instance.deduplicate(connectionPool, address, this);
result = connection;
}
}
closeQuietly(socket);
return result;
}
這邊尋找到一個(gè)連接后,講這個(gè)鏈接賦值個(gè)一個(gè)RealConnection,然后去調(diào)用下一個(gè)鏈條的proceed方法,就是上面的
return realChain.proceed(request, streamAllocation, httpCodec, connection);
根據(jù)上面的集合 下一個(gè)鏈條是CallServerInterceptor,我們來進(jìn)入CallServerInterceptor
CallServerInterceptor
這是鏈中的最后一個(gè)攔截器。 它對(duì)服務(wù)器進(jìn)行網(wǎng)絡(luò)呼叫
是不是感覺不想看了?頭暈眼皮重想睡覺?
**穩(wěn)住 我們能贏!! **
public final class CallServerInterceptor implements Interceptor {
private final boolean forWebSocket;
//RealCall 初始化 是否是webSocket
public CallServerInterceptor(boolean forWebSocket) {
this.forWebSocket = forWebSocket;
}
@Override public Response intercept(Chain chain) throws IOException {
//同樣的配方 同樣的味道
RealInterceptorChain realChain = (RealInterceptorChain) chain;
//在上個(gè)鏈條里初始化的HttpCodec 一般ed是HttpCodec1
HttpCodec httpCodec = realChain.httpStream();
//從上一個(gè)里面獲取streamAllocation
StreamAllocation streamAllocation = realChain.streamAllocation();
//獲取當(dāng)前鏈接
RealConnection connection = (RealConnection) realChain.connection();
//獲取請(qǐng)求
Request request = realChain.request();
long sentRequestMillis = System.currentTimeMillis();
///這應(yīng)該更新HTTP引擎的sentRequestMillis字段
httpCodec.writeRequestHeaders(request);
Response.Builder responseBuilder = null;
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.
//如果請(qǐng)求中有一個(gè)“Expect:100-continue”頭,請(qǐng)等待“HTTP / 1.1 100 Continue”響應(yīng),
// 然后再發(fā)送請(qǐng)求主體。如果我們沒有這樣做,
// 請(qǐng)返回我們所獲得的(例如4xx響應(yīng)),而不會(huì)傳輸請(qǐng)求體。
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
httpCodec.flushRequest();
responseBuilder = httpCodec.readResponseHeaders(true);
}
if (responseBuilder == null) {
// Write the request body if the "Expect: 100-continue" expectation was met.
//如果滿足“Expect:100-continue”期望,請(qǐng)寫請(qǐng)求正文
Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
} 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();
}
}
httpCodec.finishRequest();
if (responseBuilder == null) {
responseBuilder = httpCodec.readResponseHeaders(false);
}
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
int code = response.code();
if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
//連接正在升級(jí),但是我們需要確保攔截器看到一個(gè)非空響應(yīng)體
response = response.newBuilder()
.body(Util.EMPTY_RESPONSE)
.build();
} else {
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
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;
}
}
上面獲取到一系列的變量之后,然后調(diào)用了
httpCodec.writeRequestHeaders(request);
這個(gè)方法里面是這樣的
/** Returns bytes of a request header for sending on an HTTP transport. */
/** 返回用于在HTTP傳輸上發(fā)送的請(qǐng)求標(biāo)頭的字節(jié)*/
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;
}
這個(gè)就是組裝了一個(gè)頭然后請(qǐng)求過去,然后接著判斷當(dāng)前請(qǐng)求的方法和body是否合法,然后往下走,判斷一下如果請(qǐng)求中有一個(gè)“Expect:100-continue”頭,等待“HTTP / 1.1 100 Continue”響應(yīng), 平常也沒有 ,然后往下走,判斷responseBuilder 如果為空的話,調(diào)用httpCodec的createRequestBody,它里面的經(jīng)過重重調(diào)用之后,最后是調(diào)用的這個(gè)方法
private long writeOrCountBytes(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;
}
從CallServerInterceptor 調(diào)用過來的這個(gè)方法的countBytes是false, 用的是sink的buff 這個(gè)sink的buff現(xiàn)在就是socket的流操作 ,把請(qǐng)求提寫入到socket里面,完成請(qǐng)求,具體的整個(gè)sokcet請(qǐng)求的流程我會(huì)在教程里講,這里就不多敘述。
接著上面的說,請(qǐng)求完之后 responseBuilder 是通過httpcodec的readResponseHeaders來創(chuàng)建,里面有請(qǐng)求回來的請(qǐng)求碼, 狀態(tài)和頭部,然后根據(jù)responseBuilder構(gòu)架response,把當(dāng)前的請(qǐng)求,握手,發(fā)送請(qǐng)求的時(shí)間,服務(wù)器返回的時(shí)間放到response里面,此處忽略101先,然后看到response又通過builder去放入返回的body,如果是20X的話就拋出異常,然后返回response。 此處又該往回走,在RealInterceptorChain 里面后半部分,處理了一下結(jié)尾,然后返回Response,然后RealCall里面的getResponseWithInterceptorChain()返回Response,然后在execute或者AsyncCall的execute里面返回Response,如果是AsuncCall的話返回到回調(diào)里,這個(gè)流程就完成了。