1. 程式人生 > >【協議分析】HTTP響應頭中的2種編碼方式介紹

【協議分析】HTTP響應頭中的2種編碼方式介紹

href intro feo 發送 文檔 bsp firefox cep 目前

HTTP 1.1中有兩個實體頭(Entity-Header)直接與編碼相關,分別為Content-Encoding和Transfer-Encoding。
先說Content-Encoding, 該頭表示實體已經采用了的編碼方式.Content-Encoding是請求URL對應實體(Entity)本身的一部分.比如請求URL為 http://host/image.png.gz時,可能會得到的Content-Encoding為gzip.Content-Encoding的值是不區分大小寫的,目前HTTP1.1標準中已包括的有gzip/compress/deflate/identity等.
與Content-Encoding頭對應,HTTP請求中包含了一個Accept-Encoding頭,該頭用來說明用戶代理(User- Agent,一般也就是瀏覽器)能接受哪些類型的編碼. 如果HTTP請求中不存在該頭,服務器可以認為用戶代理能接受任何編碼類型.
接下來重點描述Transfer-Encoding, 該頭表示為了達到安全傳輸或者數據壓縮等目的而對實體進行的編碼. Transfer-Encoding與Content-Encoding的不同之處在於:
1, Transfer-Encoding只是在傳輸過程中才有的,並非請求URL對應實體的本身特性.
2, Transfer-Encoding是一個"跳到跳"頭,而Content-Encoding是"端到端"頭.
該頭的用途舉例如,請求URL為http://host/abc.txt,服務器發送數據時認為該文件可用gzip方式壓縮以節省帶寬,接收端看到Transfer-Encoding為gzip首先進行解碼然後才能得到請求實體.
此外多個編碼可能同時對同一實體使用,所以Transfer-Encoding頭中編碼順序相當重要,它代表了解碼的順序過程.同樣,Transfer- Encoding的值也是不區分大小寫的,目前HTTP1.1標準中已包括的有gzip/compress/deflate/identity /chunked等。

報文舉例:

Server: Apache-Coyote/1.1
Cache-Control: no-store
Pragma: no-cache
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: text/html;charset=GBK
Transfer-Encoding: chunked
Content-Encoding: gzip
Vary: Accept-Encoding
Date: Mon, 01 Jul 2013 02:37:55 GMT

a
.........
200
.=ks.....U..v..f......(..l....lCl..5.#5.t..{$d../X[......c.c@<,.$..^..n..7...q...v%...G....F~.T..P?.=............?.-......J.tU-..z.I.m[......h[...3K..1..U.k\3.K...<..........Oo....o.^......v).#.....(c...
b..(.......3.I....R‘......*...%o...9...(c....5.....V...4......NW.. .m./...]..}..L..Z.X=*.>.$=....{G7y....[f.(..M.........e..........Nh`.UU.n.....|ZE....,=.>l.JZ...v..y$5....ho.c....NB...
..\m.p..[J...A .I....6..RsL.q......6>.h.]Y....J.1.F...e......&Z....w...p...P..^.z+..H..SmS..i...q.m.TS.....(..K....U.0>.k
200
..d)M19..}-.{....I.~mui...N....+k...j#..qdq.....x.7MaI3..K..Z....`...j...)4...^...=......B..~(.
..]...S........>=]9`...C:....|F+K........^.hiUGD.X.T.SY..bA...v..........O..S....f.P...IY;.oI
........FD...3.Q....e..........dL...T..M.<`Z...Kf.."pR.....Y6..+.f..e..Lw&.m..t...Vt..1..].‘..3.Z...‘.RI5..j..;.:...J..:.~...>i.V\.v..wum....aM..V...&c+....<
Sf.F|.........I...Q.Q.3.....U..F...O.....!.R.E.....X...k.....z.tf.Xz....$.>)R.2..6... f.........KP7P...92.c..e......&.[.&yS.P.S.
....4.
dn....p.^.N.@..{T7.Mf
..jUT.
200

一、Transfer-Encoding含義介紹

有時候,Web服務器生成HTTP Response是無法在Header就確定消息大小的,這時一般來說服務器將不會提供Content-Length的頭信息,而采用Chunked編碼動態的提供body內容的長度。

進行Chunked編碼傳輸的HTTP Response會在消息頭部設置:

Transfer-Encoding: chunked

表示Content Body將用Chunked編碼傳輸內容。

Chunked編碼使用若幹個Chunk串連而成,由一個標明長度為0的chunk標示結束。每個Chunk分為頭部和正文兩部分,頭部內容指定下一段正文的字符總數(十六進制的數字

)和數量單位(一般不寫),正文部分就是指定長度的實際內容,兩部分之間用回車換行(CRLF)隔開。在最後一個長度為0的Chunk中的內容是稱為footer的內容,是一些附加的Header信息(通常可以直接忽略)。具體的Chunk編碼格式如下:

  

Chunked-Body = *chunk
         "0" CRLF
         footer
         CRLF 
  chunk = chunk-size [ chunk-ext ] CRLF
       chunk-data CRLF

  hex-no-zero = <HEX excluding "0">

  chunk-size = hex-no-zero *HEX
  chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-value ] )
  chunk-ext-name = token
  chunk-ext-val = token | quoted-string
  chunk-data = chunk-size(OCTET)

  footer = *entity-header

RFC文檔中的Chunked解碼過程如下:
  

   length := 0
  read chunk-size, chunk-ext (if any) and CRLF
  while (chunk-size > 0) {
  read chunk-data and CRLF
  append chunk-data to entity-body
  length := length + chunk-size
  read chunk-size and CRLF
  }
  read entity-header
  while (entity-header not empty) {
  append entity-header to existing header fields
  read entity-header
  }
  Content-Length := length
  Remove "chunked" from Transfer-Encoding

最後提供一段PHP版本的chunked解碼代碼:

$chunk_size = (integer)hexdec(fgets( $socket_fd, 4096 ) );
while(!feof($socket_fd) && $chunk_size > 0) {
    $bodyContent .= fread( $socket_fd, $chunk_size );
    fread( $socket_fd, 2 ); // skip /r/n
    $chunk_size = (integer)hexdec(fgets( $socket_fd, 4096 ) );
}

二、Content-Encoding含義介紹

Content-Encoding是HTTP協議的響應報文頭,一般形式如:

Content-Encoding:gzip,deflate,compress

Content-Encoding的說明中指出deflate指的是在RFC1950說明的zlib格式。也就是說當Content-Encoding為deflate時,內容應該為zlib格式。

compress具說chrome支持,但還沒見到哪個web服務器支持

gzip,deflate,zlib的關系:

deflate(RFC1951):一種壓縮算法,使用LZ77和哈弗曼進行編碼;
zlib(RFC1950):一種格式,是對deflate進行了簡單的封裝;
gzip(RFC1952):一種格式,也是對deflate進行的封裝.

可以看出deflate是最核心的算法,而zlib和gzip格式的區別僅僅是頭部和尾部不一樣,而實際的內容都是deflate編碼的,即:
gzip = gzip頭(10字節) + deflate編碼的實際內容 + gzip尾(8字節)

[GZIP的實現可參考GzipOutputStream.Java]
zlib = zlib頭 + deflate編碼的實際內容 + zlib尾

訪問www.163.com. 響應報文含有gzip頭,而www.baidu.com的響應報文沒有gzip頭。

看到gzip大家都很好的支持,有無gzip頭都沒有問題。

(以下內容本人未做驗證)

對deflate即zlib格式:

那麽在IE上面是打不開頁面的,包括IE6,IE7,IE8,提示為一片空白或者出錯。但是在其他的瀏覽器如Firefox,Chrome,Opera等上面都能正常打開。要讓IE能夠正常打開頁面,內容必須是deflate原始格式的數據,即去掉zlib頭和zlib尾。不知道IE為什麽不修改這個 Bug,按理說在IE6就出現的這種很簡單的問題,IE8不應該出現才對。
為了照顧IE,只好在壓縮deflate的時候去掉zlib頭和zlib尾,還好其他的瀏覽器也都能正常處理這種原始的deflate格式。

【協議分析】HTTP響應頭中的2種編碼方式介紹