Go语言的http/2服务器功能及客户端使用

2020-01-28 13:16:50刘景俊


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"))
}