EagleBear2002 的博客

这里必须根绝一切犹豫,这里任何怯懦都无济于事

Web 前端开发-12-HTTP 协议

Web 与 HTTP

Web 页面由对象组成

对象可以是 HTML 文件、JPEG 图像、音频文件...

Web 页面由基本 html 文件组成,其中包含几个引用对象每个对象都可以通过 URL 寻址

示例 URL(统一资源定位器)

1
www.someschool.edu/someDept/pic.gif |---host name----||---path name---|

HTTP 总览

HTTP:超文本传输协议

Web 的应用层协议

客户机/服务器模型

  • 客户端:请求、接收、显示 Web 对象的浏览器
  • 服务器:Web 服务器发送对象以响应请求

使用 TCP:

  1. 客户端启动 TCP 连接(创建套接字)到服务器,端口 80
  2. 服务器接受来自客户端的 TCP 连接
  3. 浏览器(HTTP 客户端)和 Web 服务器(HTTP 服务器)之间交换 HTTP 消息(应用层协议消息)
  4. TCP 连接关闭

HTTP 是无状态的。

服务器不维护关于过去客户端请求的信息

维护“状态”的协议非常复杂!

  • 必须保留过去的历史(状态)
  • 如果服务器/客户端崩溃,它们的“状态”视图可能不一致,必须进行协调

RFC 的定义

The Hypertext Transfer Protocol (HTTP) is an application-level protocol for distributed, collaborative, hypermedia information systems. It is a generic, stateless, protocol which can be used for many tasks beyond its use for hypertext, such as name servers and distributed object management systems, through extension of its request methods, error codes and headers . A feature of HTTP is the typing and negotiation of data representation, allowing systems to be built independently of the data being transferred.

超文本传输协议(HTTP)是一种为分布式,协作式的,超媒体信息系统。它是一种通用的,无状态(stateless)的协议,除了应用于超文本传输外,它也可以应用于诸如名称服务器和分布对象管理系统之类的系统,这可以通过扩展它的请求方法,错误代码和消息头来实现。HTTP 的一个特性就是是数据表现形式是可以定义的和可协商性的,这就允许系统能独立于于数据传输被构建。

https://www.w3.org/Protocols/rfc2616/rfc2616.html

HTTP 历史

  • HTTP Version 0.9
  • HTTP 1.0: RFC 1945
  • HTTP 1.1: RFC 2616
  • HTTPS (HTTP over TLS, HTTP over SSL, HTTP Secure)
  • 2009 Google 设计了基于 TCP 的 SPDY
  • HTTP/2 (originally named HTTP/2.0)
    • Hypertext Transfer Protocol version 2 - RFC7540 /9113
    • HPACK - Header Compression for HTTP/2 - RFC7541
  • HTTP/3:RFC9114
    • QUIC(Quick UDP Internet Connections, 快速 UDP 网络连接)

HTTP 协议的演进

  • HTTP over QUIC
  • UDP

HTTP/2

HTTP/2 是 HTTP 网络协议的一个重要版本。HTTP / 2 的主要目标是通过启用完整的请求和响应多路复用来减少延迟,通过有效压缩 HTTP 标头字段来最小化协议开销,并增加对请求优先级和服务器推送的支持。

HTTP/2 不会修改 HTTP 协议的语义。HTTP 1.1 中的所有核心概念(例如 HTTP 方法,状态码,URI 和 headers)都得以保留。而是修改了 HTTP/2 数据在客户端和服务器之间的格式(帧)和传输方式,这两者都管理整个过程,并在新的框架层内隐藏了应用程序的复杂性。所以,所有现有的应用程序都可以不经修改地交付。

请求-响应

HTTP 的结构很简单:

  • 客户端发送请求
  • 服务器返回一个响应

HTTP 可以在单个 TCP 连接上支持多个请求-应答交换

HTTP 客户端和服务器端使用 TCP 套接字接口进行通信

HTTP 事务延迟

