跳到主要内容

16. 扩展 HTTP

HTTP 定义了若干通用扩展点, 可以在不引入新版本的情况下为协议引入能力, 包括方法, 状态码, 字段名, 以及已定义字段内的进一步扩展点, 例如认证方案和缓存指令 (参见 [CACHING] 的 Section 5.2.3 中的 Cache-Control 扩展). 由于 HTTP 的语义没有版本号, 这些扩展点是持久的; 所使用的协议版本不会影响它们的语义.

不鼓励版本无关扩展依赖正在使用的特定协议版本, 或与该版本交互. 当无法避免时, 需要仔细考虑该扩展如何跨版本互操作.

此外, HTTP 的特定版本可能具有自己的扩展点, 例如 HTTP/1.1 中的传输编码 ([HTTP/1.1] 的 Section 6.1) 以及 HTTP/2 SETTINGS 或帧类型 ([HTTP/2]). 这些扩展点特定于其所在的协议版本.

版本特定扩展不能覆盖或修改版本无关机制或扩展点 (例如方法或头字段) 的语义, 除非该协议元素明确允许. 例如, CONNECT 方法 (Section 9.3.6) 允许这样做.

这些指南确保即使路径中的不同部分实现了不同版本的 HTTP, 协议也能正确且可预测地运行.

16.1. 方法可扩展性

16.1.1. 方法注册表

由 IANA 在 https://www.iana.org/assignments/http-methods 维护的 "Hypertext Transfer Protocol (HTTP) Method Registry" 注册方法名称.

HTTP 方法注册必须包含以下字段:

  • 方法名称 (Method Name, 见 Section 9)
  • 安全性 (Safe, "yes" 或 "no", 见 Section 9.2.1)
  • 幂等性 (Idempotent, "yes" 或 "no", 见 Section 9.2.2)
  • 规范文本指针 (Pointer to specification text)

要添加到此命名空间的值需要 IETF Review (见 [RFC8126] 的 Section 4.8).

16.1.2. 新方法的考虑事项

标准化方法是通用的; 也就是说, 它们可能适用于任何资源, 而不只是某个特定媒体类型, 资源种类或应用. 因此, 最好在不特定于单个应用或数据格式的文档中注册新方法, 因为正交技术应有正交规范.

由于消息解析 (Section 6) 需要独立于方法语义 (对 HEAD 的响应除外), 新方法定义不能改变解析算法, 也不能禁止请求消息或响应消息中存在内容. 新方法定义可以通过要求 Content-Length 头字段的值为 "0", 来指定只允许零长度内容.

同样, 新方法不能使用分别允许给 CONNECT 和 OPTIONS 的特殊 host:port 与星号形式的请求目标 (Section 7.1). 目标 URI 需要使用绝对形式的完整 URI, 这意味着要么请求目标需要以绝对形式发送, 要么目标 URI 将以与其他方法相同的方式从请求上下文重构.

新方法定义需要指明它是否安全 (Section 9.2.1), 幂等 (Section 9.2.2), 可缓存 (Section 9.2.3), 请求内容 (如果有) 关联哪些语义, 以及该方法对头字段或状态码语义作出哪些细化. 如果新方法是可缓存的, 其定义应当描述缓存如何以及在什么条件下可以存储响应并用其满足后续请求. 新方法应当描述它是否可以成为条件性的 (Section 13.1), 如果可以, 当条件为 false 时服务器如何响应. 同样, 如果新方法可能使用部分响应语义 (Section 14.2), 也应当记录这一点.

注: 避免定义以 "M-" 开头的方法名, 因为该前缀可能被误解为具有 [RFC2774] 赋予它的语义.

16.2. 状态码可扩展性

16.2.1. 状态码注册表

由 IANA 在 https://www.iana.org/assignments/http-status-codes 维护的 "Hypertext Transfer Protocol (HTTP) Status Code Registry" 注册状态码编号.

注册项必须包含以下字段:

  • 状态码 (Status Code, 3 位数字)
  • 简短描述 (Short Description)
  • 规范文本指针 (Pointer to specification text)

要添加到 HTTP 状态码命名空间的值需要 IETF Review (见 [RFC8126] 的 Section 4.8).

16.2.2. 新状态码的考虑事项

当需要表达当前状态码未定义的响应语义时, 可以注册新的状态码. 状态码是通用的; 它们可能适用于任何资源, 而不只是某个特定媒体类型, 资源种类或 HTTP 应用. 因此, 最好在不特定于单个应用的文档中注册新状态码.

新状态码必须落入 Section 15 中定义的某个类别. 为了允许现有解析器处理响应消息, 新状态码不能禁止内容, 但可以强制要求零长度内容.

