HTTP2服務器推送的第一次嘗試

來自公衆號:新世界雜貨鋪

在HTTP1.x中,訪問一個頁面,瀏覽器首先獲取HTML資源,而後在解析頁面時增量地獲取其餘資源,服務器必須等待瀏覽器發出請求後才下發頁面內資源。而服務器其實是知道頁面內資源有哪些的,若是服務器可以在瀏覽器顯式請求資源以前就將資源推送到瀏覽器,頁面加載速度將會大大提示,這也是本篇的主旨。javascript

本篇主要分爲兩個部分,第一部分是用go實現的服務器推送例子,第二部分是自簽名證書。爲何會有自簽名證書,這裏筆者先賣個關子,繼續閱讀後文將會守得雲開見月明。css

服務器推送例子

目前僅有HTTP2支持服務器推送,HTTP1.x不支持服務器推送,那咱們在代碼中應該如何判斷當前服務器是否支持推送?html

在Go中,咱們經過判斷http.ResponseWriter是否實現了http.Pusher接口就能夠知道當前服務器是否支持推送。前端

下面爲筆者寫下的第一個服務器推送例子:java

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path != "/" {
        http.NotFound(w, r)
        return
    }
    pusher, ok := w.(http.Pusher)
    if ok {
        // 主動推送服務資源
        if err := pusher.Push("/static/app.js", nil); err != nil {
            log.Printf("Failed to push: %v", err)
        }
        if err := pusher.Push("/static/style.css", nil); err != nil {
            log.Printf("Failed to push: %v", err)
        }
    }
    // 下發瀏覽器首屏資源
    fmt.Fprintf(w, `<html>
    <head>
        <title>新世界雜貨鋪</title>
        <link rel="stylesheet" href="/static/style.css"">
    </head>
    <body>
        <div>Hello 新世界雜貨鋪</div>
        <div id="content"></div>
        <script src="/static/app.js"></script>
    </body>
    </html>`)
})
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
http.ListenAndServe(":8080", nil)

上述代碼中app.js內容以下:git

document.getElementById("content").innerHTML = '"新世界雜貨鋪" from js'

運行上述代碼後,在瀏覽器中訪問http://localhost:8080/獲得以下結果:github

image

咱們看上圖中標紅部分發現js資源和樣式資源並非服務器推送下發的,並且使用的是HTTP1.1,結合筆者前面的文章知道Go是支持HTTP2的且HTTP2須要使用SSL/TLS即HTTPS。所以,筆者修改監聽代碼以下:golang

http.ListenAndServeTLS(":8080", "ca.crt", "ca.key", nil)

再次運行上述代碼,並在瀏覽器中訪問https://localhost:8080/獲得以下結果:shell

image

咱們看圖中紅色部分知本次請求使用了HTTP2協議,而且靜態資源由服務器推送。瀏覽器

上述代碼中ca.crtca.key分別爲自簽名證書以及私鑰,該證書及私鑰已上傳至筆者的github,github連接見文末。

生成自簽名證書

注:筆者生成證書環境爲macOS

筆者生成自簽名證書時,先祭出搜索大法,而後使用網上的命令生成證書,最後證書是生成了,可是運行上述例子後在瀏覽器中的訪問結果卻不盡人意。

首先執行下述命令生成證書:

// 生成私鑰
openssl genrsa -out old.key 2048
// 生成證書籤名請求,此時須要填寫一些證書信息
openssl req -new -key old.key -out old.csr
// 簽發證書
openssl x509 -req -days 365 -in old.csr -signkey old.key -out old.crt

修改例子中的證書和私鑰爲old.crtold.key,最後在Chrome瀏覽器中訪問結果以下:

image

上述頁面給了不安全提示卻沒法繼續不安全訪問(Safari瀏覽器能夠進行不安全地訪問),做爲一個輕微強迫症患者,筆者的心裏有一絲絲難受。

因而筆者翻了openssl官網,最後對生成自簽名證書的命令作了改進。

首先在執行命令的目錄建立一個ca.cnf(能夠隨意命名),並在該文件中寫入以下內容:

# 更多x509v3_config見https://www.openssl.org/docs/man1.1.1/man5/x509v3_config.html
[ ca_conf ]
extendedKeyUsage = serverAuth # 可以繼續進行不安全地訪問的關鍵
basicConstraints = CA:FALSE

改進後的簽名命令以下:

// 生成私鑰
openssl genrsa -out ca.key 2048
// 生成證書籤名請求,此時須要填寫一些證書信息
openssl req -new -key ca.key -out ca.csr
// 簽發證書
openssl x509 -req -sha256 -extfile ca.cnf -extensions ca_conf -days 365 -in ca.csr -signkey ca.key -out ca.crt

此證書即爲前面例子所使用的證書,而且第一次在瀏覽器中訪問會有以下提示:

image

點擊圖中紅框部分便可正常訪問頁面。

何時使用推送

完成上面的例子以後,筆者特地去觀察了百度、淘寶和谷歌的首頁發現你們均已開始使用HTTP2,可是好像尚未公司使用服務器推送。

所以筆者下面的總結全憑我的經驗猜想:

  1. 服務器推送不要濫用,僅推送影響該頁面展現的關鍵資源,畢竟前端的懶加載已經十分紅熟。
  2. 瀏覽器可以對資源進行緩存,對於已經緩存了的資源繼續推送沒有意義,因此這種場景下要避免二次推送。

最後,衷心但願本文可以對各位讀者有必定的幫助。

參考

https://blog.golang.org/h2push

https://blog.csdn.net/qq_4187...

注:

  1. 寫本文時, 筆者所用go版本爲: go1.15.2
  2. 文章中所用完整例子:https://github.com/Isites/go-...
相關文章
相關標籤/搜索