OKhttp原始碼解析---攔截器之RetryAndFollowUpInterceptor
我們看下它的intercept
public Response intercept(Chain chain) throws IOException { Request request = chain.request(); streamAllocation = new StreamAllocation( client.connectionPool(), createAddress(request.url())); 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(), true, request)) throw e.getLastConnectException(); releaseConnection = false; continue; } catch (IOException e) { // An attempt to communicate with a server failed. The request may have been sent. if (!recover(e, false, 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) { 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())); } else if (streamAllocation.stream() != null) { throw new IllegalStateException("Closing the body of " + response + " didn't close its backing stream. Bad interceptor?"); } request = followUp; priorResponse = response; } }
這裡首先建立了一個StreamAllocation,StreamAllocation是用來做連線分配的,傳遞的引數有兩個,一個是前面建立的連線池,另外一個是呼叫createAddress建立的Address
private Address createAddress(HttpUrl url) { SSLSocketFactory sslSocketFactory = null; HostnameVerifier hostnameVerifier = null; CertificatePinner certificatePinner = null; if (url.isHttps()) { sslSocketFactory = client.sslSocketFactory(); hostnameVerifier = client.hostnameVerifier(); certificatePinner = client.certificatePinner(); } return new Address(url.host(), url.port(), client.dns(), client.socketFactory(), sslSocketFactory, hostnameVerifier, certificatePinner, client.proxyAuthenticator(), client.proxy(), client.protocols(), client.connectionSpecs(), client.proxySelector()); }
更加client和請求的相關資訊初始化了 Addresspublic Address(String uriHost, int uriPort, Dns dns, SocketFactory socketFactory, SSLSocketFactory sslSocketFactory, HostnameVerifier hostnameVerifier, CertificatePinner certificatePinner, Authenticator proxyAuthenticator, Proxy proxy, List<Protocol> protocols, List<ConnectionSpec> connectionSpecs, ProxySelector proxySelector) { this.url = new HttpUrl.Builder() .scheme(sslSocketFactory != null ? "https" : "http") .host(uriHost) .port(uriPort) .build(); if (dns == null) throw new NullPointerException("dns == null"); this.dns = dns; if (socketFactory == null) throw new NullPointerException("socketFactory == null"); this.socketFactory = socketFactory; if (proxyAuthenticator == null) { throw new NullPointerException("proxyAuthenticator == null"); } this.proxyAuthenticator = proxyAuthenticator; if (protocols == null) throw new NullPointerException("protocols == null"); this.protocols = Util.immutableList(protocols); if (connectionSpecs == null) throw new NullPointerException("connectionSpecs == null"); this.connectionSpecs = Util.immutableList(connectionSpecs); if (proxySelector == null) throw new NullPointerException("proxySelector == null"); this.proxySelector = proxySelector; this.proxy = proxy; this.sslSocketFactory = sslSocketFactory; this.hostnameVerifier = hostnameVerifier; this.certificatePinner = certificatePinner; }
再看下 StreamAllocation的建立
public StreamAllocation(ConnectionPool connectionPool, Address address) {
this.connectionPool = connectionPool;
this.address = address;
this.routeSelector = new RouteSelector(address, routeDatabase());
}
這裡儲存了前面傳過來的連線池和地址,並建立了一個RouteSelector,並進行了路由的一個選擇
回到intercept,進入while迴圈
1、首先檢視請求是否已經取消
2、呼叫RealInterceptorChain的proceed處理這個請求並把剛建立的StreamAllocation傳遞進去
3、如果前面第二步沒有出現異常,則說明請求完成,設定releaseConnection為false,出現異常則將releaseConnection置為true,並釋放前面建立的StreamAllocation
4、priorResponse不為空,則說明前面已經獲取到了響應,這裡會結合當前獲取的Response和先前的Response
5、呼叫followUpRequest檢視響應是否需要重定向,如果不需要重定向則返回當前請求
6、重定向次數+1,並且判斷StreamAllocation是否需要重新建立
7、重新設定request,並把當前的Response儲存到priorResponse,繼續while迴圈
我們看下對是否需要重定向的判斷followUpRequest
private Request followUpRequest(Response userResponse) throws IOException {
if (userResponse == null) throw new IllegalStateException();
Connection connection = streamAllocation.connection();
Route route = connection != null
? connection.route()
: null;
int responseCode = userResponse.code();
final String method = userResponse.request().method();
switch (responseCode) {
case HTTP_PROXY_AUTH:
Proxy selectedProxy = route != null
? route.proxy()
: client.proxy();
if (selectedProxy.type() != Proxy.Type.HTTP) {
throw new ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy");
}
return client.proxyAuthenticator().authenticate(route, userResponse);
case HTTP_UNAUTHORIZED:
return client.authenticator().authenticate(route, userResponse);
case HTTP_PERM_REDIRECT:
case HTTP_TEMP_REDIRECT:
// "If the 307 or 308 status code is received in response to a request other than GET
// or HEAD, the user agent MUST NOT automatically redirect the request"
if (!method.equals("GET") && !method.equals("HEAD")) {
return null;
}
// fall-through
case HTTP_MULT_CHOICE:
case HTTP_MOVED_PERM:
case HTTP_MOVED_TEMP:
case HTTP_SEE_OTHER:
// Does the client allow redirects?
if (!client.followRedirects()) return null;
String location = userResponse.header("Location");
if (location == null) return null;
HttpUrl url = userResponse.request().url().resolve(location);
// Don't follow redirects to unsupported protocols.
if (url == null) return null;
// If configured, don't follow redirects between SSL and non-SSL.
boolean sameScheme = url.scheme().equals(userResponse.request().url().scheme());
if (!sameScheme && !client.followSslRedirects()) return null;
// Redirects don't include a request body.
Request.Builder requestBuilder = userResponse.request().newBuilder();
if (HttpMethod.permitsRequestBody(method)) {
if (HttpMethod.redirectsToGet(method)) {
requestBuilder.method("GET", null);
} else {
requestBuilder.method(method, null);
}
requestBuilder.removeHeader("Transfer-Encoding");
requestBuilder.removeHeader("Content-Length");
requestBuilder.removeHeader("Content-Type");
}
// When redirecting across hosts, drop all authentication headers. This
// is potentially annoying to the application layer since they have no
// way to retain them.
if (!sameConnection(userResponse, url)) {
requestBuilder.removeHeader("Authorization");
}
return requestBuilder.url(url).build();
case HTTP_CLIENT_TIMEOUT:
// 408's are rare in practice, but some servers like HAProxy use this response code. The
// spec says that we may repeat the request without modifications. Modern browsers also
// repeat the request (even non-idempotent ones.)
if (userResponse.request().body() instanceof UnrepeatableRequestBody) {
return null;
}
return userResponse.request();
default:
return null;
}
}
這裡主要是根據響應碼,檢視是否需要重定向,並重新設定請求
這樣RetryAndFollowUpInterceptor攔截器就分析完了,下一個攔截器的啟動是通過呼叫RealInterceptorChain的proceed
public Response proceed(Request request, StreamAllocation streamAllocation, HttpStream httpStream,
Connection 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.
if (this.httpStream != null && !sameConnection(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().
if (this.httpStream != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpStream, 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().
if (httpStream != 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;
}
這裡index為1,建立的RealInterceptorChain的index為2,獲取到的攔截器是BridgeInterceptor,下一篇我們分析它的intercept方法相關推薦
OKhttp原始碼解析---攔截器之RetryAndFollowUpInterceptor
我們看下它的intercept public Response intercept(Chain chain) throws IOException { Request request = chain.request(); streamAllocation
OKhttp原始碼解析---攔截器之CallServerInterceptor
CallServerInterceptor負責傳送請求和獲取響應,我們看下它的intercept @Override public Response intercept(Chain chain) throws IOException { HttpStream ht
OkHttp中Interceptor攔截器之公共引數請求封裝
前言 之前在面試的時候遇到這樣的一個問題,那就是如果app中所有的請求都要加入一些引數(例如 版本號、手機號、登入使用者名稱、token等。。。)那麼怎麼做才能實現,而不至於在每次請求的時候都去進行新增這些請求頭。其實這個問題,剛開始我是拒絕的(之前沒有遇到過
Okhttp原始碼解析之Interceptor(攔截器)
上一篇部落格,我們一步步瞭解了Okhttp網路請求的基本流程以及原始碼解析,最後我們發現核心的問題都放在了一系列的攔截器上面,本章我就會對各個攔截器一一一結合原始碼去理解okhttp的網路請求是如何獲取到網路資料的。上篇部落格的地址:(http://blog.c
OKHttp原始碼解析(4)----攔截器CacheInterceptor
簡介 Serves requests from the cache and writes responses to the cache. 快取攔截器,負責讀取快取直接返回、更新快取。當網路請求有符合要求的Cache時,直接返回Cache。如果當前Cache失效,則刪除。CacheStrategy:快取策略
OKHttp原始碼解析(6)----攔截器CallServerInterceptor
系列文章 OKHttp原始碼解析(1)----整體流程 OKHttp原始碼解析(2)----攔截器RetryAndFollowUpInterceptor OKHttp原始碼解析(3)----攔截器BridgeInterceptor OKHttp原始碼解析(4)----攔截器CacheIntercept
(4.2.36.4)HTTP之OkHttp(四): OkHttp原始碼解析
原始碼開始之前我先貼一段OkHttp請求網路的例項 OkHttpClient mOkHttpClient = new OkHttpClient(); final Request request = new Request.Builder() .url("
Struts2 原始碼分析-----攔截器原始碼解析 --- ParametersInterceptor
ParametersInterceptor攔截器其主要功能是把ActionContext中的請求引數設定到ValueStack中,如果棧頂是當前Action則把請求引數設定到了Action中,如果棧頂是一個model(Action實現了ModelDriven介面)則把引數設定到了model中。 下面是該攔截
ABP中的攔截器之AuditingInterceptor
esc oot on() users resource self ini intercept 日誌文件 在上面兩篇介紹了ABP中的ValidationInterceptor之後,我們今天來看看ABP中定義的另外一種Interceptor即為AuditingInterce
Okhttp的系統攔截器
目錄 1.系統攔截器作用及執行順序 2.原始碼驗證執行順序 3.原始碼驗證各個攔截器的作用 1)RetryAndFollowUpInterceptor 2)BridgeInterceptor 3)CacheInterceptor 4)ConnectInterceptor
Mybatis攔截器之資料許可權過濾與分頁整合
解決方案之改SQL 原sql SELECT a.id AS "id", a.NAME AS "name", a.sex_cd AS "sexCd", a.org_id AS "orgId", a.STATUS AS "status", a.create_org_id AS "createOrgId"
Andriod 網路框架 OkHttp 原始碼解析
1、OkHttp 的基本使用 OkHttp 是 Square 的一款應用於 Android 和 Java 的 Http 和 Http/2 客戶端。使用的時候只需要在 Gradle 裡面加入下面一行依賴即可引入: implementation 'com.squareup.okhttp3:okhttp:3.1
okHttp的日誌攔截器
日誌攔截器的分類: a.網路攔截器 b.應用攔截器 建立一個類 /** * 日誌攔截器類,請求來了,先在這裡進行處理,可以得到發請求到得到請求消耗多久的時間 * 作用:可以排查網路請求速度慢的根本原因 * 1.有可能是我們在請求網路時,客戶端寫了一堆業務邏輯 * 2.有可能
rxJava和rxAndroid原始碼解析系列四之subscribeOn和observeOn的理解(學習終結篇)
本篇文章主要解決subscribeOn和observeOn這兩個方法為什麼subscribeOn只有一次有效果,observeOn切換多次回撥的都有效果。 不知道朋友有沒有看過rxandroid的原始碼,如果看過的話,就會迎刃而解,沒什麼疑慮啦。沒看過原始碼的朋友,可以看看我這個系列的前幾篇文章
rxJava和rxAndroid原始碼解析系列二之observer訂閱
建立完Observable物件後,以後一步ObservableObserveOn.subscribe(new Observer<String>() {.....})這一步又發生了什麼呢? 接著跟蹤原始碼。 @SchedulerSupport(Schedu
OkHttp原始碼解析(一)
簡單使用 OkHttpClient okHttpClient = new OkHttpClient.Builder().build(); Request request = new Request.Builder() .url("www.bai
OkHttp原始碼解析(二)
上一篇講到OkHttp的整體流程,但是裡面有一個很重要的方法getResponseWithInterceptorChain還沒有講到,這個方法裡面包括了整個okHttp最重要的攔截器鏈,所以我們今天來講解一下。 Response getResponseWithI
okhttp原始碼解析
前言 http請求的功能應該是很簡單,只是為了魯棒性和效能需要寫很多的程式碼,發現okhttp還是挺複雜的,但是我們這裡還是要好好的搞定他。 正文 我們從最簡單的使用開始 OkHttpClient client = new OkHttpClient(); Req
徹底理解OkHttp - OkHttp 原始碼解析及OkHttp的設計思想
OkHttp 現在統治了Android的網路請求領域,最常用的框架是:Retrofit+okhttp。OkHttp的實現原理和設計思想是必須要了解的,讀懂和理解流行的框架也是程式設計師進階的必經之路,程式碼和語言只是工具,重要的是思想。 在OKhttp 原始碼解析之前,我們必須先要了解http的相
OkHttp 原始碼解析(1)
專案用到OkHttp,準備研究研究(OkHttp現在很火啊,Retrofit使用OkHttp,Volley支援替換底層http棧為OkHttp,甚至Google的最新原始碼裡,都用起了OkHttp,替換了原來用的HttpClient)。 OkHttp在網路