net/http包默认支持 http2 的,而 HTTP/2 强制使用 TLS 的,所以在使用的时候必须指定证书,正好最近玩过,在这写一下:

    原本实现一个 http 服务的代码:

    1. http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    2. fmt.Fprintf(w, "HTTP协议: %s\n", r.Proto)
    3. })
    4. http.ListenAndServe(":8080",nil)

    这里的路径为 /, 将会响应本次连接的 HTTP 协议

    启动后在浏览器访问 http://localhost: 8080,或使用 curl 命令: curl http://localhost:8080, 将会得到如下响应:

    这是因为使用的 http 连接,现在咱们把他变成 https 试试,既然要使用 https,那么我们需要一个证书,在生产环境中可以使用 certbot去向 CA 机构申请证书,这里既然我们只是用作测试,可以直接使用如下命令生成证书:

    1. openssl req -newkey rsa:2048 -nodes -keyout server.key -x509 -days 365 -out server.crt

    关于openssl命令的详细解释在这里:

    openssl 命令_Linux openssl 命令用法详解:强大的安全套接字层密码库man.linuxde.net

    输入上述命令后,会提示你输入一些信息,国家名啊公司名啊之类的,全部随便填就可以,最后一项是主机名,本机的话填 localhost 就行了,然后会在当前目录下生成两个文件:

    1. server.crt // 证书文件
    2. server.key // 服务端私钥

    关于 https 和证书:

    浅谈 https\ssl\数字证书 - P_Chou - 博客园www.cnblogs.com如何用Golang实现一个HTTP2(h2c)的server?? - 知乎 - 图1

    下面我们就让我们的服务端使用刚刚生成的证书:

    1. http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    2. fmt.Fprintf(w, "HTTP协议: %s\n", r.Proto)
    3. })
    4. http.ListenAndServeTLS(":8080", "server.crt", "server.key", nil)

    我们将原来的ListenAndServe()方法改为了ListenAndServeTLS(),传入证书和私钥的路径,此时再来试试,在浏览器中输入 https://localhost:8080或者在命令行中输入如下命令:

    1. curl https://localhost:8080 -k

    ( -k参数是允许不使用证书到 SSL 站点)

    在浏览器中输入网址后,因为我们的证书是自己生成的,所以浏览器会认为有风险进行拦截,需要在拦截后点击一下继续访问的按钮~

    此时的响应是

    ok, 到此实现了一个 HTTP2 的 server

    以上是 HTTP/2(h2),还有一种 HTTP/2 Cleartext(h2c), 是一种不需要 TLS 也可以支持 HTTP/2 的方式,但是 Go 并不鼓励使用它,使用它需要安装 h2c,这个包的用处,在于当客户端主动发起 HTTP/2 连接时,会使用 h2 协议,如果不是 HTTP/2 连接,就会选择 HTTP/1.x 版本的协议了

    下面是使用方法 (在 h2c 的 GoDoc 里也有):

    1. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    2. fmt.Fprint(w, "Hello world")
    3. fmt.Fprintf(w, "Protocol: %s\n", r.Proto)
    4. })
    5. h2s := &http2.Server{
    6. // ...
    7. }
    8. h1s := &http.Server{
    9. Addr: ":8080",
    10. Handler: h2c.NewHandler(handler,h2s),
    11. }
    12. log.Fatal(h1s.ListenAndServe())

    那现在的问题就在于我们如何让客户端发起一个 HTTP/2 请求呢?用浏览器是没辙了,浏览器对 HTTP/2 都是使用 TLS 的,如果不是 https 请求,会退化为 HTTP/1.1,curl命令也算了,主动发起 H2 请求需要安装nghttp2的 c 扩展包,太折腾了 ,我们可以使用Go来让客户端主动发起 HTTP/2 请求:

    1. client := http.Client{
    2. Transport: &http2.Transport{
    3. AllowHTTP: true,
    4. DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
    5. return net.Dial(network, addr)
    6. },
    7. },
    8. }
    9. resp, err := client.Get("http://localhost:8080")
    10. if err != nil {
    11. log.Fatalf("请求失败: %s", err)
    12. }
    13. defer resp.Body.Close()
    14. body, err := ioutil.ReadAll(resp.Body)
    15. if err != nil {
    16. log.Fatalf("读取响应失败: %s", err)
    17. }
    18. fmt.Printf("获取响应 %d: %s\n", resp.StatusCode, string(body))

    调用客户端使用 http 访问服务,会打印出

    1. 获取响应 200: Protocol: HTTP/2.0

    以上就是 H2 和 H2C 的用法,H2C 虽然可以不使用 TLS 进行 HTTP2 连接,但是我们的大部分应用场景在浏览器中,浏览器必须使用 TLS,所以还是尽量使用 TLS 的方式吧~

    点个赞再走呗~
    https://www.zhihu.com/question/305961226