尚未广泛部署的新状态码提案应当避免在明确共识认为其会被注册之前为代码分配具体编号; 相反, 早期草案可以使用 "4NN" 或 "3N0" .. "3N9" 这样的记法, 在不提前消耗编号的情况下指示所提议状态码的类别.

新状态码的定义应当说明会导致包含该状态码的响应出现的请求条件 (例如请求头字段和/或方法的组合), 以及对响应头字段的任何依赖 (例如哪些字段是必需的, 哪些字段可以修改语义, 以及与新状态码一起使用时哪些字段语义被进一步细化).

默认情况下, 状态码只适用于它所在响应对应的请求. 如果状态码适用于更大的适用范围, 例如对相关资源的所有请求或对某个服务器的所有请求, 则必须明确指定. 这样做时, 应当说明并非所有客户端都能被期望一致地应用更大的范围, 因为它们可能不理解新状态码.

新 final 状态码的定义应当指定它是否启发式可缓存. 注意, 任何带 final 状态码的响应如果具有显式新鲜度信息, 都可以被缓存. 定义为启发式可缓存的状态码允许在没有显式新鲜度信息的情况下被缓存. 同样, 如果使用 must-understand 缓存指令, 状态码定义可以对缓存行为施加约束. 详见 [CACHING].

最后, 新状态码的定义应当指明内容是否与已标识资源存在任何隐含关联 (Section 6.4.2).

16.3. 字段可扩展性

HTTP 使用最广泛的扩展点是定义新的头字段和尾字段.

可以定义新字段, 使其在被接收方理解时覆盖或增强对先前定义字段的解释, 定义请求求值的前置条件, 或细化响应含义.

不过, 定义一个字段并不能保证它会被部署或被接收方识别. 大多数字段的设计预期是接收方可以安全忽略 (但向下游转发) 任何未识别字段. 在其他情况下, 发送方理解某个给定字段的能力可能由其先前通信表明, 例如它在先前消息中发送的协议版本或字段, 或它使用了特定媒体类型. 同样, 如果在引入字段时定义了这种检查方式, 也可以通过 OPTIONS 请求或与已定义的 well-known URI [RFC8615] 交互来直接检查支持情况.

16.3.1. 字段名注册表

"Hypertext Transfer Protocol (HTTP) Field Name Registry" 定义 HTTP 字段名的命名空间.

任何一方都可以请求注册 HTTP 字段. 创建新 HTTP 字段时需要考虑的事项见 Section 16.3.2.

"Hypertext Transfer Protocol (HTTP) Field Name Registry" 位于 https://www.iana.org/assignments/http-fields/. 注册请求可以按照该处说明提交, 或发送电子邮件到 "[email protected]" 邮件列表.

字段名根据 designated expert (由 IESG 或其委托方任命) 的建议注册. 状态为 'permanent' 的字段需要 Specification Required ([RFC8126], Section 4.6).

注册请求由以下信息组成:

Field name: 请求的字段名. 它必须符合 Section 5.1 中定义的 field-name 语法, 并且应该限制为仅包含字母, 数字和连字符 ('-'), 且第一个字符为字母.

Status: "permanent", "provisional", "deprecated" 或 "obsoleted".

Specification document(s): 指向规定该字段的文档的引用, 最好包括可用于获取该文档副本的 URI. 也可以包括相关章节的指示, 但不是必需的. 对于 provisional 注册, 该项是可选的, 但鼓励提供.

以及可选的:

Comments: 附加信息, 例如关于保留条目的信息.

专家可以在与社区协商后, 定义要在注册表中收集的附加字段.

标准定义的名称状态为 "permanent". 如果专家发现其他名称正在使用, 并且在与社区协商后认为它们合适, 其他名称也可以注册为 permanent. 其他名称应该注册为 "provisional".

如果专家在与社区协商后发现 provisional 条目未被使用, 可以移除它们. 专家可以随时将 provisional 条目的状态改为 permanent.

注意, 如果专家确定某个未注册名称已被广泛部署且不太可能及时注册, 则第三方 (包括专家) 可以注册该名称.

16.3.2. 新字段的注意事项

HTTP header 和 trailer 字段是协议广泛使用的扩展点. 虽然它们可以以临时方式使用, 但打算广泛使用的字段需要仔细记录, 以确保互操作性.

