2. 替代服务概念 (Alternative Services Concepts)
本规范在 HTTP 中定义了一个新概念, 即"替代服务 (Alternative Service)". 当某个源 (origin) [RFC6454] 拥有可通过不同的协议/主机/端口组合访问的资源时, 就称该源有可用的替代服务.
替代服务可用于在网络上的另一个位置与源服务器上的资源交互, 并且可能使用不同的协议配置. 按照 [RFC7230] 第 9.1 节的含义, 替代服务被视为对源的资源具有权威性.
例如, 某个源:
("http", "www.example.com", "80")
可以声明其资源也可通过以下替代服务访问:
("h2", "new.example.com", "81")
从本质上讲, 替代服务明确以源为粒度; 它们不能选择性地应用于某个源内的部分资源.
对于任何给定资源, 替代服务都不会替换或改变其源; 一般而言, 位于访问机制"之上"的软件看不到替代服务. 替代服务本质上是可用于到达该源的备用路由信息, 其作用类似于 DNS CNAME 或 SRV 记录在名称解析层面定义路由信息. 每个源都会映射到一组这样的路由 -- 默认路由由源本身派生, 其他路由则基于替代服务信息引入.
此外需要注意, 替代服务元组中的第一个成员不同于源的 "scheme" 组成部分; 它更加具体, 不仅标识所用协议的主版本, 还可能标识该协议的通信选项.
这意味着使用替代服务的客户端可以改变其用于获取资源的主机, 端口和协议, 但这些改变禁止传播给正在使用 HTTP 的应用程序; 从应用程序的角度看, 正在访问的 URI 以及从该 URI 派生出的所有信息 (scheme, host 和 port) 都与之前相同.
重要的是, 这也包括其安全上下文; 特别是, 当使用 TLS [RFC5246] 进行认证时, 替代服务需要出示针对源主机名的证书, 而不是针对替代服务主机名的证书. 同样, Host 头字段 ([RFC7230] 第 5.4 节) 仍然由源派生, 而不是由替代服务派生 (这与使用 CNAME 时的情况相同).
不过, 这些改变可以在调试工具, 控制台等位置可见.
形式上, 替代服务由以下组合标识:
- 应用层协议协商 (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.
此外, 每个替代服务都必须具有新鲜度生存期 (freshness lifetime), 以秒为单位表示 (见第 2.2 节).
客户端可以通过多种方式发现与某个源关联的替代服务. 本文档描述其中两种机制: "Alt-Svc" HTTP 头字段 (第 3 节) 和 "ALTSVC" HTTP/2 帧类型 (第 4 节).
本节余下部分描述替代服务的通用要求, 无论这些替代服务通过何种方式发现.
2.1. 主机认证 (Host Authentication)
客户端必须有合理保证, 确认替代服务受整个源控制并且对整个源有效. 这可以缓解第 9.2 节描述的攻击.
就本文档而言, "合理保证"可以通过使用基于 TLS 的协议并执行 [RFC2818] 中定义的证书检查来建立. 客户端可以施加额外条件来建立合理保证.
例如, 如果源的主机是 "www.example.com", 并且在 "other.example.com" 上以 "h2" 协议提供了一个替代服务, 同时所提供的证书对 "www.example.com" 有效, 则客户端可以使用该替代服务. 但是, 如果其中任一服务以 "h2c" 协议提供, 客户端就不能使用它, 因为在本规范发布时, 该协议中没有用于建立源与替代服务之间关系的机制.
2.2. 替代服务缓存 (Alternative Service Caching)
发现替代服务的机制还会为替代服务关联一个新鲜度生存期; 例如, Alt-Svc 头字段使用 "ma" 参数.
只要替代服务被认为是新鲜的, 客户端就可以随时选择使用替代服务而不是源; 具体建议见第 2.4 节.
已经与替代服务建立连接的客户端, 在该替代服务的新鲜度生存期结束时无需停止使用它; 缓存机制旨在限制替代服务可用于建立新连接的时长, 而不是限制现有连接的使用.
替代服务对相关源具有完全权威性, 包括清除或更新缓存的替代服务条目, 延长新鲜度生存期, 以及源服务器原本具有的任何其他权限.
当替代服务用于将客户端引导到最优服务器时, 网络配置的变化可能导致缓存值不再最优. 因此, 在网络状态信息可用的情况下, 客户端检测到这种变化时, 应当从缓存中移除所有缺少值为 "1" 的 "persist" 标志的替代服务.
2.3. 要求服务器名称指示 (Requiring Server Name Indication)
除非客户端支持 TLS 服务器名称指示 (Server Name Indication, SNI), 否则禁止使用基于 TLS 的替代服务. 这有助于节约替代服务主机上的 IP 地址.
注意, 客户端在 TLS 中提供的 SNI 信息将属于源, 而不是替代服务 (Host HTTP 头字段值也是如此).
2.4. 使用替代服务 (Using Alternative Services)
从本质上讲, 替代服务是可选的: 客户端不需要使用它们. 但是, 当服务器使用替代服务时, 客户端以可预测的方式运行是有益的, 这有助于负载均衡等目的.
因此, 如果支持本规范的客户端得知某个替代服务, 只要该替代服务信息是新鲜的 (第 2.2 节), 并且与现有连接相比, 替代服务协议的安全属性是理想的, 客户端就应当在该替代服务可用后尽快将其用于关联源的所有请求. 随后, 可行的替代服务在各方面都被视为源; 这包括通告替代服务的能力.
如果客户端得知多个替代服务, 它会在考虑安全属性的同时, 根据自身标准选择最合适的替代服务. 例如, 某个源可能通告多个替代服务, 以通知客户端其支持多个 HTTP 版本.
对于某个给定请求, 如果客户端被配置为使用代理, 则不应为该请求直接连接到替代服务, 而应通过该代理路由该请求.
当客户端为某个请求使用替代服务时, 可以使用 Alt-Used 头字段 (第 5 节) 向服务器指示这一点.
客户端无需阻塞任何现有连接上的请求; 在替代连接建立之前, 仍可继续使用现有连接. 但是, 如果现有连接的安全属性较弱 (例如明文 HTTP/1.1), 那么为避免信息泄漏, 阻塞请求直到新连接完全可用可能是合理的.
此外, 如果到替代服务的连接失败或无响应, 客户端可以回退到使用源或另一个替代服务. 但需要注意, 这可能成为降级攻击的基础, 从而失去替代服务提供的任何增强安全属性. 如果到替代服务的连接未能协商出预期协议 (例如 ALPN 未能协商 h2, 或者到 h2c 的 Upgrade 请求未被接受), 则必须认为到该替代服务的连接已经失败.