有的時候服務器生成HTTP回應是無法確定信息大小的,這時用Content-Length就無法事先寫入長度,而需要實時生成消息長度,這時服務器一般采用Chunked編碼。
在進行Chunked編碼傳輸時,在回復消息的頭部有transfer-coding並定義為Chunked,表示將用Chunked編碼傳輸內容。
Chunked編碼使用若干個Chunk串連而成,由一個標明長度為0的chunk標示結束。每個Chunk分為頭部和正文兩部分,頭部內容指定下一段正文的字符總數(十六進制的數字)和數量單位(一般不寫),正文部分就是指定長度的實際內容,兩部分之間用回車換行(CRLF)隔開。在最后一個長度為0的Chunk中的內容是稱為footer的內容,是一些附加的Header信息(通常可以直接忽略)。
我們來模擬一下數據結構:
[Chunk大小][回車][Chunk數據體][回車][Chunk大小][回車][Chunk數據體][回車][0][回車][footer內容(有的話)][回車]
注意chunk-size是以十六進制的ASCII碼表示的,比如86AE(實際的十六進制應該是:38366165),計算成長度應該是:34478,表示從回車之后有連續的34478字節的數據。
跟蹤了www.yahoo.com的返回數據,發現在chunk-size中,還會多一些空格。可能是固定長度為7個字節,不滿7個字節的,就以空格補足,空格的ASCII碼是0x20。
解碼流程:
對chunked編碼進行解碼的目的是將分塊的chunk-data整合恢復成一塊作為報文體,同時記錄此塊體的長度。
RFC2616中附帶的解碼流程如下:(偽代碼)
length := 0 //長度計數器置0
read chunk-size, chunk-extension (if any) and CRLF //讀取chunk-size, chunk-extension
//和CRLF
while(chunk-size > 0 ) { //表明不是last-chunk
read chunk-data and CRLF //讀chunk-size大小的chunk-data,skip CRLF
append chunk-data to entity-body //將此塊chunk-data追加到entity-body后
read chunk-size and CRLF //讀取新chunk的chunk-size 和 CRLF
}
read entity-header //entity-header的格式為name:valueCRLF,如果為空即只有CRLF
while (entity-header not empty) //即,不是只有CRLF的空行
{
append entity-header to existing header fields
read entity-header
}
Content-Length:=length //將整個解碼流程結束后計算得到的新報文體length
//作為Content-Length域的值寫入報文中
Remove "chunked" from Transfer-Encoding //同時從Transfer-Encoding中域值去除chunked這個標記
Sample
Encoded response
HTTP/1.1 200 OK Content-Type: text/plain Transfer-Encoding: chunked 25 This is the data in the first chunk 1A and this is the second one 0
same as above, raw bytes in hex
0000-000F 48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 0d HTTP/1.1 200 OK. 0010-001F 0a 43 6f 6e 74 65 6e 74 2d 54 79 70 65 3a 20 74 .Content-Type: t 0020-002F 65 78 74 2f 70 6c 61 69 6e 0d 0a 54 72 61 6e 73 ext/plain..Trans 0030-003F 66 65 72 2d 45 6e 63 6f 64 69 6e 67 3a 20 63 68 fer-Encoding: ch 0040-004F 75 6e 6b 65 64 0d 0a 0d 0a 32 35 0d 0a 54 68 69 unked....25..Thi 0050-005F 73 20 69 73 20 74 68 65 20 64 61 74 61 20 69 6e s is the data in 0060-006F 20 74 68 65 20 66 69 72 73 74 20 63 68 75 6e 6b the first chunk 0070-007F 0d 0a 0d 0a 31 41 0d 0a 61 6e 64 20 74 68 69 73 ....1A..and this 0080-008F 20 69 73 20 74 68 65 20 73 65 63 6f 6e 64 20 6f is the second o 0090-009F 6e 65 0d 0a 30 0d 0a 0d 0a ne..0....
same as above, in Java code
public static final byte[] CHUNKED_RESPONSE; static { StringBuilder sb = new StringBuilder(); sb.append("HTTP/1.1 200 OK/r/n"); sb.append("Content-Type: text/plain/r/n"); sb.append("Transfer-Encoding: chunked/r/n/r/n"); sb.append("25/r/n"); sb.append("This is the data in the first chunk/r/n"); // 37 bytes of payload // (conveniently consisting of ASCII characters only) sb.append("/r/n1A/r/n"); sb.append("and this is the second one"); // 26 bytes of payload // (conveniently consisting of ASCII characters only) sb.append("/r/n0/r/n/r/n"); CHUNKED_RESPONSE = sb.toString().getBytes(java.nio.charset.Charset.forName("US-ASCII")); }
Decoded data
This is the data in the first chunk and this is the second one