特别是, 建议定义新字段的规范作者考虑并在适当时记录以下方面:

  • 字段可以在什么条件下使用; 例如仅在响应或请求中, 在所有消息中, 仅在对特定请求方法的响应中等.

  • 字段语义是否会由其上下文进一步细化, 例如与某些请求方法或状态码一起使用时.

  • 所传达信息的适用范围. 默认情况下, 字段只适用于与其关联的消息, 但某些响应字段被设计为适用于资源的所有表示, 资源本身, 或甚至更广的范围. 扩展响应字段范围的规范需要仔细考虑内容协商, 适用时间段以及 (在某些情况下) 多租户服务器部署等问题.

  • 中间方在什么条件下被允许插入, 删除或修改字段值.

  • 字段是否允许出现在 trailers 中; 默认不允许 (见 Section 6.5.1).

  • 是否适合甚至需要在 Connection 头字段中列出该字段名 (即该字段是否为 hop-by-hop; 见 Section 7.6.1).

  • 字段是否引入任何附加安全注意事项, 例如披露隐私相关数据.

如果默认行为不适合, 请求头字段还有需要记录的额外注意事项:

  • 是否适合在 Vary 响应头字段中列出该字段名 (例如, 当源服务器的内容选择算法使用该请求头字段时; 见 Section 12.5.5).

  • 字段是否意图在 PUT 请求中收到时由服务器存储 (见 Section 9.3.4).

  • 当出于安全考虑自动重定向请求时, 该字段是否应当被移除 (见 Section 15.4).

16.3.2.1. 新字段名的注意事项

建议定义新字段的规范作者选择简短但具有描述性的字段名. 简短名称可以避免不必要的数据传输; 描述性名称可以避免混淆, 并避免对可能有更广泛用途的名称进行 "squatting".

为此, 鼓励用途有限的字段 (例如仅限于单个应用或用例的头字段) 使用包含该用途 (或其缩写) 的名称作为前缀; 例如, 如果 Foo Application 需要 Description 字段, 它可以使用 "Foo-Desc"; "Description" 过于通用, 而 "Foo-Description" 则不必要地长.

虽然 field-name 语法被定义为允许任何 token 字符, 但实践中某些实现会限制它们接受的 field-name 字符. 为实现互操作, 新字段名应该将自身约束为字母数字字符, "-" 和 ".", 并且应该以字母开头. 例如, 下划线 ("_") 字符在通过非 HTTP 网关接口传递时可能带来问题 (见 Section 17.10).

字段名不应以 "X-" 为前缀; 更多信息见 [BCP178].

HTTP 字段名中有时使用其他前缀; 例如, "Accept-" 用于许多内容协商头, 而 "Content-" 的使用见 Section 6.4. 这些前缀只是帮助识别字段用途, 不会触发自动处理.

16.3.2.2. 新字段值的注意事项

定义新的 HTTP 字段时, 一项主要任务是规定字段值语法: 发送方应该生成什么, 以及接收方应该如何从收到的内容推断语义.

鼓励作者使用本规范中的 ABNF 规则或 [RFC8941] 中的规则来定义新字段值的语法, 但并非强制要求.

建议作者仔细考虑多个字段行的组合会产生什么影响 (见 Section 5.3). 由于发送方可能错误地发送多个值, 并且中间方和 HTTP 库都可能自动执行组合, 这一点适用于所有字段值, 即使预期只有单个值.

因此, 建议作者对包含逗号的值进行分隔或编码 (例如使用 Section 5.6.4 的 quoted-string 规则, [RFC8941] 的 String 数据类型, 或字段专用编码). 这可以确保字段数据中的逗号不会与分隔列表值的逗号混淆.

例如, Content-Type 字段值只允许逗号出现在 quoted string 内部, 即使存在多个值也可以可靠解析. Location 字段值提供了一个不应仿效的反例: 由于 URI 可以包含逗号, 因此无法可靠地区分包含逗号的单个值和两个值.

另外, 建议具有 singleton 值的字段 (见 Section 5.5) 的作者记录如何处理包含多个成员的消息 (合理的默认做法是忽略该字段, 但这并不总是正确选择).

16.4. 认证方案可扩展性

16.4.1. 认证方案注册表

"Hypertext Transfer Protocol (HTTP) Authentication Scheme Registry" 定义 challenge 和 credentials 中认证方案的命名空间. 该注册表维护于 https://www.iana.org/assignments/http-authschemes.

注册项必须包括以下字段:

  • Authentication Scheme Name
  • 指向规范文本的指针
  • Notes (可选)

要添加到此命名空间的值需要 IETF Review (见 [RFC8126], Section 4.8).

16.4.2. 新认证方案的注意事项

