30

Go http2 和 h2c

 5 years ago
source link: https://colobu.com/2018/09/06/Go-http2-和-h2c/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

了解一下http2和h2c (HTTP/2 over TCP,HTTP/2 without TLS)。

http/1.1 的服务器

我们经常会在代码中启动一个http服务器,最简单的http/1.1服务器如下所示:

http.Handle("/foo", fooHandler)

http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
})

log.Fatal(http.ListenAndServe(":8080", nil))

使用Go开发web服务非常的简单,快速。

http/1.1 的服务器 with TLS

如果想让http/1.1服务器支持TLS, 可以使用如下的代码:

http.Handle("/foo", fooHandler)

http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
})

log.Fatal(http.http.ListenAndServeTLS(":443", "server.crt", "server.key",nil))

至于 server.crtserver.key ,你可以使用你从CA购买的证书,你也可以使用下面的测试证书。

为了测试,你可以创建CA证书和你的服务器使用的证书。

1、 创建CA证书

$ openssl genrsa -out rootCA.key 2048
$ openssl req -x509 -new -nodes -key rootCA.key -days 1024 -out rootCA.pem

然后把 rootCA.pem 加到你的浏览器的证书中

2、 创建证书

$ openssl genrsa -out server.key 2048
$ openssl req -new -key server.key -out server.csr
$ openssl x509 -req -in server.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out server.crt -days 500

免费证书

如果你不想从CA花钱购买证书, 也不想配置测试证书,那么你可以使用 let's encrypt 的免费证书, 而且 let's encrypt 目前支持通配符证书,使用也是很方便的。

Go的扩展包中提供了 let's encrypt 的支持。

package main

import (
    "crypto/tls"
    "log"
    "net/http"

    "golang.org/x/crypto/acme/autocert"
)

func main() {
    certManager := autocert.Manager{
        Prompt:     autocert.AcceptTOS,
        HostPolicy: autocert.HostWhitelist("example.com"), 
        Cache:      autocert.DirCache("certs"),
    }

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello world"))
    })

    server := &http.Server{
        Addr: ":443",
        TLSConfig: &tls.Config{
            GetCertificate: certManager.GetCertificate,
        },
    }

    go http.ListenAndServe(":80", certManager.HTTPHandler(nil))

    log.Fatal(server.ListenAndServeTLS("", "")) //Key and cert are coming from Let's Encrypt
}

或者更简单的:

log.Fatal(http.Serve(autocert.NewListener("example.com"), handler))

看上面的例子, 把 example.com 换成你的域名,证书暂存在 certs 文件夹。autocert会定期自动刷新,避免证书过期。它会自动申请证书,并进行验证。

不过比较遗憾的是, autocert目前不支持通配符域名。

HostWhitelist returns a policy where only the specified host names are allowed. Only exact matches are currently supported. Subdomains, regexp or wildcard will not match.

通配符(ACME v2)的支持也已经完成了,但是迟迟未通过review,所以你暂时还不能使用这个特性。 ( issue#21081 )

HTTP/2

Go 在 1.6的时候已经支持 HTTP/2 了, 1.8 开始支持 PUSH 功能,你什么时候开始采用 HTTP/2 的呢?

Go的http/2使用也非常简单,但是必须和TLS一起使用。

package main

import (
	"log"
	"net/http"
	"time"

	"golang.org/x/net/http2"
)

const idleTimeout =5 * time.Minute
const activeTimeout =10 * time.Minute

func main() {
	var srv http.Server
	//http2.VerboseLogs = true
	srv.Addr = ":8972"

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("hello http2"))
	})

	http2.ConfigureServer(&srv, &http2.Server{})

	go func() {
		log.Fatal(srv.ListenAndServeTLS("server.crt", "server.key"))
	}()
	select {}
}

http2 封装并隐藏了http/2的处理逻辑,对于用户来说,可以不必关心内部的具体实现,想http/1.1一样简单的使用即可。

这里的证书可以使用上面提到证书,或者你购买的1证书,或者免费 let's encrypt 证书。

h2c

上面我们说Go的http/2必须使用TLS是不严谨的,如果你想不使用TLS,你可以使用最近添加的h2c功能。

package main

import (
	"fmt"
	"log"
	"net/http"

	"golang.org/x/net/http2"
	"golang.org/x/net/http2/h2c"
)

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "Hello h2c")
	})
	s := &http.Server{
        Addr:    ":8972",
        Handler: mux,
    }
    http2.ConfigureServer(s, &http2.Server{})
    log.Fatal(s.ListenAndServe())
}

使用起来也很简单,单数目前浏览器对http/2都是采用TLS的方式,所以用浏览器访问这个服务的话会退化为http/1.1的协议,测试的话你可以使用Go实现客户端的h2c访问。

客户端代码如下:

package main

import (
    "crypto/tls"
    "fmt"
    "log"
    "net"
    "net/http"

    "golang.org/x/net/http2"
)

func main() {
    client := http.Client{
        // Skip TLS dial
        Transport: &http2.Transport{
            AllowHTTP: true,
            DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
                return net.Dial(network, addr)
            },
        },
    }

    resp, err := client.Get("http://localhost:8972")
    if err != nil {
        log.Fatal(fmt.Errorf("error making request: %v", err))
    }
    fmt.Println(resp.StatusCode)
    fmt.Println(resp.Proto)
}

这个功能的讨论2016年就开始了( issue#14141 ),最终5月份的这个功能加上了,这样你就可以在没有TLS使用http/2高性能的特性了。

虽然有一些第三方的实现,但是显然使用官方库是更好的选择。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK