HTTP协议学习笔记
- HTTP是一种请求/应答模式(Request–Response pattern)的应用层协议
- HTTP基于TCP协议进行传输
URI是一类通用的资源标识符,由两个主要的子集URL和URN构成。 URL(统一资源定位符)用于表示浏览器寻找信息时所需的资源位置。
大多数URL方案(Scheme)的语法格式如下:
1 |
[scheme]://[user]:[password]@[host]:[port]/[path];]params]?[query]#[frag] |
各部分说明如下:
组件 | 说明 |
方案(scheme) | 指定访问服务器以获取资源时要使用哪种协议 |
用户(user) | 某些方案访问资源时需要的用户名 |
密码(password) | 某些方案访问资源时需要的密码 |
主机(host) | 资源宿主服务器的主机名或IP地址 |
端口(port) | 资源宿主服务器正在监听的端口号,很多方案都有默认端口号 |
路径(path) | 服务器上资源的本地名,由一个斜杠(/)将其与前面的URL组件分隔开来。路径组件的语法与服务器和方案有关 |
参数(params) | 某些方案会用这个组件来指定输入参数。格式为名/值对;。URL中可以包含多个参数字段,每个参数字段尾部附加分号 |
查询(query) | 某些方案会用这个组件传递参数以激活应用程序。査询组件的内容没有通用格式。用字符?将其与URL的其它组件分隔开 |
片段(frag) | 一小片或一部分资源的名字。该组件在客户端内部使用,不会传递给服务器,使用字符#与其它URL组件分隔 |
下面是一些合法URL的例子:
1 2 3 4 5 6 7 8 9 10 11 |
https://blog.gmem.cc https://blog.gmem.cc:442 ftp://alex:pswd@ftp.gmem.cc/public/readme.txt ftp://anonymous@ftp.gmem.cc/public/readme.txt mailto:me@gmem.cc jdbc:oracle:thin:@//dc.gmem.cc:1521/gmem file://D:/Programs/Scripts/autoproxy.pac |
URL有两种方式:绝对的和相对的URL。相对URL是不完整的,要获取其指向的资源,必须找到其基础URL:
- 如果HTML文档包含 标签,那么使用该标签指定的URL
- 如果没有声明上述标签,该HTML文档本身所属的URL将用来分析基础URL
为安全的处理特殊字符,URL引入了转义语法,转义字符以百分号%开头,后面跟随两位十六进制的数字。常用特殊字符的编码如下:
字符 | ASCII编码 | 示例 |
~ | 126(0x7E) | http://gmem.cc/HTTP%20faq.html |
空格 | 32(0x20) | |
% | 37(0x25) |
对于非ASCII字符的URL编码,标准规范没有规定如何处理,因此不同浏览器的实现有所不同,这导致服务器端难以正确的解码URL中包含的字符。对于这些字符,可移植性最好的方法是使用JavaScript将其转换为Unicode编码,函数escape/unescape、encodeURI/decodeURI用于完成Unicode编码处理:
Unicode字符 | Unicode编码 | URL编码 |
绿 | \u7EFF | %u7EFF |
HTTP报文由三个部分组成:
- 起始行(Start line):对报文进行简短的描述,分为请求行、响应行两种
- 首部(Header):包含若干报文属性。以一个空白行结尾
- 主体(Entity body):可选的数据主体部分
其中起始行、首部是逐行的ASCII文本,每行使用\r\n两个连续字符(CRLF)进行终止。主体部分则不同,可以是文本数据,也可以是二进制数据。
每个首部条目具有一定的格式: [名称]: [空白符][值][CRLF] ,如果首部的值过长,需要跨行的,那么非首行以至少一个空白字符开头。值中包含多个项目的,通常使用逗号分隔。不同首部字段可以按任意顺序排列。有些首部可以出现多次。
HTTP报文分为请求报文、响应报文两类。
请求报文的格式为:
1 2 3 4 5 |
[method] [request-URL] [version] [headers] [entity-body] [last-content] |
响应报文的格式为:
1 2 3 4 5 |
[version] [status] [reason-phrase] [headers] [entity-body] [last-content] |
上述格式中各组件的描述如下:
组件 | 说明 |
方法(method) | 客户端希望服务器对资源执行的动作。参考下面的常用HTTP方法小节 |
请求URL(request-URL) | 客户端请求的资源的完整URL |
版本(version) | 报文所使用的HTTP版本,格式为: HTTP/[major].[minor] 其中主、次版本号均为整数 |
状态码(status-code) | 由3位数字组成,描述了请求过程中所发生的情况。每个状态码的第1位数字用于描述状态的一般类别 |
原因短语(reason-phrase) | 数字状态码的可读版本 |
首部(header) |
可以有零个或多个首部,每个首部都包含一个名字,后面跟着一个冒号(:),然后是一个可选的空格,接着是一个值,最后是一个CRLF。首部由一个空行 (CRLF)结束 有些HTTP版 本,比如HTTP/1.1,要求有效的请求或响应报文中必须包含特定的首部 |
实体主体部分(entity-body) | 包含一个由任意数据组成的数据块(chunks of data)。该部分是可选的 |
尾部内容(last content) | 实体部分的特殊内容,可以包含一些追加的头部(trailing headers)或者标注请求/响应的结束 |
HTTP方法仅对请求报文有意义。并非所有Web服务器支持所有的方法,Web服务器也可以制定自己的扩展方法。
HTTP方法 | 说明 | 是否包含主体 |
GET | 从服务器获取一份文档 | 否 |
HEAD | 只从服务器获取文档的首部 | 是 |
POST | 向服务器发送需要处理的数据 | 是 |
PUT | 将请求的主体部分存储在服务器上 | 否 |
TRACE | 对可能经过代理服务器传送到服务器上去的报文进行追踪 | 否 |
OPTIONS | 决定可以在服务器上执行哪些方法 | 否 |
DELETE | 从服务器上删除一份文档 | 否 |
方法是用来告诉服务器做什么事情的,状态码则用来告诉客户端,发生了什么事情。状态码的分类如下:
常见的状态码如下表:
首部类型 | 说明 |
通用首部 | 请求、响应报文都可以出现的首部。提供了报文最基本的信息 |
请求首部 | 提供关于请求的更多信息 |
响应首部 | 提供关于响应的更多信息 |
实体首部 | 与HTTP报文的载荷(Payload)部分有关,可以描述报文体的尺寸、内容或者描述资源本身 |
扩展首部 | 非标准化的首部 |
名称 | 说明 |
Connection | 允许客户端/服务器指定关于请求/响应连接的选项。举例: Connection: Keep-Alive |
Keep-Alive | 调节Keep-Alive的行为。timeout、max属性在响应首部中发送,分别表示服务器希望连接持续的时间、还能为多少个事务保持连接活动状态。举例: Keep-Alive: max=5, timeout=120 |
Date | 提供报文创建的日期、时间戳。举例: Date: Tue, 11 Jul 2000 18:23:51 GMT |
MIME-Version | MIME类型的版本 |
Trailer | 如果报文采用了分块传输编码(chunked transfer encoding)方式,可以用这个首部列出位于报文拖挂(trailer)部分的首部集合。举例: Trailer: Date |
Trasfer-Encoding | 报文使用了何种编码方式进行传输。举例: Transfer-Encoding: chunked |
Upgrade | 给出发送端想升级使用的新版本或协议。举例: Upgrade: HTTP/2.0 |
Via | 显示报文传输的中间节点(例如代理、网关)。举例: Via: HTTP/1.1 Proxy1, HTTP/1.1 Proxy2 |
Cache-Control | 用于随报文发出缓存指令。举例: Cache-Control: max-age=10 。时间以秒为单位,除了指定max-age,还可以指定:no-store、no-cache、must-revalidate等 |
Pragma | 另一种发送指令的方式,但不限于缓存指令。应当使用Cache-Control代替之。举例: Pragma: no-cache 提示服务器应当返回一个刷新后的文档,即使服务器是代理并且已有缓存 |
名称 | 说明 |
信息性请求首部:提供了客户端、请求本身的一些信息 | |
Client-IP | 提供客户端机器的IP地址 |
From | 提供客户端的电子邮件地址。举例: From: me@gmem.cc |
Host | 请求发送往的服务器的主机名和端口。举例: Host: blog.gmem.cc |
Referer | 从中发起了当前请求的文档的URL。举例: Referer: www.google.com |
UA-Color | 提供与客户端显示器的显示顔色有关的信息 |
UA-CPU | 给出客户端CPU的类型或制造商 |
UA-Disp | 提供与客户端显示器能力有关的信息 |
UA-OS | 给出运行在客户端机器上的操作系统名称及版本 |
UA-Pixels | 提供了客户端显示器的像素信息 |
User-Agent | 将发起请求的应用程序名称告知服务器。举例: User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0) |
Accept首部:将客户端的偏好、能力告知服务器,以便服务器生成定制化的信息 | |
Accept | 告诉服务器,客户端能够支持哪些媒体类型。举例: Accept: text/html, image/* |
Accept-Charset | 告诉服务器,客户端能够支持哪些字符集 。举例: Accept-Charset: iso-8859-1 |
Accept-Encoding | 告诉服务器,客户端能够支持哪些编码方式。举例: Accept-Encoding: gzip, compress |
Accept-Language | 告诉服务器,客户端能够支持哪些语言。举例: Accept-Language: en, fr |
TE | 告诉服务器,客户端能够使用哪些扩展传输编码。举例: TE: trailers |
条件请求首部:要求服务器在进行响应之前,确保条件为真 | |
Expect | 允许客户端列出所要求的服务器行为。举例: Expect: 100-continue |
If-Match | 如果实体标记与文档当前的实体标记相匹配,就获取这份文档。举例: If-Match: entity_tag001 |
If-Modified-Since | 除非在某个指定的日期之后资源被修改过,否则就限制这个请求(服务器应当返回304应答)。举例: If-Modified-Since: Tue, 11 Jul 2000 18:23:51 GMT |
If-None-Match | 如果提供的实体标记与当前文档的实体标记不相符,就获取文档。举例: If-None-Match: entity_tag001 |
If-Range | 允许对文档的某个范围进行条件请求。举例: If-Range: entity_tag001 |
If-Unmodified-Since | 除非在某个指定日期之后资源没有被修改过,否则就限制这个请求。举例: If-Unmodified-Since: Tue, 11 Jul 2000 18:23:51 GMT |
Range | 如果服务器支持范围请求,就请求资源的指定范围。举例: Range: bytes=100-599 |
与安全相关的请求首部 | |
Authorization | 包含了客户端提供给服务器,以便对其自身进行认证的数据。举例: Authorization: [credentials] |
Cookie | 客户端可以用它向服务器传送一个令牌(例如Tomcat的JSESSIONID) |
Cookie2 | 用来说明请求端支持的Cookie版本 |
与代理相关的请求首部 | |
Max-Forwards | 在通往服务器的路径上,将请求转发给其他代理或网关的最大次数(与TRACE方法一同使用)。举例: Max-Forwards: 3 |
Proxy-Authorization | 与Authorization首部功能相同,伹这个首部是在与代理进行认证时使用的 |
Proxy-Connection | 与Connection首部功能相同,但这个首部是在与代理立连接时使用的 |
名称 | 说明 | ||
信息性响应首部 | |||
Age | 响应的年龄,即响应是何时创建的,如果是从代理缓存获取的响应,可能Age早于请求时间。举例: Age: 2147483648 | ||
Public | 服务器为其资源支持的请求方法列表 | ||
Retry-After | 如果资源不可用的话,在此日期或时间重试。举例: Retry-After: 60 | ||
Server | 服务器应用程序软件的名称和版本。举例: Server: Microsoft-IIS/5.0 | ||
Title | HTML文档的标题 | ||
Warning | 比原因短语中E详细一些的警告报文 | ||
协商首部:HTTP1.1可以允许服务器与客户端对资源进行协商 | |||
Accept -Ranges | 对此资源来说,服务器可接受的范围类型。举例: Accept-Ranges: none | ||
Vary | 服务器査看的其他首部的列表,可能会使响应发生变化,也就是说,这是 一个首部列表,服务器会根据这些首部的内容挑选出最适合的资源版本发送给客户端 | ||
与安全相关的响应首部 | |||
Proxy-Authenticate | 来自代理的对客户端的质询(challenges)列表。举例: Proxy-Authenticate: Basic realm-admin | ||
Set-Cookie | 可以在客户端设置一个令牌,以便服务器对客户端进行标识。举例:
|
||
Set-Cookie2 | 与 Set-Cookie 类似,来自RFC 2965 | ||
WWW-Authenticate | 来自服务器的对客户端的质询列表 |
名称 | 说明 |
Allow | 列出可以对此实体执行的请求方法。举例: Allow: GET, HEAD |
Location | 对于一个已经移动的资源,用于重定向请求者至另一个位置。举例: Location: http://gmem.cc/redirect.php |
内容相关的实体头部 | |
Content-Base | 解析主体(Body)中的相对URL时使用的基础URL |
Content-Encoding | 主体的编码方式。举例: Content-Encoding: gzip, compress |
Content-Language | 理解主体时最适宜使用的自然语言。举例: Content-Language: en |
Content-Length | 主体的长度或尺寸。举例: Content-Length: 9990 。该首部很重要,如果连接出现故障,主体被截断,该首部可以用来判断内容是否完整 |
Content-Location | 资源的实际位置。举例: Content-Location: http://gmem.cc/page.php |
Content-MD5 | 主体的MD5校验和。举例: Content-MD5: [md5-digest] |
Content-Range | 在整个资源中此实体所处的字节范围。举例: Content-Range: 1001-2000/5000 最后的5000表示总长度 |
Content-Type | 主体的内容类型。举例: Content-Type: text/html |
实体缓存相关头部 | |
ETag | 与该实体关联的实体标记(Entity tag),对于可以使用多种URL请求的资源,ETag可以用于确定实际被发送的资源是否为同一资源。举例: ETag: b38b9-17dd-367c5dcd |
Expires | 到什么时间(一个绝对时间)为止,实体将过期,应当从其原始出处重新获取。举例: Expires: Tue, 11 Jul 2000 18:23:51 GMT |
Last-Modified | 实体最后一次修改的时间。举例: Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT |
- DNS查找:如果URI对应的主机名在本地DNS缓存中不存在,则需要通过DNS服务器查找,该过程可能消耗几百毫秒——几十秒
- TCP握手与连接建立:小的HTTP事物可能在TCP连接的建立上花费超过50%的时间
- 其它与TCP协议本身有关的性能因素
- 并行连接,如果同时开启多个TCP连接,可能提升HTML页面的加载性能。浏览器通常启用了并行连接,一般几个
- 持久化连接:在一个HTTP事物结束后,TCP连接不会关闭,直到服务器或者客户端显式的关闭。持久化连接包括:
- HTTP/1.0+ keep-alive:实现该特性的客户端通过Connection: Keep-Alive首部请求一条持久化连接。如果服务器支持该请求,则其在响应报文里面也加上Connection: Keep-Alive,否则就表示不支持,客户端将在收到响应后关闭连接
- HTTP/1.1 persistent:默认激活,因此除非特别说明,HTTP/1.1的连接默认是持久的。要在事务结束后关闭连接,必须在报文中显式的添加 Connection: close 首部。客户端和服务器可以随时关闭空闲连接
- 管道化连接:HTTP/1.1允许在持久化连接可选的使用请求管道。在响应到达之前,即可将后续的多条请求放入管道,该机制在高延迟网络中,有利于减少环回时间。客户端不应当以管道化方式处理非幂等操作(例如POST),非幂等操作必须等待前一条的响应到达后再次发送
以下几种方式可以用于HTTP的身份识别:
- 承载用户身份信息的HTTP首部:多种HTTP首部可以用于承载用户身份相关信息。例如From、User-Agent、Referer、Authorization、Cookie等
- 客户端IP地址跟踪。该方式的缺点:
- 只能用于识别机器,而不是用户
- 对于动态分配的IP地址,无法使用
- 对于位于NAT背后的客户端主机,无法使用
- 服务器看到的可能是代理的IP地址,而不是真实客户端
- IP欺骗(伪造):在因特网上很容易发生,因此IP身份验证一般只适用于基于信任的内部网络
- 用户登录,使用HTTP基本认证来识别用户:HTTP提供了一个原生的质询/响应(challenge/response)框架,服务器可以发出401响应和WWW-Authentication首部,浏览器将弹出登录框,用户输入身份信息后,通过Base64编码存放到Authorization首部,并重发先前的请求
- 胖URL,在URL中嵌入识别信息:为每个用户生成特点版本的URL
- Cookie,一种功能强大、高效的持久身份识别技术
Cookie的相关知识可以参考文章:HTTP知识集锦
HTTPS是最流行的HTTP安全形式,由网景首创,主流浏览器均支持。HTTPS方案的URL以“https”开头。
使用HTTPS时,所有数据在通过网络发送之前,都要经过加密。HTTPS在HTTP(应用层)下面提供一个传输级的安全层,该安全层通过SSL或者其继任者TLS实现,可以认为HTTPS就是在SSL之上传输的HTTP。SSL与TLS非常类似,以下不作区分。
大部分涉及安全处理的编码、解码工作由SSL库完成,HTTPS协议端点只需要将TCP调用转为SSL的I/O调用、再辅以一些配置性的调用即可。
通过HTTPS建立安全Web事务后,现代浏览器会自动获得服务器的数字证书,如果没有数字证书,安全连接就会失败,服务器证书包含很多字段,包括:
- Web站点的名称和主机名
- Web站点的公开密钥(公钥)
- 证书颁发结构的名称
- 来自证书颁发机构的、对上述公钥的签名
浏览器会对颁发机构进行检查,对于权威的颁发机构,其公钥一般已经被浏览器预装。浏览器可以通过此公钥对签名的有效性进行核实。如果浏览器对颁发机构一无所知,则通常会提示用户,询问其是否信任此颁发机构。
HTTPS将数据发送到TCP层之前,会通过SSL对数据进行加密。基于SSL的安全传输机制的建立过程如下:
- 客户端连接到服务器的HTTPS端口(默认443),建立TCP连接
- 客户端和服务器通过SSL握手,初始化SSL层
- 交换协议版本号
- 选择一个两端都知晓的密码
- 对两端的身份进行验证
- 生成临时会话密钥,用于通道加密
- 客户端将HTTP报文发送给SSL层,SSL层将其加密,然后传递给TCP层并通过网络发送出去
HTTP/2 的目的是通过支持完整的请求与响应复用来减少延迟,通过有效压缩 HTTP 标头字段将协议开销降至最低,同时增加对请求优先级和服务器推送的支持。为达成这些目标,HTTP/2 还给我们带来了大量其他协议层面的辅助实现,例如新的流控制、错误处理和升级机制。
HTTP/2 没有改动 HTTP 的应用语义。HTTP 方法、状态代码、URI 和标头字段等核心概念一如往常。不过,HTTP/2 修改了数据格式化(分帧)以及在客户端与服务器间传输的方式。
各主流客户端对HTTP2的支持情况参考:https://caniuse.com/#feat=http2
HTTP/2 所有性能增强的核心在于新的二进制分帧层,它定义了如何封装 HTTP 消息并在客户端与服务器之间传输:
这里所谓的“层”,指的是位于套接字接口与应用可见的高级 HTTP API 之间一个经过优化的新编码机制,HTTP 的语义不受影响,不同的是传输期间的编码方式变了。HTTP/2 将所有传输的信息分割为更小的消息和帧,并采用二进制格式对它们编码。
新的二进制分帧机制改变了客户端与服务器之间交换数据的方式。 为了说明这个过程,我们需要了解 HTTP/2 的三个概念:
- 数据流:已建立的连接内的双向字节流,可以承载一条或多条消息
- 消息:与逻辑请求或响应消息对应的完整的一系列帧
- 帧:HTTP/2 通信的最小单位,每个帧都包含帧头,至少也会标识出当前帧所属的数据流
注意:
- 所有通信都在一个 TCP 连接上完成,此连接可以承载任意数量的双向数据流
- 每个数据流都有一个唯一的标识符和可选的优先级信息,用于承载双向消息
- 每条消息都是一条逻辑 HTTP 消息(例如请求或响应),包含一个或多个帧
- 帧是最小的通信单位,承载着特定类型的数据,例如 HTTP 头、消息负载
- 来自不同数据流的帧可以交错发送,然后再根据每个帧头的数据流标识符重新组装
帧是HTTP/2最小通信单元,在一个TCP连接上,同一时刻最多有一个帧在发送。
帧报文的格式(单位bit):
1 2 3 4 5 6 7 8 9 |
+-----------------------------------------------+ | Length (24) | +---------------+---------------+---------------+ | Type (8) | Flags (8) | +-+-------------+---------------+-------------------------------+ |R| Stream Identifier (31) | +=+=============================================================+ | Frame Payload (0...) ... +---------------------------------------------------------------+ |
字段说明:
- Length:帧载荷部分的长度
- Type: 帧类型。0000 0000为DATA帧,0000 0001为HEADERS帧
- Flags:不同类型的帧分别设置,特殊标记位。例如HEADERS帧会设置END_HEADERS标记位来提示头传送完毕
- R:无用
- Stream Identifier:当前帧所属的流的标识符
- Frame Payload帧的载荷部分
HTTP/2支持多路复用,每一路就是一个流。流具有标识符,每个帧都指明它所属的流的标识符。
由于HTTP/2支持Server Push,也就是说服务器可以主动创建流。那么客户端和服务器的流ID如何防止冲突?协议规定,服务器使用偶数,客户端使用奇数。
流的状态转换示意图:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
+--------+ send PP | | recv PP ,--------| idle |--------. / | | \ v +--------+ v +----------+ | +----------+ | | | send H / | | ,------| reserved | | recv H | reserved |------. | | (local) | | | (remote) | | | +----------+ v +----------+ | | | +--------+ | | | | recv ES | | send ES | | | send H | ,-------| open |-------. | recv H | | | / | | \ | | | v v +--------+ v v | | +----------+ | +----------+ | | | half | | | half | | | | closed | | send R / | closed | | | | (remote) | | recv R | (local) | | | +----------+ | +----------+ | | | | | | | | send ES / | recv ES / | | | | send R / v send R / | | | | recv R +--------+ recv R | | | send R / `----------->| |<-----------' send R / | | recv R | closed | recv R | `----------------------->| |<----------------------' +--------+ send: endpoint sends this frame recv: endpoint receives this frame H: HEADERS frame (with implied CONTINUATIONs) PP: PUSH_PROMISE frame (with implied CONTINUATIONs) ES: END_STREAM flag R: RST_STREAM frame |
其中:
- idle:流目前尚未启用
- open:流目前正在使用
- closed:流已经使用完成
- reserved(local):本地保留,即将要使用但是尚未使用
- reserved(remote):远端保留,即将要使用但是未使用
- half closed(local):本地半关闭,即将关闭但是尚未关闭
- half closed(remote):远端半关闭,即将关闭但是尚未关闭
收到或者发送HEADERS这种类型的帧会使流进入open状态,也就是说,HEADERS一定会建立一个新的流
发送PUSH_PROMISE的那一方会把流保存为reserved(local)的状态,当发送完HEADERS之后会变成half closed(remote)状态
帧类型 | 说明 |
0x0 DATA | 关联到帧的任意长度的帧,为了传递一个HTTP请求/应答,可能需要多个DATA帧 |
0x1 HEADERS | 用于打开流,附带一些HTTP头片段,可以发送到idle、reserved(local)、open、half-closed(remote)类型的流 |
0x2 PRIORITY | 指定发送者建议的、流的优先级 |
0x3 RST_STREAM | 用于立即终止流 |
0x4 SETTINGS | 携带影响端点通信方式的参数 |
0x5 PUSH_PROMISE | 用于在准备发起流之前,通知对方。附带将要创建的流的ID以及一系列头数据 |
0x6 PING | 用于度量RTT,测试空闲连接是否还在正常工作 |
0x7 GOAWAY |
用于发起连接的shutdown,或者指示严重的错误状态 使用此帧,可以优雅的禁止建立新流,同时允许旧流正常完毕 |
0x8 WINDOW_UPDATE | 用于实现流控,流控可以在窗口级别、整个连接级别进行 |
0x9 CONTINUATION | 用于继续一系列的头部块片段。前置的帧必须是不设置END_HEADERS的HEADERS, PUSH_PROMISE, 或CONTINUATION |
0xa ALTSVC | 允许源的资源在另外一个网络位置访问,可能用不同的协议访问 |
0xc ORIGIN | 在指定的连接山,什么源(Origin)是可用的 |
一个典型的HTTP报文包含请求/响应,组成如下:
- 零或多个HEADERS帧,一个HEADERS帧可能跟着0-N个CONTINUATION帧,以补充单个HEADERS容量不够的情况。大部分情况下一个HEADERS帧就包含了完整的报文头
- 0-N个DATA数据帧,包含了具体的消息载荷内容
- 一个HEADERS帧,后面跟随0-N个包含有报尾(Trailer-part)的CONTINUATION帧
HTTP/2服务器可以对一个客户端请求发送多个响应。换句话说,除了对最初请求的响应外,服务器还可以向客户端推送额外资源,而无需客户端明确地请求。
所有服务器推送数据流都由 PUSH_PROMISE 帧发起,表明了服务器向客户端推送所述资源的意图,并且需要先于请求推送资源的响应数据传输。
在客户端接收到 PUSH_PROMISE 帧后,它可以根据自身情况(例如有缓存)选择拒绝数据流(通过 RST_STREAM 帧)。
Google的SPDY协议包含名为NPN的TLS扩展,SPDY被HTTP/2取代后,NPN相应的成为ALPN(应用层协议协商)。
客户端在建立 TLS 连接的 Client Hello 握手中,通过 ALPN 扩展列出了自己支持的各种应用层协议,其中HTTP/2 协议名称是 h2。如果服务端支持 HTTP/2,在 Server Hello 中指定 ALPN 的结果为 h2 则协商完成,否则从客户端ALPN的列表中选择一个自己支持的协议。
Leave a Reply