本文目录

[[toc]]

通信模型

OSI 七层模型

  • 应用层: 为用户的应用程序提供网络服务。常见的应用层协议有 HTTP(超文本传输协议)、FTP(文件传输协议)、SMTP(简单邮件传输协议)、DNS(域名系统)等。用户直接使用的各种网络应用程序(如浏览器、邮件客户端)都运行在应用层。
  • 表示层: 处理数据的表示和转换,确保不同系统之间能够正确理解和处理数据。它包括数据的加密、解密、压缩、解压缩以及数据格式的转换(如 ASCII 码与 EBCDIC 码之间的转换)等功能。
  • 会话层: 负责建立、管理和终止会话。它允许不同主机上的应用程序之间进行会话,并提供会话的同步和恢复机制。
  • 传输层: 提供端到端的可靠通信或不可靠通信服务。主要有两种协议: 传输控制协议(TCP)和用户数据报协议(UDP)。
  • 网络层: 负责将数据包从源节点传输到目标节点,处理网络中的路由选择和寻址。它使用 IP 地址来标识网络中的设备,并通过路由器来实现不同网络之间的数据包转发。常见的网络层协议有 IPv4 和 IPv6。
  • 数据链路层: 将物理层接收到的比特流封装成帧,处理帧的传输和错误检测。它分为逻辑链路控制(LLC)子层和介质访问控制(MAC)子层。MAC 子层负责控制对物理介质的访问,通过 MAC 地址来标识网络中的设备;LLC 子层则负责提供错误控制和流量控制。
  • 物理层: 负责传输比特流,定义了物理介质(如电缆、光纤、无线)的电气、机械和功能特性,包括电压、信号编码、连接器规格等。

数据传输过程

  • 应用层报文被传送到运输层
  • 在最简单的情况下,运输层收取到报文并附上附加信息,该首部将被接收端的运输层使用
  • 应用层报文和运输层首部信息一道构成了运输层报文段。附加的信息可能包括: 允许接收端运输层向上向适当的应用程序交付报文的信息以及差错检测位信息。该信息让接收端能够判断报文中的比特是否在途中已被改变
  • 运输层则向网络层传递该报文段,网络层增加了如源和目的端系统地址等网络层首部信息,生成了网络层数据报
  • 网络层数据报接下来被传递给链路层,在数据链路层数据包添加发送端 MAC 地址和接收端 MAC 地址后被封装成数据帧
  • 在物理层数据帧被封装成比特流,之后通过传输介质传送到对端
  • 对端再一步步解开封装,获取到传送的数据

TCP/IP 五层模型

TCP/IP 五层协议是在实际应用中对 OSI 七层协议的简化,它将 OSI 模型中的会话层和表示层合并到应用层,形成了五层结构

  • 应用层: 包含了 OSI 模型中应用层、表示层和会话层的功能,为用户的应用程序提供网络接口,支持各种网络应用。
  • 传输层: 提供端到端的通信服务,包括 TCP 和 UDP 两种协议,确保数据的可靠传输或快速传输。
  • 网络层: 主要功能是进行数据包的路由选择和寻址,使用 IP 协议来实现不同网络之间的通信。
  • 数据链路层: 和 OSI 模型的数据链路层一样,将比特流封装成帧,处理帧的传输和错误检测,控制对物理介质的访问。
  • 物理层: 与 OSI 模型的物理层功能相同,负责传输比特流,处理物理介质和信号传输。

TCP/IP 四层模型

TCP/IP 四层协议是另一种简化的网络模型,它在五层模型的基础上,将物理层和数据链路层合并为网络接口层,形成了四层结构

  • 应用层(Application Layer): 为用户的应用程序提供网络服务,包含了各种应用层协议,如 HTTP、FTP、SMTP 等,支持用户使用各种网络应用。
  • 传输层(Transport Layer): 提供端到端的通信服务,通过 TCP 或 UDP 协议来实现数据的可靠传输或不可靠传输,确保应用程序之间的数据传输。
  • 网络层(Internet Layer): 主要功能是进行数据包的路由选择和寻址,使用 IP 协议来实现不同网络之间的通信,确保数据包能够从源主机传输到目标主机。
  • 网络接口层(Network Interface Layer): 负责将 IP 数据包封装成适合在物理网络上传输的帧,并通过物理介质进行传输。它涵盖了 OSI 模型中物理层和数据链路层的功能,处理不同类型的网络接口和物理介质。