影响 HTTP 的常见的与 tcp 相关的延迟

  1. TCP 连接建立(三次握手)
  2. TCP 慢启动拥塞控制
  3. Nagle 的数据聚合算法
  4. TCP 用于承载确认的延迟确认算法
  5. TIME_WAIT 延迟和端口耗尽

HTTP/1.x 的连接管理

短连接

每一个 HTTP 请求都由它自己独立的连接完成;这意味着发起每一个 HTTP 请求之前都会有一次 TCP 握手,而且是连续不断的。

这是 HTTP/1.0 的默认模型(如果没有指定 Connection 协议头,或者是值被设置为 close)。而在 HTTP/1.1 中,只有当 Connection 被设置为 close 时才会用到这个模型。

短连接有两个比较大的问题:创建新连接耗费的时间尤为明显,另外 TCP 连接的性能只有在该连接被使用一段时间后(热连接)才能得到改善。

长连接/持久连接

一个长连接会保持一段时间,重复用于发送一系列请求,节省了新建 TCP 连接握手的时间,还可以利用 TCP 的性能增强能力。当然这个连接也不会一直保留着:连接在空闲一段时间后会被关闭(服务器可以使用 Keep-Alive 协议头来指定一个最小的连接保持时间)。

长连接也还是有缺点的;就算是在空闲状态,它还是会消耗服务器资源,而且在重负载时,还有可能遭受 DoS attacks 攻击。这种场景下,可以使用非长连接,即尽快关闭那些空闲的连接,也能对性能有所提升。

HTTP/1.0 里默认并不使用长连接。把 Connection 设置成 close 以外的其它参数都可以让其保持长连接,通常会设置为 retry-after。

在 HTTP/1.1 里,默认就是长连接的,协议头都不用再去声明它(但我们还是会把它加上,万一某个时候因为某种原因要退回到 HTTP/1.0 呢)。

HTTP 流水线

默认情况下,HTTP 请求是按顺序发出的。下一个请求只有在当前请求收到应答过后才会被发出。由于会受到网络延迟和带宽的限制,在下一个请求被发送到服务器之前,可能需要等待很长时间。

流水线是在同一条长连接上发出连续的请求,而不用等待应答返回。这样可以避免连接延迟。理论上讲,性能还会因为两个 HTTP 请求有可能被打包到一个 TCP 消息包中而得到提升。就算 HTTP 请求不断的继续,尺寸会增加,但设置 TCP 的 MSS(Maximum Segment Size) 选项,仍然足够包含一系列简单的请求。

并不是所有类型的 HTTP 请求都能用到流水线:只有 idempotent 方式,比如 GET、HEAD、PUT 和 DELETE 能够被安全的重试:如果有故障发生时,流水线的内容要能被轻易的重试。

今天,所有遵循 HTTP/1.1 的代理和服务器都应该支持流水线,虽然实际情况中还是有很多限制:一个很重要的原因是,目前没有现代浏览器默认启用这个特性。

域名分片

如果服务器端想要更快速的响应网站或应用程序的应答,它可以迫使客户端建立更多的连接。例如,不要在同一个域名下获取所有资源,假设有个域名是 ww.example.com,我们可以把它拆分成好几个域名:www1.example.comwww2.example.comwww3.example.com。所有这些域名都指向同一台服务器,浏览器会同时为每个域名建立 6 条连接(在我们这个例子中,连接数会达到 18 条)。这一技术被称作域名分片。

备注: 除非你有紧急而迫切的需求,不要使用这一过时的技术,升级到 HTTP/2 就好了。

HTTP 事务

HTTP 消息

HTTP 消息是服务器和客户端之间交换数据的方式。

HTTP 消息由采用 ASCII 编码的多行文本构成。

有两种类型的消息:

  • 请求(requests)-- 由客户端发送用来触发一个服务器上的动作;
  • 响应(responses)-- 来自服务器的应答。

消息组成

