聊聊HTTP gzip壓縮與常見的Android網絡框架

進入主題之前,我們先來看一下客戶端與服務器通信過程中,如果服務器支持,HTTP gzip壓縮是如何實現的?

如圖所示:

HTTP compression:gzip

request header中聲明Accept-Encoding: gzip,告知服務器客戶端接受gzip的數據。
服務器支持的情況下,返回gzip后的response body,同時加入以下header:

  • Content-Encoding: gzip:表明body是gzip過的數據
  • Content-Length:117:表示body gzip壓縮后的數據大小,便于客戶端使用。

    Transfer-Encoding: chunked分塊傳輸編碼

OK,HTTP gzip壓縮的基本流程我們理清楚了,來看在Android各網絡框架中表現有什么差異。

OkHttp

OkHttp作為目前Android最火的網絡庫,應用范圍較廣,相比于Android自帶的HttpUrlConnection、Apache坑也少很多。
我們首先來看這個庫的實現:
(注:以下代碼基于OkHttp 3.4.1, 之前的版本邏輯也是一樣的,但3.4.0開始將這些邏輯抽離到了內置的interceptor中,看起來較為方便)

BridgeInterceptor.java

// If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
// the transfer stream.
boolean transparentGzip = false;
if (userRequest.header("Accept-Encoding") == null) {
  transparentGzip = true;
  requestBuilder.header("Accept-Encoding", "gzip");
}

如果header中沒有Accept-Encoding,默認自動添加 ,且標記變量transparentGziptrue

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)));
}

針對返回結果,如果同時滿足以下三個條件:

  • transparentGziptrue,即之前自動添加了Accept-Encoding
  • header中標明了Content-Encoding為gzip
  • 有body

移除 Content-EncodingContent-Length,并對結果進行解壓縮。

可以看到以上邏輯完成了:

  1. 開發者沒有添加Accept-Encoding時,自動添加Accept-Encoding: gzip
  2. 自動添加的request,response支持自動解壓
  3. 手動添加不負責解壓縮
  4. 自動解壓時移除Content-Length,所以上層Java代碼想要contentLength時為-1
  5. 自動解壓時移除 Content-Encoding
  6. 自動解壓時,如果是分塊傳輸編碼,Transfer-Encoding: chunked不受影響。

以上6點是我們通過OkHttp源碼得出的結論,我們以此來繼續看下其他框架。

HttpUrlConnection

1. 是否自動添加Accept-Encoding: gzip

官網有過說明:

In Gingerbread, we added transparent response compression. HttpURLConnection will automatically add this header to outgoing requests, and handle the corresponding response:
Accept-Encoding: gzip
Take advantage of this by configuring your Web server to compress responses for clients that can support it. If response compression is problematic, the class documentation shows how to disable it.

即:2.3后默認是gzip,不加Accept-Encoding會被自動添加上Accept-Encoding: gzip

2. 自動添加的request,response是否支持自動解壓

By default, this implementation of HttpURLConnection requests that servers use gzip compression and it automatically decompresses the data for callers of getInputStream().

即返回的數據是已經自動解壓縮的。

3. 手動添加是否負責解壓縮

By default, this implementation of HttpURLConnection requests that servers use gzip compression and it automatically decompresses the data for callers of getInputStream(). The Content-Encoding and Content-Length response headers are cleared in this case. Gzip compression can be disabled by setting the acceptable encodings in the request header:
urlConnection.setRequestProperty("Accept-Encoding", "identity");
Setting the Accept-Encoding request header explicitly disables automatic decompression and leaves the response headers intact; callers must handle decompression as needed, according to the Content-Encoding header of the response.

例子中只提到設置為identity時可以禁止gzip壓縮。
但是請注意最后一段提到,顯式聲明會禁止自動解壓,同時保留header完整性,需要根據Content-Encoding來自己處理response。

實測4.1 - 6.0 版本之后發現,并不是非要指定identity才能屏蔽,指定gzip一樣也不會解壓縮。so,只要是顯式聲明過,都不會再處理,即:手動添加不會負責解壓縮。

4. 自動解壓時Content-Length問題

Since HTTP’s Content-Length header returns the compressed size, it is an error to use getContentLength() to size buffers for the uncompressed data. Instead, read bytes from the response until InputStream.read() returns -1.

即:getContentLength() 值為gzip壓縮時的數據大小。

之前提到OkHttp在處理gzip壓縮時會把Content-Length移除,contentLength在Java層獲取為-1,而HttpURLConnection 在Android 4.4以后底層是由OkHttp實現的,那文檔中提到的getContentLength()是compressed size是否還繼續成立呢?

實測后發現 :

  • 4.4之后的版本,Content-Length被移除,getContentLength() = -1
  • 2.3- 4.3之間,Content-Length 沒有移除,getContentLength() = compressed size

5. 自動解壓時的Content-Encoding

與Content-Length對應:

  • 4.4之后的版本,Content-Encoding被移除
  • 2.3- 4.3之間,Content-Encoding存在,無變化。

6. 自動解壓時的分塊編碼傳輸

與OkHttp相同,Transfer-Encoding: chunked不受影響。

Apache

這里不再贅述,僅闡述結論:
無自動添加、解壓機制。

總結

1、是否支持自動添加Accept-Encoding與數據自動解壓?

name transparent response compression
OkHttp yes
HttpUrlConnection yes
Apache no

2、支持自動后,response header的表現如何?

name Content-Encoding: gzip Header : Content-Length Java : ContentLength
OkHttp 被移除 被移除 -1
HttpUrlConnection(2.3 ~ 4.3) 不變 不變 compressed size
HttpUrlConnection(4.4 ~ ?) 被移除 被移除 -1

name Content-Encoding: gzip Transfer-Encoding: chunked
OkHttp 被移除 不變
HttpUrlConnection(2.3 ~ 4.3) 不變 不變
HttpUrlConnection(4.4 ~ ?) 被移除 不變

3、自動模式啟動后,在Java中獲取contentLength無論是哪個版本的HttpUrlConnection還是OkHttp都是不可信的,都不是解壓縮之后的值(可能為-1或compressed size),因此最好不要通過contentLength來做什么操作

4、HttpUrlConnection、OkHttp均是手動添加不自動解壓縮,Apache沒有自動添加自動解壓功能。三者在手動添加Accept-Encoding后,表現一致(利用這個特點,可以做一個在三者之上的網絡框架,隨意切換三種通道)。

參考資料

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容