go http证书_HTTP/3 in Go
写在前面,目前 HTTP/3 协议仍在实验阶段,RFC 进行到了第 29 号版本,将于 2020/12/11 日失效,本文所讨论的均为已经达成一致的部分;前半部分介绍了一些简单的理论,后半部分进行代码实现,希望通过代码实现能够让你对 HTTP/3 有个大概的概念,在后续的概念普及中有所印象。概念部分本篇的概念部分主要介绍协议的升级方式,由于在协议普及过程中,互联网中存在着大量的旧服务应用...
写在前面,目前 HTTP/3 协议仍在实验阶段,RFC 进行到了第 29 号版本,将于 2020/12/11 日失效,本文所讨论的均为已经达成一致的部分;前半部分介绍了一些简单的理论,后半部分进行代码实现,希望通过代码实现能够让你对 HTTP/3 有个大概的概念,在后续的概念普及中有所印象。
概念部分
本篇的概念部分主要介绍协议的升级方式,由于在协议普及过程中,互联网中存在着大量的旧服务应用,必须提供一种从低版本协商升级到新版本协议的方式。
我们首先复习一下 HTTP1.1 升级到 HTTP2的方式,分为 h2c 和 h2 ;然后我们会聊到 HTTP Alternative Services(HTTP 替代服务),这是一个很有趣的协议,然后举例 HTTP/3 是如何通过 ALTSVC 协议进行自举的。
0x01 H2C
如果你在浏览器中输入 HTTP 的域名,未进入 HTTPS 协议,如果浏览器启用 HTTP/2,那么会在发起时设置 Upgrade 和 HTTP2-Settings:
GET / HTTP/1. 1
Host: server. example. com
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c
HTTP2-Settings:
服务端如果支持 HTTP/2,则响应:
HTTP/1. 1 101 Switching Protocols
Connection: Upgrade
Upgrade: h2c
客户端在接收到 101 响应后,就会发起 HTTP/2 的第一个帧(SETTINGS 帧),即开始 HTTP/2。
0x02 H2
如果你在浏览器输入的是 HTTPS 域名或 URL,则浏览器会在启用 HTTP/2 时,进行 H2 的升级协商。
H2 协商建立在 TLS 之上,采用 ALPN 扩展协议,采用 h2 作为协议标识符,由于通过 TLS 所以称之为 安全版本。
流程如下:
-
客户端和服务端 TCP 握手
-
客户端和服务端 TLS 层协商
-
客户端发送链接序言(
SETTINGS帧) -
服务端响应链接序言
-
双方各自确认序言
-
其他帧传输
下面的一张图提供了一个简单对比,可以参考:

图片来自:《HTTP/2 笔记之连接建立》
H2C 协商升级,是为了 HTTP/1.1 的广泛存在,在未启用 TLS 时提供一种协议切换的升级,HTTPS 则是一个强制的协商,在 TLS 的 ALPN 扩展中直接完成协议的交换。
HTTP/1.1 升级到 HTTP/2 用了近十六年的时间,中间经过了很多变迁,网际中存在着对 HTTP/1.1 和 TCP 大量优化和应用,由于 HTTP/1.1 是文本协议,HTTP/2 是二进制协议,两者直接跨度很大,所以设计了两种对应的协议迁移方式;又由于此,HTTP/2 并非全部基于安全协议。
0x03 HTTP Alternative Services
HTTP Alternative Services(HTTP 替代服务)是发布于 2016 年上半年的一份 RFC7838,协议约定当前请求的资源,可以通过备用主机,端口或者协议获取。
在当前的架构中,我们常用的分流方式一般分为两种:
-
DNS 分区域或供应商解析不同 IP
-
SLB 进行负载均衡,大多数有专有机器提供服务
HTTP Alternative Services 就提供了另一种方式,当支持协议的客户端请求服务器时,服务器按照下面的格式响应,可直接将请求负载到其他主机或端口,并且此时浏览器 URL 等显示不变,即可以实现用户在原来的地址,但是流量导流到其他机器。
不同于使用 30x 状态码进行重定向分流,HTTP Alternative Services 只改变浏览器获取资源的网络方式,上层应用不会感觉到任何变化。
HTTP Alternative Services 协议 定义 HTTP 头部如下:
Alt-Svc: h2="alt.example.com:8000", h2=":443"; ma=2592000; persist=1
h2 代表着 HTTP/2,ma 是 max-age 的缩写,persist 表示在遇到网络变更时依然有效。
在 HTTP/1 中,Alt-Svc 必须在首次响应的头部返回,只有在第二次请求时,浏览器才会使用备用服务,在 HTTP2 中新增 ALTSVC 帧,可以单独发送,浏览器可以在首次请求就切换为备用服务。
0x04 H3
由于互联网中存在大量不可预知的情况,所以正如 HTTP/1 到 HTTP/2 一样,需要有一种协议协商的手段,让浏览器知道什么时候可以使用 HTTP/3,所以 H3 也一样规定了协议协商的方式。
HTTP/3 通过 ALTSVC 协议实现自举,告知浏览器其已支持 HTTP/3,浏览器可切换至 UDP 的方式进行通讯。
Alt-Svc: h3-29=":4443"; ma=2592000
根据 draft-ietf-quic-http-29#section-3.1 《Draft Version Identification》章节所述:
HTTP/3 uses the token "h3" to identify itself in ALPN and Alt-Svc.
Only implementations of the final, published RFC can identify themselves as "h3". Until such an RFC exists, implementations MUST NOT identify themselves using this string.
在 HTTP/3 正式发布之前,必须不能使用 h3 作为相关标识,所以做为第 29 版实验支持版本,上面协议部分标识为 h3-29;自举样例表示含义,本服务支持 h3 的第 29 号实验版本,通过与响应相同的 IP 的 4443 端口进行通信,有效期 2592000 秒。
基于此,浏览器可以在 HTTP/2 甚至是 HTTP/1 中完成自举,告知浏览器自举已经支持 HTTP/3,同上文中提到的一样,在 HTTP/2 中,可以实现在首次请求时切换协议,而在 HTTP/1 中,只能在第二次请求中切换到新的协议(在实验中,大多是在第二次请求才切换 HTTP/3)。
实验部分
由于 HTTP/3 通过 QUIC 实现通讯,QUIC 使用 TLS 1.3 传输层安全协议(RFC 8446),且不存在非加密版本;所以在本实验中,你必须提前准备秘钥和证书,你可以申请正式的秘钥和证书,也可以根据《Go Implement HTTPS》 生成自签名证书和秘钥。
上篇中,我们通过自签名在 Go 语言下实现了 HTTPS Service;本节我们通过实验的方式,一步步来了解 HTTP3;如果你没有读过,那么建议你阅读一下,当然如果你对自签名的原理和用法十分清晰,那么直接进行本节也是没有任何难度的。
0x05 HTTPS
我们先实现一个最简单的 HTTPS Service:
package main
import (
"fmt"
"net/http"
)
func main() {
// 请注意匹配正确的路径 certFile := "/Users/Shared/go/quic/tls/server.crt"
keyFile := "/Users/Shared/go/quic/tls/server.key"
handle := func(w http.ResponseWriter, req *http.Request) {
_, _ = fmt.Fprintf(w, "hello world")
}
mux := http.NewServeMux()
mux.HandleFunc("/", handle)
err := http.ListenAndServeTLS(":443", certFile, keyFile, mux)
fmt.Println(err)
}
请替换上面代码中的证书和私钥,你可以通过《Go Implement HTTPS》章节的内容进行自签名,并将 CA 证书导入对应的浏览器或系统证书,并选择信任;或者你可以去申请正式的证书,并通过本地 HOST 指向去测试。
你可以直接通过 IDE 直接运行或者在对应的目录下执行 go run main.go,执行后控制台没有任何输出,但是请注意由于 443 端口,可能需要 sudo 权限,这取决于你运行的系统和用户。
如果你正确的导入了信任证书,或者使用了正式的可信证书,那么此时你用浏览器访问 https://localhost 应该能够正确的看到 hello world,或者通过 curl https://localhost/ -i -k (-k 用户忽略证书验证,否则你需要通过 --cacert 指定 CA 证书位置),那么你可以看到以下输出:
HTTP/2 200
content-type: text/html; charset=utf-8
content-length: 37
date: Mon, 10 Aug 2020 10:05:36 GMT
hello world</body></html>
通过 CURL 的 -i 参数,我们看到了,在配置证书和私钥文件后,Go http Server 会优先为我们启用 HTTP2。
请注意,到这一步我们应该已经成功的实现了 HTTP2(这里我们暂不考虑 H2C)。
0x06 Quic-go
quic-go is an implementation of the QUIC protocol in Go. It roughly implements the IETF QUIC draft, although we don't fully support any of the draft versions at the moment.
go get github.com/lucas-clemente/quic-go
Quic-go 是 Go 语言实现的 QUIC 协议,知名 Caddy 服务器软件就是基于此包实现的 HTTP/3 协议的支持,接下来让我们通过样例,来将 HTTP/3 跑起来,让 HTTP/3 看起来离我们并不远。
package main
import (
"fmt"
"net/http"
"github.com/lucas-clemente/quic-go/http3"
)
func main() {
certFile := "/Users/Shared/go/quic/tls/server.crt"
keyFile := "/Users/Shared/go/quic/tls/server.key"
handle := func(w http.ResponseWriter, req *http.Request) {
_, _ = fmt.Fprintf(w, "hello world")
}
mux := http.NewServeMux()
mux.HandleFunc("/", handle)
err := http3.ListenAndServe(":443", certFile, keyFile, mux)
fmt.Println(err)
}
什么?这与上面的 HTTPS Service 有什么区别?
是的,只有 http3.ListenAndServe 从 http 变为 http3 并引入了 github.com/lucas-clemente/quic-go/http3 包,但是我却用了很长时间才将样例代码跑起来。
接下来我简单的介绍下 quic-go 包的实现:
-
首先 提供了
ListenAndServe,ListenAndServeQUIC,以及Service去自己实现Service.ListenAndServe -
http3.ListenAndServe加载了证书和秘钥,并同时监听了 Addr 的 TCP 和 UDP 连接 -
并且为 TCP 的
Handle注册一个全局SetQuicHeaders实现ALTSVC自举 -
TCP 连接则有
http包实现Service,UDP 有quic-go实现Service -
quic-go的Service有着与http的Service类似的接口,监听端口后进行处理
0x07 验证
上面的代码,你可能已经直接 Run 起来了,可是却不知道怎么验证,是否成功?
我在准备文章之前曾尝试几种方式:
-
新版本的 CURL,有依赖问题
-
Curl + Quiche,编译麻烦
-
http3-client,同样需要编译
-
谷歌浏览器,需要导入 CA 到系统证书
最后,我选择了 火狐浏览器,并且推荐使用 开发者版 Firefox Developer Edition ;你可以前往下面的地址进行下载,然后安装:
https://www.mozilla.org/zh-CN/firefox/developer/
安装完成后,需要进行下面的配置:
-
在 URL 地址栏输入:
about:config -
在弹出的
三思而后行提示页面,点击接受风险并继续 -
在
搜索首选项输入框中输入:http3 -
双击
network.http.http3.enabled所在行,启用HTTP3
此时,在地址栏中,输入我们在代码中使用的证书域名名称,此时注意域名前是 https 并注意端口号是否和代码一致,然后页面右键点击 检查元素,打开开发者工具,切换到 网络 栏,点击页面刷新,然后我们选择请求记录,查看请求 协议版本 ;如果顺利,你就可以看到下面的图了。

引用
-
RFC7838: HTTP Alternative Services
-
HTTP Alternative Services 介绍
-
HTTP/2 笔记之连接建立
-
HTTPS 深入浅出 - 什么是 ALPN?
-
Hypertext Transfer Protocol Version 3 (HTTP/3)[draft-ietf-quic-http-29]
-
Get a head start with QUIC
-
一文看完 HTTP3 的演化历程
-
如何玩转 HTTP 3?
-
科普:QUIC 协议原理分析
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐



所有评论(0)