区别

  • OSI 七层模型: 主要用于理论研究和教学,为网络协议的设计和分析提供了一个标准的参考模型,但由于其过于复杂,在实际网络中很少直接使用。
  • TCP/IP 五层模型: 在实际网络开发和教学中被广泛使用,它既保留了 OSI 模型的分层思想,又对其进行了简化,便于理解和实现。
  • TCP/IP 四层模型: 是互联网实际使用的网络模型,它更加注重网络的实用性和效率,被广泛应用于各种网络设备和操作系统中。

DNS (Domain Names System) 协议

DNS 用于将域名对应到不同的 IP 地址中,一般分为根域名 . 、 顶级域名 comcn 、 二级域名 cctv.com 等等

递归查询

  1. 本地 DNS 请求根域名服务器
  2. 根域名服务器请求顶级域名服务器
  3. 顶级域名服务器请求权限域名服务器
  4. 权限域名服务器发送域名对应的 IP 地址给顶级域名服务器
  5. 顶级域名服务器返回给根服务器
  6. 根服务器返回给本地 DNS

迭代查询

与递归查询类似,但是根域名服务器、顶级域名服务器只返回下一阶段的服务器地址,如根域名服务器返回顶级域名服务器地址,本地 DNS 自行请求顶级域名服务器查询权限域名服务器地址

域名缓存

  1. 浏览器缓存: 浏览器为了性能考虑自己会缓存 DNS ,不需要每次都去操作系统中查
  2. 操作系统缓存: 操作系统内部缓存有 域名/IP 映射表,不需要每次都去重新查询 DNS

浏览器中域名解析过程

  1. 检索浏览器缓存的 DNS 中是否保存有对应的 IP ,如果有,直接返回
  2. 请求本地 DNS ,本地 DNS 查询是否缓存对应 IP ,如果有,返回给浏览器
  3. 进行 DNS 迭代查询
  4. 将查询结果缓存到操作系统中,同时也发送给浏览器
  5. 浏览器接受到来自本地 DNS 的解析结果,缓存到浏览器 DNS 解析表

Web 架构

评估 Web 架构的关键属性

  • 性能 Performance: 影响高可用的关键因素
    • 网络性能 Network Performance
      • Throughput 吞吐量: 小于等于带宽 bandwidth
      • Overhead 开销: 首次开销,每次开销
    • 用户感知到的性能User-perceivedPerformance
      • Latency 延迟: 发起请求到接收到响应的时间
      • Completion完成时间: 完成一个应用动作所花费的时间
    • 网络效率 Network Efficiency
      • 重用缓存
      • 减少交互次数
      • 数据传输距离更近
      • COD ( Code on Demand ): 按需代码
  • 伸缩性 Scalability: 支持部署可以互相交互的大量组件
  • 简单性 Simplicity: 易理解、易实现、易验证
  • 可见性 Visiable: 对两个组件间的交互进行监视或者仲裁的能力。如缓存、分层设计等
  • 可移植性 Portability: 在不同的环境下运行的能力
  • 可靠性 Reliability: 出现部分故障时,对整体影响的程度
  • 可修改性 Modifiability: 对系统作出修改的难易程度,由可进化性、可定制性、可扩展性、可配置性、可重用性构成
    • 可进化性 Evolvability: 一个组件独立升级而不影响其他组件
    • 可扩展性 Extensibility: 向系统添加功能,而不会影响到系统的其他部分
    • 可定制性 Customizability: 临时性、定制性地更改某一要素来提供服务不对常规客户产生影响
    • 可配置性 Configurability: 应用部署后可通过修改配置提供新的功能
    • 可重用性 Reusabilit: 组件可以不做修改在其他应用在使用

REST 架构

名词

URL 、 URI 、 URN

  • URL: Uniform ResourceLocator
    • 所属标准: RFC1738(1994.12)
    • 功能: 表示资源的位置,期望提供查找资源的方法
  • URN: Uniform ResourceName
    • 所属标准: RFC2141(1997.05)
    • 功能: 期望为资源提供持久的、位置无关的标识方式,并允许简单地将多个命名空间映射到单个 URN 命名空间
    • 示例: 磁力链接 magnet:?xt=urn:sha1:YNCKHTQC5C
  • URI: UniformResOurceIdentifier
    • 所属标准: RFC1630 ( 1994.6 ) 、 RFC3986 ( 2005.1 ,取代 RFC2396 和 RFC2732 )
    • 功能: 用以区分资源,是 URL 和 URN 的超集,用以取代 URL 和 URN 概念
    • 组成: schema, user information, host, port, path, query, fragment
      • schema: 使用的协议,例如 sshmagnethttps
      • user information: 用户信息,例如 username:password@ ,主要用于鉴权, HTTP 协议中比较少见
      • host: 域名或者 IP
      • port: 端口
      • path: 请求的资源路径
      • query: 请求参数
      • fragment: JS 中的 Hash 部分,例如 #anchor

