RFC:https://tools.ietf.org/html/rfc7540
RFC 中英文对照:https://github.com/fex-team/http2-spec/blob/master/HTTP2%E4%B8%AD%E8%8B%B1%E5%AF%B9%E7%85%A7%E7%89%88(06-29).md
O'Reilly :https://hpbn.co/http2/
SPDY:http://www.chromium.org/spdy/
随着Web应用日渐广泛,复杂程度和重要性也在不断的增长,也因此对开发人员和用户带来了负担,HTTP2 支持所有的HTTP/1.1的核心特征,提供了HTTP语义的传输优化,并且在各方面做到更高效。
HTTP 2 基本概念
HTTP2 是运行在TCP或者SSL协议之上,属于应用层的协议。HTTP2.0消息包以二进制帧的形式进行封装。
HTTP2引入了一下的三个新概念:
Stream: 已经建立连接的双向字节流,用唯一ID标示,可以传输一个或多个消息
Message:逻辑上的HTTP消息,请求或者响应,可以包含多个 frame
Frame:HTTP2通信的最小单位,二进制头封装,封装HTTP头部或body
HTTP2是把一个HTTP数据包分成多个帧发送,每个帧有一个二进制头,并把HTTP分成多个独立小帧,多个帧组成一个Message在流中发送。不同流的帧有可能交错到达,帧的报文头中标示了属于哪一个流。
HTTP2 的帧格式
HTTP2的最小数据单位是帧,所有帧以9字节的帧头并跟着0-16,383字节的数据。
Length: unsigned 24-bit integer,最大值为 2^24(16384),指的是不包括头部的部分
Type:帧的类型
Flags:为帧类型保留的8字节字段有具体的布尔标识
R:1位的保留字段
Stream Identifier:31字节的流标识符。0是保留的,标明帧是与连接相关作为一个整体而不是一个单独的流。
帧主体的结构和内容完全取决于帧类型。
HTTP2 有以下十种帧类型:
1、DATA :数据帧,Type=0x0,主要用来传递消息体
Pad Length : 8位,可选,只在设置了PADDED标记时呈现。表示帧填充的字节为单位的长度。
Data : 应用数据。
Padding : 填充字节不包含任何应用语义值。填充字节在发送时设为0,接收时忽略。
DATA 帧定义了以下的 Flags:
END_STREAM (0x1): 位1,表示当前帧是相应流发送的最后一帧。设置时流进入半封闭或关闭状态。
PADDED (0x8): 位4,表示Pad Length 字段启用与否。
2、HEADERS :头部帧,Type=0x1,主要用于传递消息头
Pad Length:8位可选,在设置PADDED 标记时呈现。表示帧填充的字节为单位的长度。
E : 1位可选,在优先级标记设置时呈现。表示用于标识流依赖是否是专用的。
Stream Dependency : 31位可选,在优先级标记设置时呈现。流依赖的流的标识符。
Weight : 8位可选,在优先级标记设置时呈现。流的8位权重标记,1-256的值。
Header Block Fragment : 报头块。
Padding : 填充字节
HEADERS 帧定义了以下的 Flags:
END_STREAM (0x1) : 位1,标识是此发送端对流发送的最后报头区块。设置这标记使流进入半封闭状态。
END_HEADERS (0x4) : 位3,表示帧包含了整个的报头块,且后面没有延续帧。 END_HEADERS为0的报头帧后面必须跟着延续帧。
PADDED (0x8) : 位4,表示Pad Length字段会呈现。
PRIORITY (0x20) : 位6,优先级标记(E), 流依赖及权重字段将会呈现。
3、PRIORITY :优先级帧,Type=0x2,用于设置流的优先级
E : 1位标记,指示流的依赖是专有的。
Stream Dependency : 流所依赖流的31位标识符。
Weight: 流的权重(8位)。1-256的权重值。
4、RST_STREAM :流结束帧,Type=0x3,用于终止异常流
RST_STREAM 帧由一个32位整数标记错误码,指明流被终止的原因。
RST_STREAM 帧必须与流相关联,就是说stream id 不能为 0x0
RST_STREAM帧绝对不能在流处于“空闲”状态下发送。
5、SETTINGS :连接配置参数帧,Type=0x4,用于设置参数
载体包含0或多个参数,每个包含一个16位标识以及一个32位的值。
1)设置帧由两个终端在连接开始时发送,连接生存期的任意时间发送。
2)设置帧的参数将替换参数中现有值,不能识别的忽略。
3)设置帧总是应用于连接,而不是一个单独的流。流ID必须为0;
SETTINGS 帧定义了以下的 Flags:
ACK (0x1) : 位1,表示设置帧被接收端接收并应用。如果设置了ACK,设置帧的载体必须为空。
SETTINGS_HEADER_TABLE_SIZE (0x1) : 发送端通知远端报头压缩表的最大承载量。初始值是4,096个字节。
SETTINGS_ENABLE_PUSH (0x2) : 用来关闭服务器推送。0时不能发送PUSH_PROMISE。1表示可以推送。
SETTINGS_MAX_CONCURRENT_STREAMS (0x3) : 标明发送端允许接收端创建的最大并发流的数量。没有限制(<100) 。 0值阻止新流的创建。
SETTINGS_INITIAL_WINDOW_SIZE (0x4) : 表示发送端流量控制的初始窗口大小(字节)。初始值是65,535。 影响所有流的窗口大小.
SETTINGS_MAX_FRAME_SIZE (0x5): 接收最大帧大小。初始值为2^14 (16,384)字节,最大值为2^24-1 or 16,777,215字节。
SETTINGS_MAX_HEADER_LIST_SIZE (0x6): 可接收的header列表长度(字节),基于非压缩的列表大小。
6、PUSH_PROMISE:推送承诺帧,Type=0x5,Server推送之前告知Client端
1) 被推送的流并不需要按照顺序使用。
2) 接收端可以给推送端返回一个RST_STREAM拒绝接收。
Pad Length : 8位,只在PADDED标记设置时才呈现。
R : 1bit保留位。Padding : 填充字节。
Promised Stream ID : 31位整数表示终端准备发送的流标记。
Header Block Fragment : 包含请求头字段的报头区块。
PUSH_PROMISE 帧定义了以下的Flags:
END_HEADERS (0x4) : 位3, 表明帧包含了整个报头区块。
PADDED (0x8) : 位4, 表明Pad Length字段是已设置。
7、PING:Type=0x6,发送端测量最小的RTT时间,检测连接是否可用
PING 帧定义了以下的Flags:
ACK (0x1) : 位1表示PING帧是一个PING响应。
1)PING帧可以被任何终端任何时刻发送。
2)PING帧必须在载体中包含一个8字节长度的数据。
收到不含ACK的PING帧必须发送一个有ACK的PING响应,带相同的载荷。PING响应应设置比其他帧更高的优先级。流ID为0,不和任何流关联;
8、GOAWAY:超时帧,Type=0x7,通知对端不要在连接上建新流
可以由客户端或服务端发送。发动端将忽略连接上流标示符大于Last-Stream-ID的流。
接收端接收到超时帧后不能在这个连接上打开新流,可以创建新连接。
终端在关闭连接之前总是应当发送一个超时帧;
适用于连接而不是特定的流。流标识符必须是0x0,否则错误处理;
连接关闭前小于或等于标识符上的流没有完全关闭的,重试请求;
小于或等于最后流标识符的流可能仍然能成功完成,保持连接在打开状态直到正在处理的流全部处理完成。
在发送超时帧后,发送端能丢弃流标识符大于最终流标识的流的帧。但任何修改流状态的帧不能被忽略。
超时帧包含一个32位错误码,包含关闭连接的原因。
9、WINDOW_UPDATE:Type=0x8,实现流量控制
1) 可以作用单独的流(ID!=0) 或 整个连接(ID==0)
2) 所有类型的流量控制都是逐跳的(hop-by-hop), 中介端不会转发
3) 流量控制只适用于Data帧
4) 一个保留字节,一个31位整数( 发送端被允许传输的字节数,它的大小是接收端的缓存能力的衡量)。
5) 流和连接的初始值都是65535;流的窗口大小可以用SETTING帧设置大小SETTINGS_INITIAL_WINDOW_SIZE;
6) 通过设置窗口大小,可能导致窗口大小为负数(当前有10字节数据,设置为5字节,则剩余-5字节的长度)。
10、CONTINUATION:延续帧,Type=0x9,延续一个报头区块
只要流上前一帧是不带END_HEADERS的HEADERS帧、PUSH_PROMISE帧或者不带有END_HEADERS标记的CONTINUATION帧,可以发送任意多个延续帧。
CONTINUATION 帧定义了以下的Flags:
END_HEADERS (0x4) : 位3,指示帧是否是报头区块的终止。 如果END_HEADERS位没有被设置,这个帧必须跟着另一个延续帧。
HTTP2 错误码
HTTP2 连接过程
http2 的版本标识:
h2:基于TLS之上构建的HTTP/2,作为ALPN的标识符,两个字节表示,0x68, 0x32,即https
h2c:直接在TCP之上构建的HTTP/2,缺乏安全保证,即http
HTTP版本的请求过程:
在不知道服务器是否支持http2的情况下,可以利用http的升级机制发送试探包
1、客户端发起请求
2、服务器不支持 http2,直接按照 http/1.1响应
3、服务器支持 http2,通知客户端切换到http2
4、服务器发送的第一个http2帧,必须为SETTINGS帧做为连接序言
5、客户端接收到101响应后,也必须发送一个序言作为响应,其逻辑结构:
6、客户端可以马上发送请求帧或其它帧过去,不用等待来自服务器端的SETTINGS帧
7、任一端接收到SETTINGS帧之后,都需要返回一个包含确认标志位SETTIGN作为确认
8、其它帧的正常传输
HTTPS 版本的建立连接:
1、客户端和服务器端TLS层协商
2、客户端发送连接序言(同上表示,PRI + SETTINGS)
3、接收到客户端连接序言之后,服务器端发送连接序言
4、双方各自确认SETTINGS帧
5、其它帧的正常传输
HTTP/2的直接连接:
1、客户端必须首先发送一个连接序言,其逻辑结构:
2、发送完毕序言之后,客户端可以不用等待来自服务器端响应,马上发送HTTP/2其它帧
3、服务器端接收到客户端的连接序言之后,需要发送一个SETTINGS帧作为连接序言
4、任一端接收到SETTINGS帧之后,都需要返回一个包含确认标志位SETTIGN作为确认
5、其它帧的正常传输
对比明文版的HTTP/1.1和HTTP/2完成一次请求-响应:
1、HTTP/1.1在建立建立之后,只需要发送请求报文数据
2、HTTP/2客户端需要在连接建立之初马上发送一个连接序言过去,然后才是正常请求
3、两端(客户端+服务器端)的两次完整的连接序言+确认的交互流程,多了两次往返过程