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{}
// Create a pool with the server certificate since it is not signed
// by a known CA
caCert, err := ioutil.ReadFile("server.crt")
if err != nil {
log.Fatalf("Reading server certificate: %s", err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
// Create TLS configuration with the certificate of the server
tlsConfig := &tls.Config{
RootCAs: caCertPool,
}
// Use the proper transport in the client
switch *httpVersion {
case 1:
client.Transport = &http.Transport{
TLSClientConfig: tlsConfig,
}
case 2:
client.Transport = &http2.Transport{
TLSClientConfig: tlsConfig,
}
}
// Perform the request
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 %sn",
resp.StatusCode, resp.Proto, string(body)
)
}
这一次我们得到了正确的回应:
$ go run h2-client.go Got response 200: HTTP/2.0 Hello
在服务器日志中,我们将看到正确的日志线:获得连接:Got connection: HTTP/2.0!!
但是当我们尝试使用HTTP/1.1传输时,会发生什么呢?
$ go run h2-client.go -version 1 Got response 200: HTTP/1.1 Hello
我们的服务器对HTTP/2没有任何特定的东西,所以它支持HTTP/1.1连接。这对于向后兼容性很重要。此外,服务器日志表明连接是HTTP/1.1:Got connection: HTTP/1.1。
HTTP/2 高级特性
我们创建了一个HTTP/2客户机-服务器连接,并且我们正在享受安全有效的连接带来的好处。但是HTTP/2提供了更多的特性,让我们来研究它们!
服务器推送
HTTP/2允许服务器推送“使用给定的目标构造一个合成请求”。
这可以很容易地在服务器处理程序中实现(在github上的视图):
func handle(w http.ResponseWriter, r *http.Request) {
// Log the request protocol
log.Printf("Got connection: %s", r.Proto)
// Handle 2nd request, must be before push to prevent recursive calls.
// Don't worry - Go protect us from recursive push by panicking.
if r.URL.Path == "/2nd" {
log.Println("Handling 2nd")
w.Write([]byte("Hello Again!"))
return
}
// Handle 1st request
log.Println("Handling 1st")
// Server push must be before response body is being written.
// In order to check if the connection supports push, we should use
// a type-assertion on the response writer.
// If the connection does not support server push, or that the push
// fails we just ignore it - server pushes are only here to improve
// the performance for HTTP/2 clients.
pusher, ok := w.(http.Pusher)
if !ok {
log.Println("Can't push to client")
} else {
err := pusher.Push("/2nd", nil)
if err != nil {
log.Printf("Failed push: %v", err)
}
}
// Send response body
w.Write([]byte("Hello"))
}