响应状态码码表

  • 1xx: 请求已接收到,需要进一步处理才能完成, HTTP/1.0 不支持
    • 100 Continue: 上传大文件前使用,由客户端发起请求中携带 Expect: 100-continue 头部触发
    • 101 Switch Protocols: 协议升级使用,由客户端发起请求中携带 Upgrade: 头部触发,如升级 websocket 或者 HTTP/2.0
    • 102 Processing: WebDAV 请求可能包含许多涉及文件操作的子请求,需要很长时间才能完成请求。该代码表示​服务器已经收到并正在处理请求,但无响应可用。这样可以防止客户端超时,并假设请求丢失
  • 2xx: 成功处理请求
    • 200 OK: 成功返回响应。
    • 201 Created: 有新资源在服务器端被成功创建。
    • 202 Accepted: 服务器接收并开始处理请求,但请求未处理完成。这样一个模糊的概念是有意如此设计,可以覆盖更多的场景。例如异步、需要长时间处理的任务。
    • 203 Non-Authoritative Information: 当代理服务器修改了 origin server 的原始响应包体时(例如更换了 HTML 中的元素值),代理服务器可以通过修改 200 为 203 的方式告知客户端这一事实,方便客户端为这一行为作出相应的处理。 203 响应可以被缓存。
    • 204 NoContent: 成功执行了请求且不携带响应包体,并暗示客户端无需更新当前的页面视图。
    • 205 ResetContent: 成功执行了请求且不携带响应包体,同时指明客户端需要更新当前页面视图。
    • 206 PartialContent: 使用 range 协议时返回部分响应内容时的响应码
    • 207 Multi-Status: RFC4918,在 WEBDAV 协议中以 XML 返回多个资源的状态。
    • 208 Already Reported: RFC5842,为避免相同集合下资源在 207 响应码下重复上报,使用 208 可以使用父集合的响应码。
  • 3xx: 重定向使用 Location 指向的资源或者缓存中的资源。在 RFC2068 中规定客户端重定向次数不应超过 5 次,以防止死循环。
    • 300 Multiple Choices: 资源有多种表述,通过 300 返回给客户端后由其自行选择访问哪一种表述。由于缺乏明确的细节,300 很少使用。
    • 301 Moved Permanently: 资源永久性的重定向到另一个 URI 中。
    • 302 Found: 资源临时的重定向到另一个 URI 中。
    • 303 See Other: 重定向到其他资源,常用于 POST/PUT 等方法的响应中。
    • 304 Not Modified: 当客户端拥有可能过期的缓存时,会携带缓存的标识 etag、时间等信息询问服务器缓存是否仍可复用,而 304 是告诉客户端可以复用缓存。
    • 307 Temporary Redirect: 类似 302,但明确重定向后请求方法必须与原请求方法相同,不得改变。
    • 308 Permanent Redirect: 类似 301,但明确重定向后请求方法必须与原请求方法相同,不得改变。
  • 4xx: 客户端错误
    • 400 Bad Request: 服务器认为客户端出现了错误,但不能明确判断为以下哪种错误时使用此错误码。例如 HTTP 请求格式错误。
    • 401 Unauthorized: 用户认证信息缺失或者不正确,导致服务器无法处理请求。
    • 407 Proxy Authentication Required: 对需要经由代理的请求,认证信息未通过代理服务器的验证
    • 403 Forbidden: 服务器理解请求的含义,但没有权限执行此请求
    • 404 Not Found: 服务器没有找到对应的资源
    • 410 Gone: 服务器没有找到对应的资源,且明确的知道该位置永久性找不到该资源
    • 405 Method Not Allowed: 服务器不支持请求行中的 method 方法
    • 406 Not Acceptable: 对客户端指定的资源表述不存在(例如对语言或者编码有要求),服务器返回表述列表供客户端选择。
    • 408 Request Timeout: 服务器接收请求超时
    • 409 Conflict: 资源冲突,例如上传文件时目标位置已经存在版本更新的资源
    • 411 Length Required: 如果请求含有包体且未携带 Content-Length 头部,且不属于 chunk 类请求时,返回 411
    • 412 Precondition Failed: 复用缓存时传递的 If-Unmodified-Since 或 If-None-Match 头部不被满足
    • 413 Payload Too Large/Request Entity Too Large: 请求的包体超出服务器能处理的最大长度
    • 414 URI Too Long: 请求的 URI 超出服务器能接受的最大长度
    • 415 Unsupported Media Type: 上传的文件类型不被服务器支持
    • 416 Range Not Satisfiable: 无法提供 Range 请求中指定的那段包体
    • 417 Expectation Failed: 对于 Expect 请求头部期待的情况无法满足时的响应码
    • 421 Misdirected Request: 服务器认为这个请求不该发给它,因为它没有能力处理。
    • 426 Upgrade Required: 服务器拒绝基于当前 HTTP 协议提供服务,通过 Upgrade 头部告知客户端必须升级协议才能继续处理。
    • 428 Precondition Required: 用户请求中缺失了条件类头部,例如 If-Match
    • 429 Too Many Requests: 客户端发送请求的速率过快
    • 431 Request Header Fields Too Large: 请求的 HEADER 头部大小超过限制
    • 451 Unavailable For Legal Reasons: RFC7725 ,由于法律原因资源不可访问
  • 5xx: 服务器错误
    • 500 Internal Server Error: 服务器内部错误,且不属于以下错误类型
    • 501 Not Implemented: 服务器不支持实现请求所需要的功能
    • 502 Bad Gateway: 代理服务器无法获取到合法响应
    • 503 Service Unavailable: 服务器资源尚未准备好处理当前请求
    • 504 Gateway Timeout: 代理服务器无法及时的从上游获得响应
    • 505 HTTP Version Not Supported: 请求使用的 HTTP 协议版本不支持
    • 507 Insufficient Storage: 服务器没有足够的空间处理请求
    • 508 Loop Detected: 访问资源时检测到循环
    • 511 Network Authentication Required: 代理服务器发现客户端需要进行身份验证才能获得网络访问权限

