前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go每日一库之126:h2c(明文https)

Go每日一库之126:h2c(明文https)

作者头像
luckzack
发布2023-09-30 08:39:58
6020
发布2023-09-30 08:39:58
举报
文章被收录于专栏:人人都是架构师

背景

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

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

代码语言:javascript
复制
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "HTTP协议: %s\n", r.Proto)
	})
http.ListenAndServe(":8080",nil)

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

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

代码语言:javascript
复制
HTTP协议: HTTP/1.1

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

代码语言:javascript
复制
openssl req -newkey rsa:2048 -nodes -keyout server.key -x509 -days 365 -out server.crt

openssl命令的详细解释

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

代码语言:javascript
复制
 server.crt   // 证书文件
 server.key   // 服务端私钥

关于https和证书

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

代码语言:javascript
复制
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "HTTP协议: %s\n", r.Proto)
	})
http.ListenAndServeTLS(":8080", "server.crt", "server.key", nil)

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

代码语言:javascript
复制
curl https://localhost:8080 -k

( -k参数是允许不使用证书到SSL站点) 在浏览器中输入网址后,因为我们的证书是自己生成的,所以浏览器会认为有风险进行拦截,需要在拦截后点击一下继续访问的按钮~

此时的响应是

代码语言:javascript
复制
HTTP协议: HTTP/2.0

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

主角h2c来了

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

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

代码语言:javascript
复制
package main

import (
	"fmt"
	"golang.org/x/net/http2"
	"golang.org/x/net/http2/h2c"
	"net/http"
	"log"
)

func main(){
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "Hello world")
		fmt.Fprintf(w, "Protocol: %s\n", r.Proto)
	})
	h2s := &http2.Server{
		// ...
	}
	h1s := &http.Server{
		Addr:    ":8080",
		Handler: h2c.NewHandler(handler,h2s),
	}
	log.Fatal(h1s.ListenAndServe())

}

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

代码语言:javascript
复制
	client := http.Client{
		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("https://localhost:8080")
	if err != nil {
		log.Fatalf("请求失败: %s", err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatalf("读取响应失败: %s", err)
	}
	fmt.Printf("获取响应 %d: %s\n", resp.StatusCode, string(body))

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

代码语言:javascript
复制
获取响应 200: Protocol: HTTP/2.0

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 主角h2c来了
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档