HTTP Authentication 框架的某些方面会约束新认证方案的工作方式:

  • HTTP 认证被假定为无状态: 认证请求所需的所有信息都必须在请求中提供, 而不是依赖服务器记住先前请求. 基于或绑定到底层连接的认证超出本规范范围, 并且本质上存在缺陷, 除非采取步骤确保该连接不能被已认证用户以外的任何一方使用 (见 Section 3.3).

  • 认证参数 "realm" 保留用于按 Section 11.5 所述定义保护空间. 新方案禁止以不兼容该定义的方式使用它.

  • "token68" 表示法是为了与现有认证方案兼容而引入的, 每个 challenge 或 credential 中只能使用一次. 因此, 新方案应当改用 auth-param 语法, 否则未来扩展将不可能实现.

  • challenge 和 credentials 的解析由本规范定义, 不能由新的认证方案修改. 使用 auth-param 语法时, 所有参数都应当同时支持 token 和 quoted-string 语法, 并且语法约束应当定义在解析后的字段值上 (即 quoted-string 处理之后). 这是必要的, 以便接收方可以使用适用于所有认证方案的通用解析器.

    Note: "realm" 参数的值语法被限制为 quoted-string 是一个糟糕的设计选择, 新参数不应重复这种做法.

  • 新方案的定义应当规定如何处理未知扩展参数. 一般来说, "must-ignore" 规则优于 "must-understand" 规则, 因为否则在存在旧接收方时很难引入新参数. 此外, 描述定义新参数的策略也是好的做法 (例如 "update the specification" 或 "use this registry").

  • 认证方案需要记录它们是否可用于源服务器认证 (即使用 WWW-Authenticate) 和/或代理认证 (即使用 Proxy-Authenticate).

  • Authorization 头字段中携带的凭据特定于用户代理, 因此, 在其出现的请求范围内, 对 HTTP 缓存具有与 "private" 缓存响应指令 ([CACHING], Section 5.2.2.7) 相同的效果.

    因此, 选择不在 Authorization 头字段中携带凭据的新认证方案 (例如使用新定义的头字段) 需要通过强制使用缓存响应指令 (例如 "private") 来明确禁止缓存.

  • 使用 Authentication-Info, Proxy-Authentication-Info 或任何其他认证相关响应头字段的方案, 需要考虑并记录相关安全注意事项 (见 Section 17.16.4).

16.5. 范围单位可扩展性

16.5.1. 范围单位注册表

"HTTP Range Unit Registry" 定义范围单位名称的命名空间, 并引用其对应规范. 该注册表维护于 https://www.iana.org/assignments/http-parameters.

HTTP Range Unit 的注册项必须包括以下字段:

  • Name
  • Description
  • 指向规范文本的指针

要添加到此命名空间的值需要 IETF Review (见 [RFC8126], Section 4.8).

16.5.2. 新范围单位的注意事项

其他范围单位, 例如 pages, sections, records, rows 或 time 这类格式特定边界, 可能可在 HTTP 中用于应用特定目的, 但实践中并不常用. 替代范围单位的实现者应当考虑它们如何与内容编码和通用中间方配合工作.

16.6. 内容编码可扩展性

16.6.1. 内容编码注册表

由 IANA 在 https://www.iana.org/assignments/http-parameters/ 维护的 "HTTP Content Coding Registry" 注册 content-coding 名称.

Content coding 注册项必须包括以下字段:

  • Name
  • Description
  • 指向规范文本的指针

内容编码名称禁止与传输编码名称重叠 (按照位于 https://www.iana.org/assignments/http-parameters/ 的 "HTTP Transfer Coding Registry"), 除非编码转换完全相同 (Section 8.4.1 中定义的压缩编码就是这种情况).

要添加到此命名空间的值需要 IETF Review (见 [RFC8126] 的 Section 4.8), 并且必须符合 Section 8.4.1 中定义的内容编码目的.

16.6.2. 新内容编码的注意事项

新的内容编码应当尽可能自描述, 可选参数应能在编码格式本身内部发现, 而不是依赖可能在传输期间丢失的外部元数据.

16.7. Upgrade Token 注册表

"Hypertext Transfer Protocol (HTTP) Upgrade Token Registry" 定义用于在 Upgrade 头字段中标识协议的 protocol-name token 命名空间. 该注册表维护于 https://www.iana.org/assignments/http-upgrade-tokens.

每个已注册的协议名称都与联系信息以及一组可选规范相关联, 这些规范详细说明连接升级后将如何处理.

注册按 "First Come First Served" 方式进行 (见 [RFC8126] 的 Section 4.4), 并受以下规则约束:

  1. protocol-name token 一旦注册, 将永久保持注册.
  2. protocol-name token 大小写不敏感, 并以发送方生成时首选的大小写形式注册.
  3. 注册项必须指明对该注册负责的一方.
  4. 注册项必须指明联系点.
  5. 注册项可以指明与该 token 相关联的一组规范. 这些规范不必公开可用.
  6. 注册项应该指明注册时与该 token 相关联的一组预期 "protocol-version" token.
  7. 责任方可以随时更改注册项. IANA 会保留所有此类变更的记录, 并按请求提供.
  8. IESG 可以重新分配某个协议 token 的责任. 这通常只会在无法联系到责任方时使用.