Golang尝试官方示例中的HTTP2推送功能但未成功

Golang尝试官方示例中的HTTP2推送功能但未成功 代码示例如下:

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        pusher, ok := w.(http.Pusher)
        if ok {
            // 将资源推送到客户端
            if err := pusher.Push("/styles.css", nil); err != nil {
                log.Printf("推送失败: %v", err)
            }
            if err := pusher.Push("/app.js", nil); err != nil {
                log.Printf("推送失败: %v", err)
            }
        }

        // 返回HTML响应
        w.Header().Set("Content-Type", "text/html; charset=utf-8")
        fmt.Fprintf(w, "<html><head><title>HTTP/2 Push Example</title><link rel=\"stylesheet\" href=\"/styles.css\"><script src=\"/app.js\"></script></head><body><h1>Hello, HTTP/2 Push!</h1></body></html>")
    })

    log.Println("HTTP服务器正在监听 :8080")
    log.Fatal(http.ListenAndServeTLS(":8080", "server.crt", "server.key", nil))
}

而 app.js 的内容是 console.log("Hello http2!")

但是当我使用 Chrome 访问 https://localhost:8080 时,app.js 文件中显示的内容却类似于: <html><head><title>HTTP/2 Push Example</title><link rel=\"stylesheet\" href=\"/styles.css\"><script src=\"/app.js\"></script></head><body><h1>Hello, HTTP/2 Push!</h1></body></html>

这是怎么回事?

更有趣的是,如果我直接使用 Gin 框架的 HTTP/2 推送示例,它却能正常工作。


更多关于Golang尝试官方示例中的HTTP2推送功能但未成功的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang尝试官方示例中的HTTP2推送功能但未成功的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


问题在于你的HTTP/2推送代码逻辑。当浏览器请求/时,你同时推送了CSS和JS文件,但浏览器随后会正常请求这些资源,导致重复响应。

以下是修正后的示例:

func main() {
    // 为静态资源添加单独的处理
    http.HandleFunc("/styles.css", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "text/css")
        fmt.Fprint(w, "body { background: #f0f0f0; }")
    })

    http.HandleFunc("/app.js", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/javascript")
        fmt.Fprint(w, `console.log("Hello http2!");`)
    })

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        // 只在HTTP/2连接上尝试推送
        if r.ProtoMajor == 2 {
            pusher, ok := w.(http.Pusher)
            if ok {
                // 推送前检查浏览器是否已缓存
                if err := pusher.Push("/styles.css", &http.PushOptions{
                    Method: "GET",
                    Header: http.Header{
                        "Accept-Encoding": r.Header["Accept-Encoding"],
                    },
                }); err != nil {
                    log.Printf("推送CSS失败: %v", err)
                }
                
                if err := pusher.Push("/app.js", &http.PushOptions{
                    Method: "GET",
                    Header: http.Header{
                        "Accept-Encoding": r.Header["Accept-Encoding"],
                    },
                }); err != nil {
                    log.Printf("推送JS失败: %v", err)
                }
            }
        }

        // 返回HTML响应
        w.Header().Set("Content-Type", "text/html; charset=utf-8")
        fmt.Fprintf(w, `<html>
            <head>
                <title>HTTP/2 Push Example</title>
                <link rel="stylesheet" href="/styles.css">
                <script src="/app.js"></script>
            </head>
            <body>
                <h1>Hello, HTTP/2 Push!</h1>
            </body>
        </html>`)
    })

    log.Println("HTTP服务器正在监听 :8080")
    log.Fatal(http.ListenAndServeTLS(":8080", "server.crt", "server.key", nil))
}

关键修改:

  1. /styles.css/app.js添加了独立的处理函数
  2. 添加了r.ProtoMajor == 2检查确保只在HTTP/2连接上推送
  3. 使用http.PushOptions传递适当的请求头
  4. 修正了HTML字符串的引号转义问题

使用以下命令生成测试证书:

openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt -days 365 -nodes -subj "/C=US/ST=State/L=City/O=Org/CN=localhost"

访问https://localhost:8080并检查Chrome开发者工具的Network标签,应该能看到app.jsstyles.css的Type显示为"Push"。

回到顶部