ABNF

ABNF 是一种用来描述格式的语言,一般 HTTP 协议格式会使用 ABNF 描述

  • 空白字符: 用来分隔定义中的各个元素,例如: method SP request-target SP HTTP-version CRLF
  • 选择 /: 表示多个规则都是可供选择的规则,例如: start-line = request-line / status-line
  • 值范围 %c##-##: 表示取值范围,例如: OCTAL = “0” / “1” / “2” / “3” / “4” / “5” / “6” / “7” 与 OCTAL = %x30-37 等价
  • 序列组合 (): 将规则组合起来,视为单个元素
  • 不定量重复 m*n:
    • * 元素表示零个或更多元素,例如: *( header-field CRLF )
    • 1* 元素表示一个或更多元素, 2*4 元素表示两个至四个元素
  • 可选序列 []: 例如: [ message-body ]
  • ALPHA: 大写和小写ASCII字母 (A-Z, a-z) ,例如: %x41-5A / %x61-7A
  • DIGIT: 数字 ( 0-9 ) ,例如: %x30-39
  • HEXDIG: 十六进制数字 ( 0-9, A-F, a-f ) ,例如: DIGIT / “A” / “B” / “C” / “D” / “E” / “F”
  • DQUOTE: 双引号,例如: %x22
  • SP: 空格,例如: %x20
  • HTAB: 横向制表符,例如: %x09
  • WSP: 空格或横向制表符,例如: SP / HTAB
  • OWS: 连续空白,例如: *( SP / HTAB )
  • LWSP: 直线空白(晚于换行),例如: *(WSP / CRLF WSP)
  • VCHAR: 可见(打印)字符,例如: %x21-7E
  • CHAR: 任何 7- 位 US-ASCII 字符,不包括 NUL ( %x00 ) ,例如: %x01-7F
  • OCTET: 8 位数据,例如: %x00-FF
  • CTL: 控制字符,例如: %x00-1F / %x7F
  • CR: 回车,例如: %x0D
  • LF: 换行,例如: %x0A
  • CRLF: 互联网标准换行,例如: CR LF
  • BIT: 二进制数字,例如: “0” / “1”

Header

Request Header

  • Host: 格式为 Host: uri-host [ ":" port ] 用于寻找虚拟主机,会使用 Host 与域名进行匹配。 规范中要求如果缺少 Host 、 超过一个 Host 、 Host 值不合法,需要返回 400
  • Max-Forwards: 只能用于 OPTIONS 与 TRACE 请求,限制 Proxy 最大转发次数,也就是限制 X-Forwarded-For 中记录的 IP 数,格式为: Max-Forwards: 1 DIGIT
  • Via: 经过的代理服务器名称及版本,格式为 Via: received-protocol RWS received-by [ RWS comment ]
  • X-Real-IP: 传递用户 IP
  • X-Forwarded-For: 传递链路中经过的 IP
  • User-Agent: 格式为 ${token} ["/" ${version} [( ${product} 或者 ${comment} )]] , 例如: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:66.0) Gecko/20100101 Firefox/66.0
  • Referer: 浏览器自动填充,记录当前页面对来自哪个页面,服务器端常用于统计分析、缓存优化、防盗链等功能,有两种不会添加 Referer 的情况:
    • 来源页面使用 file 协议或者 data URI
    • 当前页面是 http ,来源页面是 https
  • From: 主要用于网络爬虫,告诉服务器如何通过邮箱联系爬虫负责人,例如: From: me@he110.site

