Implementing HTTP/2 Server Push in Go
This article demonstrates how to add on‑demand and periodic HTTP/2 server‑push functionality in Go, explaining the required handlers, showing complete Go code examples, and detailing the HTTP/2 frame exchange observed with an h2c client.
In the previous article we learned how to send HTTP/2 headers from the server while keeping the connection alive; this article shows how to automatically issue server push.
The author from the 360 Search Technology Team provides a Go implementation that adds two handlers: /crt serving a certificate and /push that triggers a push of /crt to the client.
package main
import (
"html/template"
"log"
"net/http"
)
func main() {
http.HandleFunc("/header", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("X-custom-header", "custom header")
w.WriteHeader(http.StatusNoContent)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
select {}
})
// handler for /crt, returns certificate content for client push
http.HandleFunc("/crt", func(w http.ResponseWriter, r *http.Request) {
tpl := template.Must(template.ParseFiles("server.crt"))
tpl.Execute(w, nil)
})
// request this path to trigger push of /crt
http.HandleFunc("/push", func(w http.ResponseWriter, r *http.Request) {
pusher, ok := w.(http.Pusher)
if !ok {
log.Println("not support server push")
} else {
err := pusher.Push("/crt", nil)
if err != nil {
log.Printf("Failed for server push: %v", err)
}
}
w.WriteHeader(http.StatusOK)
})
log.Println("start listen on 8080...")
log.Fatal(http.ListenAndServeTLS(":8080", "server.crt", "server.key", nil))
}Running the server with TLS and using the h2c client demonstrates the exchange of HEADERS, PUSH_PROMISE, and DATA frames, with the client receiving the promised resource without an explicit request.
An automatic push example uses a timer (time.Tick) inside the handler to repeatedly push /crt every 5 seconds, incrementing the promised stream IDs by 2 each time.
http.HandleFunc("/autoPush", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("X-custom-header", "custom")
w.WriteHeader(http.StatusNoContent)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
pusher, ok := w.(http.Pusher)
if ok {
for {
select {
case <-time.Tick(5 * time.Second):
err := pusher.Push("/crt", nil)
if err != nil {
log.Printf("Failed for server push: %v", err)
}
}
}
}
})The article also shows the internal Go HTTP/2 implementation of the Push method, how a PUSH_PROMISE frame is constructed, and how the server processes push requests via the serve loop.
By following these examples, developers can implement both on‑demand and periodic server‑push functionality in Go.
360 Tech Engineering
Official tech channel of 360, building the most professional technology aggregation platform for the brand.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.