2018 年,基于 QUIC 协议的 HTTP(HTTP over QUIC)也就是 HTTP/3,正式被确定为下一代网络规范。基于 QUIC 的 HTTP/3 的范例和概念没有改变。它含有头部(header)和正文(body),请求和回复,还有动词(verb)、Cookie 和缓存。HTTP/3 的主要改变是将这些报文以比特传送到另一端的方式。
为了使 HTTP 可以通过 QUIC 传输,协议的某些方面要进行修改,修改的结果便是 HTTP/3。这些必要修改是因 QUIC 与 TCP 在某些性质上有所不同所致的,修改包括:
相似之处:
不同之处:
HTTP/2 的连接需要 3 RTT,如果考虑会话复用,即把第一次握手算出来的对称密钥缓存起来,那么也需要 2 RTT,更进一步的,如果 TLS 升级到 1.3,那么 HTTP/2 连接需要 2 RTT,考虑会话复用则需要 1 RTT。有人会说 HTTP/2 不一定需要 HTTPS,握手过程还可以简化。这没毛病,HTTP/2 的标准的确不需要基于 HTTPS,但实际上所有浏览器的实现都要求 HTTP/2 必须基于 HTTPS,所以 HTTP/2 的加密连接必不可少。而 HTTP/3 首次连接只需要 1 RTT,后面的连接更是只需 0 RTT,意味着客户端发给服务端的第一个包就带有请求数据,这一点 HTTP/2 难以望其项背。
这样,QUIC 从请求连接到正式接发 HTTP 数据一共花了 1 RTT,这 1 个 RTT 主要是为了获取 Server Config,后面的连接如果客户端缓存了 Server Config,那么就可以直接发送 HTTP 数据,实现 0 RTT 建立连接。
HTTP/3 针对 QUIC 设计,所以它可以利用 QUIC 流的所有好处,通过 HTTP/3 传输的 HTTP 请求使用一系列的数据流完成。而 HTTP/2 不得不在 TCP 之上构建它的数据流和复用概念。
HTTP/3 中的数据帧种类不多且固定(截至 2018 年 12 月 18 日有九种)。最关键的帧可能是:
HTTP/3 中有一种帧是优先度(PRIORITY)。与 HTTP/2 中的类似,它用于设定一个流的优先度和依赖关系。该帧可以设定一个流依赖于另一个流,也可以设定特定流的 “权重”。
服务器应该只在一个流所依赖的所有流都被关闭,或者都无法取得进展时为该流分配资源。一个流的权重是介于 1 到 256 之间的值,有着相同父系流的流应该按照权重的比例分配资源。
客户端通过其发起的双向 QUIC 流来发送 HTTP 请求。一个请求包括一个 HEADERS 帧,之后可能有一两种其他的帧,例如:一系列的 DATA 帧,以及可能有一个作为末尾的 HEADERS 帧。发送一个请求后,客户端会关闭该数据流以进行发出。
服务器在双向流上发回其 HTTP 响应。其中含有一个 HEADERS 帧,一系列 DATA 帧,末尾可能有一个 HEADERS 帧。
HEADERS 含有用 QPACK 算法压缩的 HTTP 头部。QPACK 与 HTTP/2 中的 HPACK(RFC 7541)类似,并针对乱序流做了相应修改。QPACK 本身在两个端点间使用两个额外的单向 QUIC 流,用于在两个方向上传递动态表信息。
替代服务(alternative service)头部 Alt-svc: 和它相对应的 ALT-SVC HTTP/2 帧并不是特别为 QUIC 和 HTTP/3 设计的。它是为了让服务器可以告诉客户端:“看,我在这个主机的这个端口用这个协议提供相同的服务” 而设计的。详见 RFC 7838。
如果初始连接使用的是 HTTP/2(甚至 HTTP/1),服务器可以响应并告诉客户端它可以再试试 HTTP/3。连接可以指向相同主机或者不同但提供相同服务的主机。Alt-svc: 回复中有一个到期计时器,让客户端可以在指定的时间内使用建议的替代协议将后续的连接和请求直接发送给替代主机。
服务器推送实际上就是对一个未曾发出的客户端请求做出响应!
HTTP/3 的服务器推送与 HTTP/2(RFC 7540)类似,但机制上有所不同。
服务器推送仅在客户端同意的前提下才允许发出。在 HTTP/3 中,客户端甚至能通过通告给服务器的最大推送流 ID 来设置所接受推送的次数限制。超出限制将导致连接错误。
如果服务器端认为客户端可能需要某个并未要求但应该有的额外资源,服务器可以通过请求流发送一个 PUSH_PROMISE 帧,使该推送请求看上去像是一个响应,然后通过新的流发送实际响应。
虽然客户端之前已经表示过推送可接受,但如果客户端认为适合,每个推送流仍可以随时取消,然后发送一个 CANCEL_PUSH 帧到服务器。