Response Header

  • Server: 指明服务器上所用软件的信息,用于帮助客户端定位问题或者统计数据,格式对标 User-Agent
  • Allow: 告诉客户端,服务器上该 URI 对应的资源允许哪些方法的执行,例如: Allow: GET, HEAD, PUT
  • Accept-Ranges: 告诉客户端服务器上该资源是否允许 range 请求,例如: Accept-Ranges: bytesAccept-Ranges: none
  • Content-Disposition: 声明需要下载的文件资源,格式为 "inline" | "attachment" | disp-ext-type
    • inline: 指定包体是以 inline 内联的方式,作为页面的一部分展示
    • attachment: 指定浏览器将包体以附件的方式下载,例如: Content-Disposition: attachment; filename="filename.jpg"filename 可省略。
    • 在 multipart/form-data 类型的响应中,可以用于子消息体部分,例如: Content-Disposition: form-data; name="fieldName"; filename="filename.jpg"

HTTP

组成

HTTP 一般有三部分组合:

start-line

按照请求与响应,有所区分

request-line

请求行,格式为 method SP request-target SP HTTP-version CRLF

  • method 用于指明目的,一般是动词,各 method 以及语义表如下:
  • GET: 主要的获取信息方法,大量的性能优化都针对该方法,幂等方法
  • HEAD: 类似 GET 方法,但服务器不发送 BODY,用以获取 HEAD 元数据,幂等方法
  • POST: 常用于提交 HTML FORM 表单、新增资源等
  • PUT: 更新资源,带条件时是幂等方法
  • DELETE: 删除资源,幂等方法
  • CONNECT: 建立 tunnel 隧道
  • OPTIONS: 显示服务器对访问资源支持的方法,幂等方法
  • TRACE: 回显服务器收到的请求,用于定位问题。有安全风险
  • PROPFIND: 用于文档管理的 WEBDAV 方法,从 Web 资源中检索以 XML 格式存储的属性。它也被重载,以允许一个检索远程系统的集合结构(也叫目录层次结构)
  • PROPPATCH: 用于文档管理的 WEBDAV 方法,在单个原子性动作中更改和删除资源的多个属性
  • MKCOL: 用于文档管理的 WEBDAV 方法,创建集合或者目录
  • COPY: 用于文档管理的 WEBDAV 方法,将资源从一个 URI 复制到另一个 URI
  • MOVE: 用于文档管理的 WEBDAV 方法,将资源从一个 URI 移动到另一个 URI
  • LOCK: 用于文档管理的 WEBDAV 方法,锁定一个资源。 WebDAV 支持共享锁和互斥锁。
  • UNLOCK: 用于文档管理的 WEBDAV 方法,解除资源的锁定
  • request-target 即请求目标,支持 origin-form / absolute-form / authority-form / asterisk-form
  • origin-form: 路径地址 + 参数,格式为 absolute-path [ "?" query ] ,如果地址为空,需要给 /
  • absolute-form: 仅用于向正向代理 proxy 发起请求时使用,需要携带 协议 、 域名 、 端口 等域信息
  • authority-form:: 仅用于 CONNECT 方法,例如 CONNECT www.example.com:80 HTTP/1.1
  • asterisk-form: 仅用于 OPTIONS 方法,一般给 * ,表示不关注请求哪个 uri
  • HTTP-version: 参考 HTTP 发展历史 ,支持 HTTP/0.9 、 HTTP/1.0 、 HTTP/1.1 、 HTTP/2 、 HTTP/3

status-line

响应行,格式为 HTTP-version SP status-code SP reason-phrase CRLF

header-field

格式为 field-name ":" OWS field-value OWS

message-body

格式为 *OCTET

长连接

HTTP 的网络传输过程

长连接解决的问题

HTTP/1.0 与 HTTP/1.1 是基于 TCP 并且不支持多路复用的,这意味着每请求一个资源都需要建立一个 TCP 连接,获取完响应后将该连接关闭。

由于资源碎片化,同一时间可能会有多个请求发出,这将有大量的 TCP 握手工作,很可能会击穿操作系统内的 SYN_RECV 、 ACCEPT 队列,导致自身被 DDos 。

所以在浏览器中,会对每个源进行 TCP 连接数进行限制,比较常见的是 Chrome 的限制为最多 6 个 TCP 连接。

HTTP/1.1 中为了减少 TCP 连接重复创建与销毁,引入了长连接的概念,即: 在 TCP 连接接受完响应后,不立即关闭连接,如果此时有其他的同源 HTTP 请求,可以直接复用此连接。