HTTP 请求和响应具有相似的结构,由以下部分组成:

  1. 一行起始行用于描述要执行的请求,或者是对应的状态,成功或失败。这个起始行总是单行的。
  2. 一个可选的 HTTP 头集合指明请求或描述消息正文。
  3. 一个空行指示所有关于请求的元数据已经发送完毕。
  4. 一个可选的包含请求相关数据的正文(比如 HTML 表单内容), 或者响应相关的文档。正文的大小有起始行的 HTTP 头来指定。

起始行和 HTTP 消息中的 HTTP 头统称为请求头,而其有效负载被称为消息正文。

HTTP 请求消息

HTTP 请求消息:

  • ASCII
  • 行以 CRLF "" 结尾
  • 第一行叫做“请求行”
1
2
3
4
GET /somedir/page.html HTTP/1.1 // 请求行 (GET, POST, HEAD方法) Host:
www.someschool.edu ---- User-agent: Mozilla/4.0 | Connection: close |-头域
Accept-language:fr ---- (extra carriage return, line feed) //
回车,换行表示消息的结束

请求方法

  • GET :从服务器获取 URL 对应的资源
  • HEAD :除了服务器响应中不能包含消息体,该方法与 GET 一样。用于只需少数元信息的情况
  • POST :被设计用来注解、修改 URL 所对应的资源
  • PUT :被设计用来修改或创建资源。当 URL 对应的资源存在时,则提交的作为新版本,否则新建资源
  • DELETE :被设计用来删除 URL 对应的资源
  • TRACE :主要用来测试。服务器将最终接收到的请求本身发送回来,作为客户端诊断依据
  • OPTIONS :客户端查询服务器对与某 URL 允许的通信选项
  • CONNECT :保留的方法名,用于代理切换隧道
  • 支持扩展
GET 方法示例

POST 方法示例

TRACE 示例

头域

由主键/值对组成,描述客户端或者服务器的属性、被传输的资源以及应该实现连接。

四种不同类型的头标:

  1. 通用头标:即可用于请求,也可用于响应,是作为一个整体而不是特定资源与事务相关联。
  2. 请求头标:允许客户端传递关于自身的信息和希望的响应形式。
  3. 响应头标:服务器和于传递自身信息的响应。
  4. 实体头标:定义被传送资源的信息。即可用于请求,也可用于响应。

实例

1
2
Accept: text/html Host: www.nju.edu.cn From: abc@nju.edu.cn User-Agent:
Mozilla/4.0

HTTP 响应消息

  • ASCII 状态行
  • 头域
  • 内容
1
2
3
4
5
HTTP/1.1 200 OK // status line (protocol status code status phrase) Connection:
close ---- Date: Thu, 06 Aug 1998 | 12:00:15 GMT | Server: Apache/1.3.0 (Unix)
|-header lines Last-Modified: Mon, 22 Jun | 1998 … | Content-Length: 6821 |
Content-Type: text/html ---- data data data data data ... // data, e.g.,
requested HTML file

响应状态行

  • HTTP-Version:http 协议版本
  • Status-Code:状态码(三位数字)
  • Reason-Phrase:状态描述

状态码

HTTP 响应状态码用来表明特定 HTTP 请求是否成功完成。响应被归为以下五大类:

  1. 信息响应 (100-199)
  2. 成功响应 (200-299)
  3. 重定向消息 (300-399)
  4. 客户端错误响应 (400-499)
  5. 服务端错误响应 (500-599)

常用状态码

状态码 含义
200 OK
301 Moved Permanently
400 Bad Request
401 Unauthorized
403 forbidden
404 Not Found
500 Internal Server Error

信息响应

状态码 含义
100 Continue 这个临时响应表明,迄今为止的所有内容都是可行的,客户端应该继续请求,如果已经完成,则忽略它。
101 Switching Protocols 该代码是响应客户端的 Upgrade (en-US) 请求头发送的,指明服务器即将切换的协议。
102 Processing (WebDAV) 此代码表示服务器已收到并正在处理该请求,但当前没有响应可用。
103 Early Hints 此状态代码主要用于与 Link 链接头一起使用,以允许用户代理在服务器准备响应阶段时开始预加载 preloading 资源。

