HTTP 1.1 之前的实现就不讨论了,因为它们已经过时太久了,我上网的时候就已经接触不到了,所以主要说说 HTTP/1.1、HTTP/2.
HTTP/1.1#
HTTP/1.1 协议报文简介#
CRLF:
\r\n
METHOD: HTTP 请求,
GET
、POST
、PUT
、DELETE
...URI: 统一资源标识符,例如
/
,/index.html
...HTTPVersion: HTTP 协议的版本号,例如
HTTP/1.1
,HTTP/2
HEADERS: 请求头,例如
Host:localhost
,Accept: */*
.BODY: 请求体,例如说一个 JSON 数据
{"name":"fzdwx"}
HTTPStatus: HTTP 响应状态,常见的有
200
,404
等HTTPStatusDesc: HTTP 响应状态描述,
200
对应的OK
.
请求#
METHOD<SPACE>URI<SPACE>HTTPVersion
HEADERS
<CRLF>
BODY
示例:
GET /hello HTTP/1.1
Host: 192.168.1.107:8889
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.82 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
响应#
HTTPVersion HTTPStatus HTTPStatusDesc
HEADERS
<CRLF>
BODY
示例:
如果响应中使用了
transfer-encoding: chunked
这个来替代Content-Length
, 就表示这是一个不固定大小的响应,结尾通常用0\r\n
来分割.
HTTP/1.1 200 OK
transfer-encoding: chunked
content-type: application/json; charset=utf-8
0/r/n
HTTP/1.1 主要新特性#
- 默认是长连接 (
Connection: Keep-alive
), 支持一个 TCP 连接处理多个请求. - 缓存策略,在请求头中使用
Cache-Control
,Expires
,Last-Modified
,ETag
等来控制. - 允许响应分块,就是上面提到的
transfer-encoding: chunked
, 允许服务端可以多次返回响应体.
但是还是存在一定的问题,例如说如果有一个 TCP 连接阻塞了,还是会开启新的 TCP 连接进行处理请求.
H2#
HTTP2 中的主要概念:
Connection
: 一个 TCP 连接包含一个或多个Stream
, 所有的通讯都在一个 TCP 连接上完成.Stream
: 一个可以双向通讯的数据流,包含一条或多条Message
, 每个数据流都一个唯一标识符以及可选的优先级信息.Message
: 对应 HTTP/1.1 中的请求或响应,包含一条或多条Frame
.Frame
: 最小传输单位, 它以二进制进行编码.
在 HTTP/1.1 中是有Start Line
+ header
+ body
组成的,而在 H2 中是由一个HEADER Frame
以及多个DATA Frame
组成的.
Frame#
通常有一些公共的字段,例如Length
,Type
,Flags
以及Stream Id
;也各个类型所独有的字段.
分类如下:
- DATA: 用于传输 http 消息体.
- HEADERS: 用于传输首部字段.
- PRIORITY: 用于指定或重新指定引用资源的优先级.
- RST_STREAM: 用于通知流的非正常终止.
- SETTINGS: 用于约定客户端和服务端的配置数据。例如设置初识的双向流量控制窗口大小.
- PUSH_PROMISE: 服务端推送许可.
- PING: 用于计算往返时间,执行 “活性” 检活.
- GOAWAY: 用于通知对端停止在当前连接中创建流.
- WINDOW_UPDATE: 用于调整个别流或个别连接的流量.
- CONTINUATION: 专门用于传递较大 HTTP 头部时的持续帧.
为什么 H2 必须要走 HTTPS?#
这其实在 H2 标准中没有规定,主要是为了更方便的进行 HTTP 协议的 升级 / 协商,确认一个 Web 服务器是否支持 H2 通常有两种方式:
- 在请求头中设置
Upgrade: HTTP/2.0
以及Connection: Upgrade,HTTP2-Settings
等,类似升级到Websocket
. - 使用
TLS
中的ALPN
(Application Layer Protocol Negotiation, 应用层协议协商) 中的ALPN Next Protocol
字段,在Client Hello
与Server Hello
这个阶段就可以确定下来.
而现在的浏览器基本都是实现的方式二, 即与 HTTPS 绑定在一起. 但是如果我们不用浏览器进行访问,当然也可以不用 HTTPS.
详细可参考.
为什么 H2 能实现并行响应请求?#
在 HTTP/1.1 中,请求与响应是一一对应的,在同一个连接里,客户端依次发送两个请求,一段时间以后收到来自服务器的一个响应,这个响应一定是对应于第一个发出去的请求的.
因为没有一个标志来表示哪个响应对应哪个请求.
而在 H2 中基于Stream
和Frame
的设计: 每个Frame
都带有Stream Id
来标识是否为同一个Stream
里面的数据, 每个Stream
互不影响,这样就能做到在一个 TCP 里面连接里面传输多对请求 / 响应.
H2 的新特性#
H2 的对 HTTP/1.1 优化的核心就是 使用尽可能少的连接数.
- 多路复用:只用一个 TCP 连接就能处理多对 请求 / 响应,不用在开启另外的 TCP 连接,就是通过
Stream
与Frame
来实现的. - 二进制分帧:使用
Frame
为最小单位进行通讯,并采用二进制编码. - 头部压缩: 使用
HPACK
算法进行优化.在 HTTP/1 中消息体可以用 gzip 进行压缩,但是请求头通常没有任何压缩,有时候请求头的数据可能比请求体的数据还多.
- 请求优先级:一般在
HEADERS
帧与PRIORITY
帧中携带,通常依赖于服务端的支持程度.
工具#
生成测试签名#
go run $GOROOT/src/crypto/tls/generate_cert.go --host localhost
使用 curl 调试 HTTPS#
curl https://zcygov.cn -vv