Backend Development 12 min read

Using Go's HTTP/2 Server and Client: TLS Setup, Server Push, and Full‑Duplex Communication

This article demonstrates how to build an HTTP/2 server and client in Go, covering TLS certificate generation, server and client implementations, switching between HTTP/1.1 and HTTP/2, server‑push handling, and full‑duplex echo communication with complete code examples.

360 Tech Engineering
360 Tech Engineering
360 Tech Engineering
Using Go's HTTP/2 Server and Client: TLS Setup, Server Push, and Full‑Duplex Communication

Go's standard library HTTP server supports HTTP/2 out of the box. The article first shows how to create a simple HTTP/2 server, generate a self‑signed TLS certificate using openssl req -newkey rsa:2048 -nodes -keyout server.key -x509 -days 365 -out server.crt , and start the server with TLS.

package main
import (
    "log"
    "net/http"
)
func main() {
    srv := &http.Server{Addr: ":8000", Handler: http.HandlerFunc(handle)}
    log.Printf("Serving on https://0.0.0.0:8000")
    log.Fatal(srv.ListenAndServeTLS("server.crt", "server.key"))
}

func handle(w http.ResponseWriter, r *http.Request) {
    log.Printf("Got connection: %s", r.Proto)
    w.Write([]byte("Hello"))
}

The client side uses Go's http.Client with either the standard http.Transport for HTTP/1.1 or http2.Transport for HTTP/2. Because the server certificate is self‑signed, the client loads the certificate into a custom tls.Config and adds it to a certificate pool.

package main
import (
    "crypto/tls"
    "crypto/x509"
    "flag"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "golang.org/x/net/http2"
)

const url = "https://localhost:8000"
var httpVersion = flag.Int("version", 2, "HTTP version")

func main() {
    flag.Parse()
    client := &http.Client{}
    caCert, err := ioutil.ReadFile("server.crt")
    if err != nil { log.Fatalf("Reading server certificate: %s", err) }
    caCertPool := x509.NewCertPool()
    caCertPool.AppendCertsFromPEM(caCert)
    tlsConfig := &tls.Config{RootCAs: caCertPool}
    switch *httpVersion {
    case 1:
        client.Transport = &http.Transport{TLSClientConfig: tlsConfig}
    case 2:
        client.Transport = &http2.Transport{TLSClientConfig: tlsConfig}
    }
    resp, err := client.Get(url)
    if err != nil { log.Fatalf("Failed get: %s", err) }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil { log.Fatalf("Failed reading response body: %s", err) }
    fmt.Printf("Got response %d: %s %s\n", resp.StatusCode, resp.Proto, string(body))
}

The article explains how the client can switch between HTTP/1.1 and HTTP/2 using a command‑line flag, and shows the different log output for each protocol. It also discusses why Go's client does not support server push, while browsers like Chrome do.

Server push is demonstrated with a handler that checks for the http.Pusher interface, pushes a secondary request "/2nd", and writes responses for both the initial and pushed requests. The example shows that push may fail if the client disables it.

func handle(w http.ResponseWriter, r *http.Request) {
    log.Printf("Got connection: %s", r.Proto)
    if r.URL.Path == "/2nd" {
        log.Println("Handling 2nd")
        w.Write([]byte("Hello Again!"))
        return
    }
    log.Println("Handling 1st")
    pusher, ok := w.(http.Pusher)
    if ok {
        if err := pusher.Push("/2nd", nil); err != nil {
            log.Printf("Failed push: %v", err)
        }
    } else {
        log.Println("Can't push to client")
    }
    w.Write([]byte("Hello"))
}

Full‑duplex communication is illustrated with an echo handler that flushes response headers immediately and then copies the request body to the response using a custom writer that calls http.Flusher after each write.

type flushWriter struct { w io.Writer }
func (fw flushWriter) Write(p []byte) (int, error) {
    n, err := fw.w.Write(p)
    if f, ok := fw.w.(http.Flusher); ok { f.Flush() }
    return n, err
}

func echoCapitalHandler(w http.ResponseWriter, r *http.Request) {
    if f, ok := w.(http.Flusher); ok { f.Flush() }
    io.Copy(flushWriter{w: w}, r.Body)
}

Finally, the article summarizes that Go's standard library supports HTTP/2 with TLS, server push (though not in the default client), and full‑duplex communication, while still being compatible with HTTP/1.1.

Backend DevelopmentGoTLSHTTP/2Server PushFull-duplex
360 Tech Engineering
Written by

360 Tech Engineering

Official tech channel of 360, building the most professional technology aggregation platform for the brand.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.