BridgeInterceptor源碼解析
本文基於okhttp3.10.0
這是okhttp攔截器中第二個,作用非常簡單就是增加必要的請求頭,處理請求體。
1. 主要功能
- 添加請求頭
- Cookie管理
- Gzip壓縮
1.1 添加請求體
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
RequestBody body = userRequest.body();
if (body != null) {//body不爲空
MediaType contentType = body.contentType();
if (contentType != null) {//contentType不爲空
requestBuilder.header("Content-Type", contentType.toString());//添加contentType參數
}
long contentLength = body.contentLength();
if (contentLength != -1) {//body長度不爲空
requestBuilder.header("Content-Length", Long.toString(contentLength));//添加Content-Length參數
requestBuilder.removeHeader("Transfer-Encoding");
} else {
requestBuilder.header("Transfer-Encoding", "chunked");//分塊傳輸
requestBuilder.removeHeader("Content-Length");
}
}
if (userRequest.header("Host") == null) {//host爲null增加host參數
requestBuilder.header("Host", hostHeader(userRequest.url(), false));
}
if (userRequest.header("Connection") == null) {//默認支持keep-alive
requestBuilder.header("Connection", "Keep-Alive");
}
if (userRequest.header("User-Agent") == null) {//增加用戶標識信息
requestBuilder.header("User-Agent", Version.userAgent());
}
Response networkResponse = chain.proceed(requestBuilder.build());//進行請求
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
return responseBuilder.build();//返回結果
}
剔除Cookie和Gzip代碼後BridgeInterceptor變的非常簡單,這裏我們歸納下都加了那些頭和作用
- Content-Type:報文主體內容類型
- Content-Length:實體主體的大小(單位:字節)
- Transfer-Encoding:規定了傳輸報文主體時採用的編碼方式
- Host:請求資源所在服務器,段在 HTTP/1.1 規範內是唯一一個必須被包含在請 求內的首部字段
- Connection:管理持久連接
- User-Agent:HTTP 客戶端程序的信息
我隨便抓了個請求,可以看下它頭部信息
1.2 Cookie管理
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());//獲取本地存儲的cookie
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));//cookies不等於空添加到頭部
}
Response networkResponse = chain.proceed(requestBuilder.build());
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());//存儲服務端返回的coockie
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
return responseBuilder.build();
}
cookieJar是通過okhttpclient#cookieJar()獲取的,默認實現爲CookieJar.NO_COOKIES
public interface CookieJar {
CookieJar NO_COOKIES = new CookieJar() {//默認實現
@Override public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
}
@Override public List<Cookie> loadForRequest(HttpUrl url) {
return Collections.emptyList();
}
};
void saveFromResponse(HttpUrl url, List<Cookie> cookies);//存儲coockie
List<Cookie> loadForRequest(HttpUrl url);//通過url讀取cookie
}
所以在讀取的時候默認是返回一個空的list,再來看下存儲HttpHeaders#receiveHeaders()
public static void receiveHeaders(CookieJar cookieJar, HttpUrl url, Headers headers) {
if (cookieJar == CookieJar.NO_COOKIES) return;//默認不存儲
List<Cookie> cookies = Cookie.parseAll(url, headers);//將headers中的cookie解析爲Cookie對象
if (cookies.isEmpty()) return;
cookieJar.saveFromResponse(url, cookies);//觸發存儲邏輯
}
默認直接走到了return裏,否則通過Cookie#parseAll()解析response的header中的cookie
public static List<Cookie> parseAll(HttpUrl url, Headers headers) {
List<String> cookieStrings = headers.values("Set-Cookie");//其實就是取響應頭中set-cookie字段的值,判斷是cookie值轉爲cookie對象存儲在list中返回
List<Cookie> cookies = null;
for (int i = 0, size = cookieStrings.size(); i < size; i++) {
Cookie cookie = Cookie.parse(url, cookieStrings.get(i));
if (cookie == null) continue;
if (cookies == null) cookies = new ArrayList<>();
cookies.add(cookie);
}
return cookies != null
? Collections.unmodifiableList(cookies)
: Collections.<Cookie>emptyList();
}
那麼如果要使用cookie只需要實現CookieJar接口並傳給okhttpclient即可。
接下來可以看下http請求中關於cookie運用的例子
okhttp也就是根據這個規則解析respose中的cookie在下次請求的時候在帶上cookie信息。
1.3 Gzip壓縮
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
boolean transparentGzip = false;
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {//如果沒有Accept-Encoding,則添加默認值,告訴服務器客戶端可以接受gzip壓縮數據
transparentGzip = true;
requestBuilder.header("Accept-Encoding", "gzip");
}
Response networkResponse = chain.proceed(requestBuilder.build());
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
if (transparentGzip
&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse)) {//response使用的gzip壓縮
GzipSource responseBody = new GzipSource(networkResponse.body().source());
Headers strippedHeaders = networkResponse.headers().newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build();
responseBuilder.headers(strippedHeaders);
String contentType = networkResponse.header("Content-Type");
responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));//進行gzip解壓還原數據
}
return responseBuilder.build();//返回給我們
}
okhttp默認在頭部添加Accept-Encoding:gzip,如果服務端確實支持gzip壓縮的話,okhttp會幫我們進行解壓省去了自行解壓的過程。
2. 總結
BridgeInterceptor非常簡單就不總結了,就醬