成功响应

状态码 含义
200 OK 请求成功。
201 Created 该请求已成功,并因此创建了一个新的资源。这通常是在 POST 请求,或是某些 PUT 请求之后返回的响应。
202 Accepted 请求已经接收到,但还未响应,没有结果。意味着不会有一个异步的响应去表明当前请求的结果,预期另外的进程和服务去处理请求,或者批处理。
203 Non-Authoritative Information 服务器已成功处理了请求,但返回的实体头部元信息不是在原始服务器上有效的确定集合,而是来自本地或者第三方的拷贝。当前的信息可能是原始版本的子集或者超集。例如,包含资源的元数据可能导致原始服务器知道元信息的超集。使用此状态码不是必须的,而且只有在响应不使用此状态码便会返回 200 OK 的情况下才是合适的。
204 No Content 对于该请求没有的内容可发送,但头部字段可能有用。用户代理可能会用此时请求头部信息来更新原来资源的头部缓存字段。
205 Reset Content 告诉用户代理重置发送此请求的文档。
206 Partial Content 当从客户端发送 Range 范围标头以只请求资源的一部分时,将使用此响应代码。
207 Multi-Status (WebDAV) 对于多个状态代码都可能合适的情况,传输有关多个资源的信息。
208 Already Reported (WebDAV) 在 DAV 里面使用 dav:propstat 响应元素以避免重复枚举多个绑定的内部成员到同一个集合。
226 IM Used (HTTP Delta encoding) 服务器已经完成了对资源的 GET 请求,并且响应是对当前实例应用的一个或多个实例操作结果的表示。

重定向消息

状态码 含义
300 Multiple Choice 请求拥有多个可能的响应。用户代理或者用户应当从中选择一个。
301 Moved Permanently 请求资源的 URL 已永久更改。在响应中给出了新的 URL。
302 Found 此响应代码表示所请求资源的 URI 已暂时更改。未来可能会对 URI 进行进一步的改变。因此,客户机应该在将来的请求中使用这个相同的 URI。
303 See Other 服务器发送此响应,以指示客户端通过一个 GET 请求在另一个 URI 中获取所请求的资源。
304 Not Modified 这是用于缓存的目的。它告诉客户端响应还没有被修改,因此客户端可以继续使用相同的缓存版本的响应。
305 Use Proxy 已弃用 在 HTTP 规范中定义,以指示请求的响应必须被代理访问。由于对代理的带内配置的安全考虑,它已被弃用。
306 unused 此响应代码不再使用;它只是保留。它曾在 HTTP/1.1 规范的早期版本中使用过。
307 Temporary Redirect 服务器发送此响应,以指示客户端使用在前一个请求中使用的相同方法在另一个 URI 上获取所请求的资源。这与 302 Found HTTP 响应代码具有相同的语义,但用户代理不能更改所使用的 HTTP 方法:如果在第一个请求中使用了 POST,则在第二个请求中必须使用 POST
308 Permanent Redirect 这意味着资源现在永久位于由 Location: HTTP Response 标头指定的另一个 URI。这与 301 Moved Permanently HTTP 响应代码具有相同的语义,但用户代理不能更改所使用的 HTTP 方法:如果在第一个请求中使用 POST,则必须在第二个请求中使用 POST。

301-永久重定向

什么情况下使用 301 重定向:

  • 迁移到另外一个域名时,通过 301 永久重定向将旧域名重定向至新域名,挽回流量损失和 SEO。
  • 保持链接有效:当重构 Web 站点的时候,资源的 URL 会发生改变。你并不想因此而使旧链接失效,因为它们会带来宝贵的用户(并且帮助优化你的 SEO),所以需要建立从旧链接到新链接的重定向映射。
  • 如果有多个闲置域名需要指向同一网站时,通过 301 永久重定向可以实现。
  • 打算实现网址规范化
  • 强制使用 HTTPS 协议。对于 HTTP 版本站点的请求会被重定向至采用了 HTTPS 协议的版本。