如何开启长连接

观察 Connection 字段:

  • 如果请求头中没有 Connection 或者发送了 Connection: Close ,则是使用短连接。
  • 如果请求头存在 Connection: Keep-Alive ,但是响应头没有 Connection 或者返回了 Connection: Close ,表示客户端希望开启长连接,但是服务端不支持开启长连接,还是使用短连接
  • 如果请求头和响应头都存在 Connection: Keep-Alive ,则使用长连接。

HTTP/1.1 中默认支持长连接,不需要 Connection: Keep-Alive ,但是依旧可以通过 Connection: Close 关闭长连接功能。

请求阻塞

  • 存在更高优先级的请求先处理
  • 同一个源已经打开了 6 个 TCP 连接,达到阈值,仅在 HTTP/1.0 和 HTTP/1.1 会出现
  • 浏览器正在短暂的分配磁盘缓存中的空间

Proxy

HTTP 许多内容描述的都是端到端的信息,如果出现代理的情况,往往会出现需要特殊处理的情况,例如屏蔽 Header 、修改 Proxy 行为等。

Connection

Connection 头仅表示两端通信使用的连接情况,在转发的时候,不应该转发 Connection ,而是应该剔除该头部。

否则如果代理服务器本身不支持长连接,却转发了客户端与服务器的 Connection: Keep-Alive ,会导致代理服务器与两端都建立长连接,然后代理服务器使用的却是短连接,导致通信出现异常。

如果通信链路中存在此类代理服务器,需要将 Connection 替换为 Proxy-Connect ,这样的话:

  • 陈旧的代理服务器不识别该头部: 退化为短连接
  • 新版本的代理服务器理解该头部:
    • 与客户端建立长连接
    • 与服务器使用 Connection 替代 Proxy-Connect 头部

内容协商

同一个 Uri ,在不同国家/地区,不同的用户设备,网络情况等,可能都会代表不同的内容,获取这些内容的过程,就是内容协商。

按照决定权在客户端还是服务器,分为主动式内容协商和被动式内容协商。

主动式内容协商 ( Proactive )

由客户端先在请求头部中提出需要的表述形式,而服务器根据这些请求头部提供特定的 representation 表述。

该方式为较为常见的形式。

协商时,需要客户端需要传输的内容如下:

  • q: 质量因子。即内容的质量、优先级等。
    • 没有单独的请求头,在相关请求头中配置。
    • 质量指的比如图片质量,优先级是内容倾向于哪些格式。
  • Accept: 媒体资源的 MIME 类型及质量因子。例如:
    • Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    • Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
  • Accept-Charset: 字符编码。由于 UTF-8 格式广为使用, Accept-Charset 已被废弃
  • Accept-Encoding: 内容编码。主要指压缩算法,例如 Accept-Encoding: gzip, deflate, br
  • Accept-Language: 表述语言。例如:
    • Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
    • Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2

服务端描述资源时,常见响应头如下:

  • Content-Type: MIME 类型 、 编码。例如: Content-Type: text/html; charset=utf-8
  • Content-Encoding: 内容编码。例如: Content-Encoding: gzip
  • Content-Language: 语言。例如: Content-Language: de-DE, en-CA

被动式内容协商 ( Reactive )

指服务器返回 300 Multiple Choices 或者 406 Not Acceptable,由客户端选择一种表述 URI 使用。

这种情况使用较少,一方面浏览器支持程度较差,另一方面是需要额外的请求往返,性能开销更高。

Response

body 传输

确定包体完整长度传输

发送 HTTP 消息时已能够确定包体的全部长度,使用 Content-Length 头部明确指明包体长度,必须与实际传输的包体长度一致。

  • 如果传输的 Content-Length > Body 实际长度,请求会直接报错
  • 如果传输的 Content-Length < Body 实际长度, Body 的内容会被截断

优点: 接收端处理简单。

缺点: 压缩体积较大的包体时,需要等待包体压缩完成,才能发送响应,客户端接收时间相对较晚。

不能确定包体完整长度传输

发送 HTTP 消息时不能确定包体的全部长度,使用 Transfer-Encoding 头部指明使用 Chunk 传输方式,含 Transfer-Encoding 头部后 Content-Length 头部应被忽略

优点:

  • 基于长连接持续推送动态内容
  • 压缩体积较大的包体时,不必完全压缩完(计算出头部)再发送,可以边发送边压缩
  • 传递必须在包体传输完才能计算出的 Trailer 头部

缺点:

  • 接收端处理流程复杂
  • 没有正式的 RFC ,支持程度依赖浏览器实现

