HTTP Content-Length深入實踐

引子

HTTP頭部Content-Length用于描述HTTP消息實體的傳輸長度,瀏覽器對比Content-Length和HTTP請求或者響應(yīng)body長度判斷一次HTTP傳輸過程,以獨立于TCP長連接。但是如果Content-Length與HTTP請求或者響應(yīng)body長度不一致時,本文深入實踐瀏覽器怎么處理這些異常情況。
Content-Length和Content-Type焦不離孟,關(guān)于Content-Type可以參考拙作HTTP Content-Type深入實踐。

情況1:HTTP Response頭部不顯示指定Content-Length

后端Spring boot+Java代碼:

package com.demo.web.http;

import com.google.common.collect.Maps;
import com.google.gson.Gson;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

@Controller
@RequestMapping("http")
public class ContentTypeController {
    private final static Gson GSON = new Gson();
    @RequestMapping("/content-type-response")
    public String contentType4Response() {
        return "http/content-type-response";
    }

    @RequestMapping("content-type-response.json")
    @ResponseBody
    public void json4Response(HttpServletResponse response) throws IOException {
        Map<String, Object> map = Maps.newHashMap();
        map.put("name", "datou");
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(GSON.toJson(map));
    }
}

前端html+css+javascript+jquery代碼:

<!DOCTYPE HTML>
<html>
<head>
    <title>HTTP response Content-Type Demo</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <script src="http://code.jquery.com/jquery-1.4.2.min.js" type="text/javascript"></script>
</head>
<body>
<p>Name: <span id="name"></span></p>
<button onclick="show()">show name</button>
<script>
    function show() {
        $.get("content-type-response.json", function (data) {
            console.log(data);
            $("#name").text(data.name);
        });
    }
</script>
</body>
</html>

訪問圖1紅色方框的域名,對應(yīng)圖2綠色方框的抓包,點擊“show name”按鈕,前端發(fā)送ajax請求服務(wù)端,對應(yīng)圖2藍色方框的抓包,即使服務(wù)端不顯示指定HTTP Response頭部Content-Length,實際的HTTP Response頭部Content-Length: 16,如圖1紅色方框,對應(yīng)圖2紅色方框的seq 712:848,其中136字節(jié)不只包含HTTP Response body。

圖1 前端頁面訪問后端

圖2 前端頁面訪問后端tcpdump

情況2:HTTP Response頭部顯示指定Content-Length等于實際Response body長度

后端Spring boot+Java代碼,顯示指定Content-Length:response.setContentLength(16);

package com.demo.web.http;

import com.google.common.collect.Maps;
import com.google.gson.Gson;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

@Controller
@RequestMapping("http")
public class ContentTypeController {
    private final static Gson GSON = new Gson();
    @RequestMapping("/content-type-response")
    public String contentType4Response() {
        return "http/content-type-response";
    }

    @RequestMapping("content-type-response.json")
    @ResponseBody
    public void json4Response(HttpServletResponse response) throws IOException {
        Map<String, Object> map = Maps.newHashMap();
        map.put("name", "datou");
        response.setContentType("application/json;charset=utf-8");
        response.setContentLength(16);
        response.getWriter().write(GSON.toJson(map));
    }
}

訪問前端頁面與情況1一樣,效果如圖1和圖2所示。

情況3:HTTP Response頭部顯示指定Content-Length小于實際Response body長度

后端Spring boot+Java代碼,顯示指定Content-Length:response.setContentLength(15);
訪問圖3域名,點擊“show name”按鈕,前端發(fā)送ajax請求服務(wù)端,服務(wù)端返回HTTP Response頭部Content-Length: 15,對應(yīng)圖2紅色方框的seq 712:847,相比圖2的紅色方框少一個字節(jié)。導致Response body不完整,前端也就不能解碼Content-Type:application/json;charset=UTF-8的字符串為json對象,所以圖3的Name:為空。

圖3 前端頁面訪問后端

圖4 前端頁面訪問后端tcpdump

情況4:HTTP Response頭部顯示指定Content-Length大于實際Response body長度

后端Spring boot+Java代碼,顯示指定Content-Length:response.setContentLength(17);
訪問圖5域名,點擊“show name”按鈕,前端發(fā)送ajax請求服務(wù)端,服務(wù)端返回HTTP Response頭部Content-Length: 17,但是實際上HTTP Response body長度為16字節(jié),對應(yīng)圖2紅色方框的seq 712:848,與圖2紅色方框一致。
因為HTTP Response頭部Content-Length: 17,所以瀏覽器一直等待第17個字節(jié),不會解析實際上已經(jīng)接收完服務(wù)端發(fā)送的HTTP Response body(16字節(jié)長度),等待一段時間后瀏覽器報net::ERR_CONTENT_LENGTH_MISMATCH,同時圖5的Name:為空。

圖5 前端頁面訪問后端

圖6 前端頁面訪問后端tcpdump

HTTP首部Content-Length使用場景

當客戶端向服務(wù)器請求一個靜態(tài)頁面或者一張圖片時,服務(wù)器可以很清楚的知道內(nèi)容大小,然后通過Content-length消息首部字段告訴客戶端需要接收多少數(shù)據(jù)。
HTTP首部定義Connection: keep-alive后,客戶端、服務(wù)端怎么知道本次傳輸結(jié)束呢?靜態(tài)頁面通過Content-Length提前告知對方數(shù)據(jù)傳輸大小。關(guān)于HTTP首部Connection詳解請查看拙作HTTP首部Connection實踐。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容