临时重定向

有时候请求的资源无法从其标准地址访问,但是却可以从另外的地方访问。在这种情况下可以使用临时重定向。

搜索引擎不会记录该新的、临时的链接。在创建、更新或者删除资源的时候,临时重定向也可以用于显示临时性的进度页面。

编码 含义 处理方法 典型应用场景
302 Found GET 方法不会发生变更,其他方法有可能会变更为 GET 方法。 由于不可预见的原因该页面暂不可用。在这种情况下,搜索引擎不会更新它们的链接。
303 See Other GET 方法不会发生变更,其他方法会变更为 GET 方法(消息主体会丢失)。 用于 PUT 或 POST 请求完成之后进行页面跳转来防止由于页面刷新导致的操作的重复触发。
307 Temporary Redirect 方法和消息主体都不发生变化。 由于不可预见的原因该页面暂不可用。在这种情况下,搜索引擎不会更新它们的链接。当站点支持非 GET 方法的链接或操作的时候,该状态码优于 302 状态码。

特殊重定向

客户端错误响应

状态码 含义
400 Bad Request 由于被认为是客户端错误(例如,错误的请求语法、无效的请求消息帧或欺骗性的请求路由),服务器无法或不会处理请求。
401 Unauthorized 虽然 HTTP 标准指定了 "unauthorized",但从语义上来说,这个响应意味着 "unauthenticated"。也就是说,客户端必须对自身进行身份验证才能获得请求的响应。
402 Payment Required 实验性
此响应代码保留供将来使用。创建此代码的最初目的是将其用于数字支付系统,但是此状态代码很少使用,并且不存在标准约定。
403 Forbidden 客户端没有访问内容的权限;也就是说,它是未经授权的,因此服务器拒绝提供请求的资源。与 401 Unauthorized 不同,服务器知道客户端的身份。
404 Not Found 服务器找不到请求的资源。在浏览器中,这意味着无法识别 URL。在 API 中,这也可能意味着端点有效,但资源本身不存在。服务器也可以发送此响应,而不是 403 Forbidden,以向未经授权的客户端隐藏资源的存在。这个响应代码可能是最广为人知的,因为它经常出现在网络上。
405 Method Not Allowed 服务器知道请求方法,但目标资源不支持该方法。例如,API 可能不允许调用 DELETE 来删除资源。
406 Not Acceptable 当 web 服务器在执行服务端驱动型内容协商机制后,没有发现任何符合用户代理给定标准的内容时,就会发送此响应。
407 Proxy Authentication Required 类似于 401 Unauthorized 但是认证需要由代理完成。
408 Request Timeout 此响应由一些服务器在空闲连接上发送,即使客户端之前没有任何请求。这意味着服务器想关闭这个未使用的连接。由于一些浏览器,如 Chrome、Firefox 27+ 或 IE9,使用 HTTP 预连接机制来加速冲浪,所以这种响应被使用得更多。还要注意的是,有些服务器只是关闭了连接而没有发送此消息。
409 Conflict 当请求与服务器的当前状态冲突时,将发送此响应。
410 Gone 当请求的内容已从服务器中永久删除且没有转发地址时,将发送此响应。客户端需要删除缓存和指向资源的链接。HTTP 规范打算将此状态代码用于“有限时间的促销服务”。API 不应被迫指出已使用此状态代码删除的资源。
411 Length Required 服务端拒绝该请求因为 Content-Length 头部字段未定义但是服务端需要它。
412 Precondition Failed 客户端在其头文件中指出了服务器不满足的先决条件。
413 Payload Too Large 请求实体大于服务器定义的限制。服务器可能会关闭连接,或在标头字段后返回重试 Retry-After。
414 URI Too Long 客户端请求的 URI 比服务器愿意接收的长度长。
415 Unsupported Media Type 服务器不支持请求数据的媒体格式,因此服务器拒绝请求。
416 Range Not Satisfiable 无法满足请求中 Range 标头字段指定的范围。该范围可能超出了目标 URI 数据的大小。
417 Expectation Failed 此响应代码表示服务器无法满足 Expect 请求标头字段所指示的期望。
418 I'm a teapot 服务端拒绝用茶壶煮咖啡。笑话,典故来源茶壶冲泡咖啡
421 Misdirected Request 请求被定向到无法生成响应的服务器。这可以由未配置为针对请求 URI 中包含的方案和权限组合生成响应的服务器发送。
422 Unprocessable Entity (WebDAV) 请求格式正确,但由于语义错误而无法遵循。
423 Locked (WebDAV) 正在访问的资源已锁定。
424 Failed Dependency (WebDAV) 由于前一个请求失败,请求失败。
425 Too Early 实验性
表示服务器不愿意冒险处理可能被重播的请求。
426 Upgrade Required 服务器拒绝使用当前协议执行请求,但在客户端升级到其他协议后可能愿意这样做。 服务端发送带有 Upgrade (en-US) 字段的 426 响应来表明它所需的协议(们)。
428 Precondition Required 源服务器要求请求是有条件的。此响应旨在防止'丢失更新'问题,即当第三方修改服务器上的状态时,客户端 GET 获取资源的状态,对其进行修改并将其 PUT 放回服务器,从而导致冲突。
429 Too Many Requests 用户在给定的时间内发送了太多请求(“限制请求速率”)
431 Request Header Fields Too Large 服务器不愿意处理请求,因为其头字段太大。在减小请求头字段的大小后,可以重新提交请求。
451 Unavailable For Legal Reasons 用户代理请求了无法合法提供的资源,例如政府审查的网页。

服务端错误响应

状态码 含义
500 Internal Server Error 服务器遇到了不知道如何处理的情况。
501 Not Implemented 服务器不支持请求方法,因此无法处理。服务器需要支持的唯二方法(因此不能返回此代码)是 GET and HEAD.
502 Bad Gateway 此错误响应表明服务器作为网关需要得到一个处理这个请求的响应,但是得到一个错误的响应。
503 Service Unavailable 服务器没有准备好处理请求。常见原因是服务器因维护或重载而停机。请注意,与此响应一起,应发送解释问题的用户友好页面。这个响应应该用于临时条件和如果可能的话,HTTP 标头 Retry-After 字段应该包含恢复服务之前的估计时间。网站管理员还必须注意与此响应一起发送的与缓存相关的标头,因为这些临时条件响应通常不应被缓存。
504 Gateway Timeout 当服务器充当网关且无法及时获得响应时,会给出此错误响应。
505 HTTP Version Not Supported 服务器不支持请求中使用的 HTTP 版本。
506 Variant Also Negotiates 服务器存在内部配置错误:所选的变体资源被配置为参与透明内容协商本身,因此不是协商过程中的适当终点。
507 Insufficient Storage (WebDAV) 无法在资源上执行该方法,因为服务器无法存储成功完成请求所需的表示。
508 Loop Detected (WebDAV) 服务器在处理请求时检测到无限循环。
510 Not Extended 服务器需要对请求进行进一步扩展才能完成请求。
511 Network Authentication Required 指示客户端需要进行身份验证才能获得网络访问权限。

Body

请求/响应的最后一部分是它的 body

  • 不是所有的请求都有一个 body:例如获取资源的请求,GET,HEAD,DELETE 和 OPTIONS,通常它们不需要 body。有些请求将数据发送到服务器以便更新数据:常见的的情况是 POST 请求(包含 HTML 表单数据)。
  • 不是所有的响应都有 body:具有状态码(如 201 或 204) 的响应,通常不会 有 body。

任何信息

需要 Content-Length、Content-Type

HTTP/1.x 报文缺点

HTTP/1.x 报文有一些性能上的缺点:

  • Header 不像 body,它不会被压缩。
  • 两个报文之间的 header 通常非常相似,但它们仍然在连接中重复传输。
  • 无法复用。当在同一个服务器打开几个连接时:TCP 热连接比冷连接更加有效。

HTTP/2 - 为了更优异的表现

在 2010 年到 2015 年,谷歌通过实践了一个实验性的 SPDY 协议,证明了一个在客户端和服务器端交换数据的另类方式。其收集了浏览器和服务器端的开发者的焦点问题。明确了响应数量的增加和解决复杂的数据传输,SPDY 成为了 HTTP/2 协议的基础。

HTTP/2 在 HTTP/1.1 有几处基本的不同:

  • HTTP/2 是二进制协议而不是文本协议。不再可读,也不可无障碍的手动创建,改善的优化技术现在可被实施。
  • 这是一个复用协议。并行的请求能在同一个链接中处理,移除了 HTTP/1.x 中顺序和 阻塞的约束。
  • 压缩了 headers。因为 headers 在一系列请求中常常是相似的,其移除了重复和传输重复数据的成本。
  • 其允许服务器在客户端缓存中填充数据,通过一个叫服务器推送的机制来提前请求。

流、消息、帧

流(Stream):是在 HTTP/2 连接中在客户端和服务器之间交换的独立双向帧序列。流是连接中的一个虚拟信道,可以承载双向消息传输。每个流有唯一整数标识符。为了防止两端流 ID 冲突,客户端发起的流具有奇数 ID,服务器端发起的流具有偶数 ID。能携带一个至多个消息。

消息(Message):一个完整的请求或者响应,比如请求、响应等,由一个或多个组成。

帧(Frame):在 HTTP/2 通信的最小单元。每个桢包括一个帧头,里面有个很小标志,来区别是属于哪个流。

流的重要特征

  1. 一个 HTTP/2 连接可以包含多个同时打开的流,并且可以从多个流中交叉帧。
  2. 流可以单独建立和使用,也可以由客户端或服务器共享。
  3. 流可以由任一端点关闭。
  4. 帧在流上发送的顺序非常重要。 收件人按收到的顺序处理框架。 特别是,HEADERS 和 DATA 帧的顺序在语义上很重要。
  5. 流用无符号的 31 位整数标识。 流标识符由启动流的端点分配给流。客户端发起的流必须使用奇数流标识符;那些由服务器发起的必须使用偶数流标识符。

二进制格式

  • Start Line
  • Header
  • Body
  • Length(24)
  • Type(8)
  • Flags(8)
  • R Stream ID(31)
  • Payload()

二进制帧层

请求和响应多路复用

流量控制

流量控制特定于连接。两种类型的流量控制都位于单跳的端点之间,而不是整个端到端 路径。

流量控制基于 WINDOW_UPDATE 帧。接收者宣告他们准备在一个流上以及整个连接上 接收多少个字节。

流量控制是由接收器提供的全面控制方向。接收者可以选择为每个流和整个连接设置它想要的任何窗口大小。发送方必须遵守接收方施加的流量控制限制。客户端,服务器和中介都独立地将其流量控制窗口作为接收者进行通告,并遵守发送时由对等方设置的流量控制限制。

对于新流和整个连接,流量控制窗口的初始值为 65,535 个八比特组。

帧类型决定流量控制是否适用于帧。在本文档中指定的帧中,只有数据帧受流量控制;所有其他帧类型不会占用通告的流量控制窗口中的空间。这确保了重要的控制框架不会被流量控制阻塞。

流量控制不能禁用。

HTTP/2 仅定义 WINDOW_UPDATE 帧的格式和语义。

优先级和依赖性

每个流都包含一个优先级(也就是“权重”),它被用来告诉对端哪个流更重要。当资源有限的时候,服务器会根据优先级来选择应该先发送哪些流。

客户端可以通过在打开流的 HEADERS 帧中包含优先级信息来为新流分配优先级。在其他任何时候,PRIORITY 帧都可以用来改变流的优先级。

每个流可以被赋予对另一个流的显式依赖。包括依赖关系表示优先将资源分配给识别的流而不是依赖流。借助于 PRIORITY 帧,客户端同样可以告知服务器当前的流依赖于其他哪个流。该功能让客户端能建立一个优先级“树”,所有“子流”会依赖于“父流”的传输完成情况。 优先级和依赖关系可以在传输过程中被动态的改变。这样当用户滚动一个全是图片的页面的时候,浏览器就能够指定哪个图片拥有更高的优先级。或者是在你切换标签页的时候,浏览器可以提升新切换到页面所包含流的优先级。

服务器推送

这个功能通常被称作“缓存推送”。主要的思想是:当一个客户端请求资源 X,而服务器知道它很可能也需要资源 Z 的情况下,服务器可以在客户端发送请求前,主动将资源 Z 推送给客户端。这个功能帮助客户端将 Z 放进缓存以备将来之需。

服务器推送需要客户端显式的允许服务器提供该功能。但即使如此,客户端依然能自主选择是否需要中断该推送的流。如果不需要的话,客户端可以通过发送一个 RST_STREAM 帧来中止。

头压缩

http1.x 的头带有大量信息,而且每次都要重复发送。http/2 使用 encoder 来减少需要传输的 header 大小,通讯双方各自缓存一份头部字段表,既避免了重复 header 的传输,又减小了需要传输的大小。

对于相同的数据,不再通过每次请求和响应发送,通信期间几乎不会改变通用键-值对(用户代理、可接受的媒体类型,等等)只需发送一次。

事实上,如果请求中不包含首部(例如对同一资源的轮询请求),那么,首部开销就是零字节,此时所有首部都自动使用之前请求发送的首部。

如果首部发生了变化,则只需将变化的部分加入到 header 帧中,改变的部分会加入到头部字段表中,首部表在 http 2.0 的连接存续期内始终存在,由客户端和服务器共同渐进地更新。

需要注意的是,http 2.0 关注的是首部压缩,而我们常用的 gzip 等是报文内容(body)的压缩,二者不仅不冲突,且能够一起达到更好的压缩效果。

http/2 使用的是专门为首部压缩而设计的 HPACK 算法。

头压缩实例

协商示例

1
2
3
4
5
GET /page HTTP/1.1 Host: server.example.com Connection: Upgrade, HTTP2-Settings
Upgrade: HTTP/2.0 HTTP2-Settings: (SETTINGS payload) HTTP/1.1 200 OK
Content-length: 243 Content-type: text/html (... HTTP 1.1 response ...) (or)
HTTP/1.1 101 Switching Protocols Connection: Upgrade Upgrade: HTTP/2.0 (... HTTP
2.0 response ...)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const http2 = require("http2");
const fs = require("fs");
const server = http2.createSecureServer({
key: fs.readFileSync("localhost-privkey.pem"),
cert: fs.readFileSync("localhost-cert.pem"),
});
server.on("error", (err) => console.error(err));
server.on("stream", (stream, headers) => {
// stream is a Duplex
stream.respond({
"content-type": "text/html",
":status": 200,
});
stream.end("<h1>Hello World</h1>");
});
server.listen(8443);

为何需要 HTTP/3

  • TCP 队头阻塞问题
  • TCP 握手时长
  • 移动场景的网络切换成本:IP 地址会发生变化,而 TCP 协议是根据四元组来确定一个连接的,需要重新建立连接

HTTP/3

HTTP/3 基于 UDP 协议重新定义了连接,在 QUIC 层实现了无序、并发字节流的传输,解决了队头阻塞问题(包括基于 QPACK 解决了动态表的队头阻塞);

HTTP/3 重新定义了 TLS 协议加密 QUIC 头部的方式,既提高了网络攻击成本又降低了建立连接的速度(仅需 1 个 RTT 就可以同时完成建链与密钥协商);

HTTP/3 将 Packet、QUIC Frame、HTTP3 Frame 分离,实现了连接迁移功能,降低了 5G 环境下高速移动设备的连接维护成本。