跳到主要内容

2. 替代服务概念

本规范在 HTTP 中定义了一个新概念, 即"替代服务 (Alternative Service)". 当某个源 (origin) [RFC6454] 拥有可通过不同的协议/主机/端口组合访问的资源时, 就称该源有可用的替代服务.

替代服务可用于在网络上的另一个位置与源服务器上的资源交互, 并且可能使用不同的协议配置. 按照 [RFC7230] 第 9.1 节的含义, 替代服务被视为对源的资源具有权威性.

例如, 某个源:

("http", "www.example.com", "80")

可以声明其资源也可通过以下替代服务访问:

("h2", "new.example.com", "81")

从本质上讲, 替代服务明确以源为粒度; 它们不能选择性地应用于某个源内的部分资源.

对于任何给定资源, 替代服务都不会替换或改变其源; 一般而言, 位于访问机制"之上"的软件看不到替代服务. 替代服务本质上是可用于到达该源的备用路由信息, 其作用类似于 DNS CNAME 或 SRV 记录在名称解析层面定义路由信息. 每个源都会映射到一组这样的路由 -- 默认路由由源本身派生, 其他路由则基于替代服务信息引入.

此外需要注意, 替代服务元组中的第一个成员不同于源的 "scheme" 组成部分; 它更加具体, 不仅标识所用协议的主版本, 还可能标识该协议的通信选项.

这意味着使用替代服务的客户端可以改变其用于获取资源的主机, 端口和协议, 但这些改变禁止 (MUST NOT) 传播给正在使用 HTTP 的应用程序; 从应用程序的角度看, 正在访问的 URI 以及从该 URI 派生出的所有信息 (scheme, host 和 port) 都与之前相同.

重要的是, 这也包括其安全上下文; 特别是, 当使用 TLS [RFC5246] 进行认证时, 替代服务需要出示针对源主机名的证书, 而不是针对替代服务主机名的证书. 同样, Host 头字段 ([RFC7230] 第 5.4 节) 仍然由源派生, 而不是由替代服务派生 (这与使用 CNAME 时的情况相同).

不过, 这些改变可以 (MAY) 在调试工具, 控制台等位置可见.

形式上, 替代服务由以下组合标识:

  • 应用层协议协商 (Application Layer Protocol Negotiation, ALPN) 协议名称, 如 [RFC7301] 所述
  • 主机, 如 [RFC3986] 第 3.2.2 节所述
  • 端口, 如 [RFC3986] 第 3.2.3 节所述

ALPN 协议名称用于标识替代服务所使用的应用协议或协议套件. 注意, 就本规范而言, 除非某个 ALPN 协议名称的定义另有说明, 否则该名称所标识的协议套件隐含包含 TLS. 特别地, [RFC7301] 第 6 节注册的 ALPN 名称 "http/1.1" 标识基于 TLS 的 HTTP/1.1.

此外, 每个替代服务都必须 (MUST) 具有新鲜度生存期 (freshness lifetime), 以秒为单位表示 (见第 2.2 节).

客户端可以通过多种方式发现与某个源关联的替代服务. 本文档描述其中两种机制: "Alt-Svc" HTTP 头字段 (第 3 节) 和 "ALTSVC" HTTP/2 帧类型 (第 4 节).

本节余下部分描述替代服务的通用要求, 无论这些替代服务通过何种方式发现.

2.1. 主机认证​

客户端必须 (MUST) 有合理保证, 确认替代服务受整个源控制并且对整个源有效. 这可以缓解第 9.2 节描述的攻击.

就本文档而言, "合理保证"可以通过使用基于 TLS 的协议并执行 [RFC2818] 中定义的证书检查来建立. 客户端可以 (MAY) 施加额外条件来建立合理保证.

例如, 如果源的主机是 "www.example.com", 并且在 "other.example.com" 上以 "h2" 协议提供了一个替代服务, 同时所提供的证书对 "www.example.com" 有效, 则客户端可以使用该替代服务. 但是, 如果其中任一服务以 "h2c" 协议提供, 客户端就不能使用它, 因为在本规范发布时, 该协议中没有用于建立源与替代服务之间关系的机制.

2.2. 替代服务缓存​

发现替代服务的机制还会为替代服务关联一个新鲜度生存期; 例如, Alt-Svc 头字段使用 "ma" 参数.

只要替代服务被认为是新鲜的, 客户端就可以随时选择使用替代服务而不是源; 具体建议见第 2.4 节.

已经与替代服务建立连接的客户端, 在该替代服务的新鲜度生存期结束时无需停止使用它; 缓存机制旨在限制替代服务可用于建立新连接的时长, 而不是限制现有连接的使用.

替代服务对相关源具有完全权威性, 包括清除或更新缓存的替代服务条目, 延长新鲜度生存期, 以及源服务器原本具有的任何其他权限.

当替代服务用于将客户端引导到最优服务器时, 网络配置的变化可能导致缓存值不再最优. 因此, 在网络状态信息可用的情况下, 客户端检测到这种变化时, 应当 (SHOULD) 从缓存中移除所有缺少值为 "1" 的 "persist" 标志的替代服务.

2.3. 要求服务器名称指示​

除非客户端支持 TLS 服务器名称指示 (Server Name Indication, SNI), 否则禁止 (MUST NOT) 使用基于 TLS 的替代服务. 这有助于节约替代服务主机上的 IP 地址.

注意, 客户端在 TLS 中提供的 SNI 信息将属于源, 而不是替代服务 (Host HTTP 头字段值也是如此).

2.4. 使用替代服务​

从本质上讲, 替代服务是可选的 (OPTIONAL): 客户端不需要使用它们. 但是, 当服务器使用替代服务时, 客户端以可预测的方式运行是有益的, 这有助于负载均衡等目的.

因此, 如果支持本规范的客户端得知某个替代服务, 只要该替代服务信息是新鲜的 (第 2.2 节), 并且与现有连接相比, 替代服务协议的安全属性是理想的, 客户端就应当 (SHOULD) 在该替代服务可用后尽快将其用于关联源的所有请求. 随后, 可行的替代服务在各方面都被视为源; 这包括通告替代服务的能力.

如果客户端得知多个替代服务, 它会在考虑安全属性的同时, 根据自身标准选择最合适的替代服务. 例如, 某个源可能通告多个替代服务, 以通知客户端其支持多个 HTTP 版本.

对于某个给定请求, 如果客户端被配置为使用代理, 则不应 (SHOULD NOT) 为该请求直接连接到替代服务, 而应通过该代理路由该请求.

当客户端为某个请求使用替代服务时, 可以使用 Alt-Used 头字段 (第 5 节) 向服务器指示这一点.

客户端无需阻塞任何现有连接上的请求; 在替代连接建立之前, 仍可继续使用现有连接. 但是, 如果现有连接的安全属性较弱 (例如明文 HTTP/1.1), 那么为避免信息泄漏, 阻塞请求直到新连接完全可用可能是合理的.

此外, 如果到替代服务的连接失败或无响应, 客户端可以 (MAY) 回退到使用源或另一个替代服务. 但需要注意, 这可能成为降级攻击的基础, 从而失去替代服务提供的任何增强安全属性. 如果到替代服务的连接未能协商出预期协议 (例如 ALPN 未能协商 h2, 或者到 h2c 的 Upgrade 请求未被接受), 则必须 (MUST) 认为到该替代服务的连接已经失败.