HTTP Range

HTTP Range 规范 ( RFC7233 ) 允许服务器基于客户端的请求只发送响应包体的一部分给到客户端,而客户端自动将多个片断的包体组合成完整的体积更大的包体

通过 Range 可以实现:

  • 断点续传
  • 多线程下载
  • 视频播放从指定位置立即开始播放

服务器通过 Accept-Range 头部表示是否支持 Range 请求。

  • Accept-Ranges: bytes: 支持
  • Accept-Ranges: none: 不支持

客户端通过 Range 请求头传递请求范围,如: Range: bytes=0-499

基于字节,设包体总长度为 10000

  • 第 1 个 500 字节: bytes=0-499
  • 第 2 个 500 字节: bytes=500-999
  • 支持分段: bytes=500-600,601-999
  • 最后 1 个 500 字节: bytes=-500 、 bytes=9500-
  • 仅要第 1 个和最后 1 个字节: bytes=0-0,-1

Range 功能也可以结合缓存进行,例如: 请求的前 100 字节命中了缓存,后 100 字节需要重新请求,有两种方案:

  • 可以使用 If-Unmodified-Since 或者 If-Match
  • 直接使用 If-Range: ETag / Last-Modified

响应结果有三种:

  • 206 Partial Content: 一部分命中了协商缓存,另一部分重新下载,通过 Content-Range 表示当前片断包体在完整包体中的位置
    • 例如: Content-Range: bytes 42-1233/1234 表示获取了 42-1233 字节的数据,数据包总字节为 1234
  • 200 OK: 服务器不支持 Range 请求时,则以 200 返回完整的响应包体
  • 416 Range Not Satisfiable: 请求范围不满足实际资源的大小,例如: Content-Range: bytes */1234

如果请求的片段不连续,会使用 multipart 返回,例如: Content-Type:multipart/byteranges; boundary=…

CORS

同源策略

限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互,避免站点 B 的脚本就可以随意修改站点 A 的 DOM 结构

同源策略在设计的时候需要兼顾可用性与安全性:

可用性: 网站创作者可以决定跨域请求是否对本站点安全

  • script、img、iframe、link、video、audio 等带有 src 属性的标签可以跨域访问
  • 允许跨域写操作: 例如表单提交或者重定向请求。
    • 原因在于表单项通常由站点开发者自行配置,开发者知道发生了跨域写操作,以及对应的安全风险,同时也是为了跟历史代码兼容。
  • CSRF 安全性问题

安全性: 浏览器需要防止站点 A 的脚本向站点 B 发起危险动作

  • Cookie、LocalStorage 和 IndexDB 无法读取
  • DOM 无法获得(防止跨域脚本篡改 DOM 结构)
  • AJAX 请求不能发送: AJAX 可以通过 CDN 等脚本注入,不一定是开发者自行编写,所以需要默认拦截。

CORS 过程

如果站点 A 允许站点 B 的脚本访问其资源,必须在 HTTP 响应中显式的告知浏览器,站点 B 是被允许访问的

  • 访问站点 A 的请求,浏览器应告知该请求来自站点 B
  • 站点 A 的响应中,应明确哪些跨域请求是被允许的

考虑到安全性、性能、兼容性, CORS 将请求划分为简单请求与非简单请求,二者的 CORS 流程是不同的。

  • 安全性: 非简单请求往往会对系统进行修改或者敏感操作
  • 性能: 简单请求往往与 form 表单一致,不进行预检可以降低服务器压力
  • 兼容性: 向前兼容 form 表单发送的请求

简单请求

简单请求需要满足以下条件:

  • GET/HEAD/POST 方法之一
  • 仅能使用 CORS 安全的头部: Accept 、 Accept-Language 、 Content-Language 、 Content-Type
  • Content-Type 值只能是: text/plain 、 multipart/form-data 、 application/x-www-form-urlencoded 三者其中之一

简单请求基本流程如下:

  • 请求中携带 Origin 头部告知来自哪个域
  • 响应中携带 Access-Control-Allow-Origin 头部表示允许哪些域
  • 浏览器放行

非简单请求

除了简单请求以外都是非简单请求,非简单请求需要进行预检。

预检请求头部

  • Access-Control-Request-Method
  • Access-Control-Request-Headers

预检请求响应

  • Access-Control-Allow-Methods
  • Access-Control-Allow-Headers
  • Access-Control-Max-Age
