Dev Life

Beispiel für einen HTTP/2 Cleartext (H2C) Client in Go

Erfahren Sie, was über den H2C-Support in Golang herausgefunden wurde und wie Sie den Standard-HTTP/2-Client von Golang mit einem H2C-fähigen Server verbinden. Weiterlesen...
Bild für Beispiel für einen HTTP/2 Cleartext (H2C) Client in Go

Dieser Blogbeitrag wurde von einem Mitglied unseres Entwicklungsteams verfasst und selbst veröffentlicht. Sie finden ihn auch auf Mittel.

Da meine Internetrecherche ins Leere lief und das einzige brauchbare Beispiel für einen H2C-Client in der eigentlichen Go-Testsuite zu finden war, werde ich hier darlegen, was ich über den H2C-Support in Golang herausgefunden habe.

Zunächst einmal, falls Sie sich fragen, was H2C ist: Es ist im Grunde HTTP/2 ohne TLS. H2C ist verständlicherweise nicht sehr verbreitet, da HTTP/2 mit TLS sicherer ist und keine Angriffsfläche für bestimmte Arten von Attacken bietet. H2C hat jedoch durchaus seine Anwendungsfälle; so nutzt GRPC beispielsweise H2C, wenn Sie einen Client mit grpc.WithInsecure(). erstellen.

Zweitens unterstützt der Standard-Golang-Code zwar HTTP/2, bietet aber keinen direkten Support für H2C. H2C-Support gibt es nur im Paket golang.org/x/net/http2/h2c . Sie machen Ihren HTTP-Server H2C-fähig, indem Sie Ihren Handler oder Mux folgendermaßen in h2c.NewHandler() kapseln.

                                

                                    h2s := &http2.Server{}

handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, %v, http: %v", r.URL.Path, r.TLS == nil)
})

server := &http.Server{
    Addr:    "0.0.0.0:1010",
    Handler: h2c.NewHandler(handler, h2s),
}

fmt.Printf("Listening [0.0.0.0:1010]... ")
checkErr(server.ListenAndServe(), "while listening")

                                
                            

Der obige Code ermöglicht es dem Server, H2C-Upgrades und H2C Prior Knowledge zusammen mit dem standardmäßigen HTTP/2 und HTTP/1.1 zu unterstützen, die von Golang nativ unterstützt werden.

Wenn Ihnen der Support für HTTP/1.1 nicht wichtig ist, führen Sie diesen Code aus, der nur H2C Prior Knowledge unterstützt.

                                

                                    server := http2.Server{}

l, err := net.Listen("tcp", "0.0.0.0:1010")
checkErr(err, "while listening")

fmt.Printf("Listening [0.0.0.0:1010]... ")

for {
    conn, err := l.Accept()
    checkErr(err, "during accept")

    server.ServeConn(conn, &http2.ServeConnOpts{
        Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            fmt.Fprintf(w, "Hello, %v, http: %v", r.URL.Path, r.TLS == nil)
        }),
    })
}

                                
                            

Sobald der Server läuft, installieren Sie curl-OpenSSL und verwenden curl, um Ihren H2C-fähigen Server zu überprüfen.

                                

                                    $ brew install curl-openssl

# Add curl-openssl to the front of your PATH
$ export PATH="/usr/local/opt/curl-openssl/bin:$PATH"

                                
                            

Über HTTP/1.1 verbinden und dann Upgrade auf HTTP/2 (H2C) durchführen

                                

                                    $ curl -v --http2 http://localhost:1010

* Trying ::1:1010...
* TCP_NODELAY set
* Connected to localhost (::1) port 1010 (#0)

> GET / HTTP/1.1
> Host: localhost:1010
> User-Agent: curl/7.65.0
> Accept: */*
> Connection: Upgrade, HTTP2-Settings
> Upgrade: h2c
> HTTP2-Settings: AAMAAABkAARAAAAAAAIAAAAA
>
* Mark bundle as not supporting multiuse

< HTTP/1.1 101 Switching Protocols
< Connection: Upgrade
< Upgrade: h2c
* Received 101
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!

< HTTP/2 200
< content-type: text/plain; charset=utf-8
< content-length: 20
< date: Wed, 05 Jun 2019 19:01:40 GMT
<
Hello, /, http: true

                                
                            

Verbindung über HTTP/2 (H2C)

                                

                                    curl -v --http2-prior-knowledge http://localhost:1010

* Trying ::1:1010...
* TCP_NODELAY set
* Connected to localhost (::1) port 1010 (#0)
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7fdab8007000)

> GET / HTTP/2
> Host: localhost:1010
> User-Agent: curl/7.65.0
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!

< HTTP/2 200
< content-type: text/plain; charset=utf-8
< content-length: 20
< date: Wed, 05 Jun 2019 19:00:43 GMT
<
Hello, /, http: true

                                
                            

Erinnern Sie sich, dass ich gesagt habe, dass die Golang-Standardbibliothek H2C nicht unterstützt? Obwohl das technisch gesehen richtig ist, gibt es einen Workaround, um den Standard-HTTP/2-Client von Golang mit einem H2C-fähigen Server zu verbinden.

Dazu müssen Sie DialTLS überschreiben und das streng geheime Flag AllowHTTP setzen.

                                

                                    client := http.Client{
    Transport: &http2.Transport{
        // So http2.Transport doesn't complain the URL scheme isn't 'https'
        AllowHTTP: true,

        // Pretend we are dialing a TLS endpoint.
        // Note: we ignore the passed tls.Config
        DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
            return net.Dial(network, addr)
        },
    },
}

resp, _ := client.Get(url)
fmt.Printf("Client Proto: %d\n", resp.ProtoMajor)

                                
                            

Obwohl das alles ein wenig abenteuerlich aussieht, funktioniert es tatsächlich sehr gut und liefert in Produktionsumgebungen eine hervorragende Leistung.

Ein vollständiges, funktionierendes Beispiel finden Sie hier http://github.com/thrawn01/h2c-golang-example.