sequenceDiagram
  participant Client
  participant Server

  rect rgb(255, 255, 255)
  Note over Client, Server: 预检请求
  Client->>Server: OPTIONS /doc HTTP/1.1
  Note right of Client: "Origin: Server-b.com<br/>Access-Control-Request-Method: POST<br/>Access-Control-Request-Headers: X-PINGOTHER, Content-Type"

  Server-->>Client: HTTP/1.1 200 OK
  Note left of Server: "Access-Control-Allow-Origin: http://foo.example<br/>Access-Control-Allow-Headers: X-PINGOTHER, Content-Type<br/>Access-Control-Max-Age: 86400"
  end

  rect rgb(255, 255, 255)
  Note over Client, Server: 正式请求
  Client->>Server: POST /doc HTTP/1.1
  Note right of Client: "X-PINGOTHER: pingpong<br/>Content-Type: text/xml charset=UTF-8<br/>Origin: Server-b.com"

  Server-->>Client: HTTP/1.1 200 OK
  Note left of Server: Access-Control-Allow-Origin: http://foo.example
  end

CORS Header

请求头部:

  • Origin ( RFC6454 ): 一个页面的资源可能来自于多个域名,在 AJAX 等子请求中标明来源于某个域名下的脚本,以通过服务器的安全校验
    • origin: 格式为: "Origin:" OWS origin-list-or-null OWS
    • origin-list-or-null: 格式为: %x6E %x75 %x6C %x6C / origin-list
    • origin-list: 格式为: serialized-origin *( SP serialized-origin )
    • serialized-origin: 格式为: scheme "://" host [ ":" port ]
  • Access-Control-Request-Method: 在 preflight 预检请求 ( OPTIONS ) 中,告知服务器接下来的请求会使用哪些方法
  • Access-Control-Request-Headers: 在 preflight 预检请求 ( OPTIONS ) 中,告知服务器接下来的请求会传递哪些头部

响应头部

  • Access-Control-Allow-Methods: 在 preflight 预检请求的响应中,告知客户端后续请求允许使用的方法
  • Access-Control-Allow-Headers: 在 preflight 预检请求的响应中,告知客户端后续请求允许携带的头部
  • Access-Control-Max-Age: 在 preflight 预检请求的响应中,告知客户端该响应的信息可以缓存多久
  • Access-Control-Expose-Headers: 告知浏览器哪些响应头部可以供客户端使用,默认情况下只有 Cache-Control 、 Content-Language 、 Content-Type 、 Expires 、 Last-Modified 、 Pragma 可供使用
  • Access-Control-Allow-Origin: 告知浏览器允许哪些域访问当前资源, * 表示允许所有域。为避免缓存错乱,响应中需要携带 Vary: Origin
  • Access-Control-Allow-Credentials: 告知浏览器是否可以将 Credentials 暴露给客户端使用,Credentials 包含 cookie 、 authorization 类头部、 TLS 证书等。

条件请求

由客户端携带条件判断信息,而服务器预执行条件验证过程成功后,再返回资源的表述,常见应用场景:

  • 协商缓存、 SWR 等
  • 断点续传时对之前内容的验证
  • 当多个客户端并行修改同一资源时,防止某一客户端的更新被错误丢弃

条件请求中,服务器使用验证器 ( validator ) 对资源进行验证,判断资源是否可用,一般分为两种:

  • 强验证器: 服务器上的资源表述只要有变动(例如版本更新或者元数据更新),那么以旧的验证头部访问一定会导致验证不过
  • 弱验证器: 服务器上资源变动时,允许一定程度上仍然可以验证通过(例如一小段时间内仍然允许缓存有效)

验证器通过 ETag 与 Last-Modified 通知客户端,当前请求验证的资源有效期

  • ETag: ETag 包含了对应资源表述的标签,由后端返回,一般为 ${last_modified_time}-${content_length}
    • ETag: "tag": 使用强验证器
    • ETag: W/"tag": 使用弱验证器
  • Last-Modified: 表示对应资源表述的上次修改时间
    • Date 也是时间,但是 Date 是响应包体的生成时间, Last-Modified 不能晚于 Date

在进行竞态操作时,又是不止需要匹配,还需要不匹配(断点续传), HTTP 有以下进行条件判断的请求头:

  • If-Match: 检查资源的 ETag 是否与服务器当前资源的 ETag 完全匹配,格式为: If-Match "*" / 1#entity-tag
  • If-None-Match: 检查资源的 ETag 是否与服务器当前资源的 ETag 不匹配 ,格式为: If-None-Match "*" / 1#entity-tag
  • If-Modified-Since: 检查资源是否在指定时间后 被修改过 ,格式为: If-Modified-Since HTTP-date
  • If-Unmodified-Since: 检查资源是否在指定时间后 未被修改过,格式为: If-Unmodified-Since HTTP-date
  • If-Range: 配合 Range 分块下载,格式为: If-Range entity-tag / HTTP-date

具体的判断流程可以参考: