| 互联网工程任务组 (IETF) | M. Nottingham |
| 请求评注: 9651 | Cloudflare |
| 废弃: 8941 | P-H. Kamp |
| 类别: 标准轨道 | The Varnish Cache Project |
| ISSN: 2070-1721 | 2024年9月 |
HTTP 的结构化字段值
摘要
本文件描述了一组数据类型和相关算法,旨在使定义和处理 HTTP 报头与尾部字段(称为“结构化字段”、“结构化报头”或“结构化尾部”)更容易且更安全。供新 HTTP 字段的规范使用。
本文件废弃 RFC 8941。
本备忘录的状态
这是一个互联网标准路线文件。
本文件由互联网工程任务组 (IETF) 制作。它代表了 IETF 社区的共识。它已接受公开审查并已被互联网工程指导小组 (IESG) 批准发表。有关互联网标准的更多信息,请参阅 RFC 7841 第 2 节。
有关本文件当前状态、任何勘误表以及如何对其提供反馈的信息,可在 https://www.rfc-editor.org/info/rfc9651 获得。
Copyright Notice
Copyright (c) 2024 IETF Trust and the persons identified as the document authors. All rights reserved.
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.
1. 引言
为新的 HTTP 报头(和尾部)字段指定语法是一项繁重的工作;即使有 第 16.3.2 节 的指导,也存在许多决定和陷阱,可能让拟定 HTTP 字段的作者犯错。
一旦字段被定义,通常需要编写定制的解析器和序列化器,因为每个字段值对看似通用的语法有略微不同的处理方式。
本文件引入了一组用于在新 HTTP 字段值定义中使用的通用数据结构,以解决这些问题。具体来说,它为这些结构定义了一个通用的抽象模型,以及用于在 HTTP [HTTP] 报头和尾部字段中表达该模型的具体序列化方式。
如果某个 HTTP 字段被定义为“结构化报头”或“结构化尾部”(如果字段既可用于报头又可用于尾部,则称为“结构化字段”),则该字段使用本规范中定义的类型来定义其语法和基本处理规则,从而简化规范作者的定义工作和实现方的处理工作。
此外,未来的 HTTP 版本可以为这些结构的抽象模型定义替代序列化方法,允许使用该模型的字段更高效地传输而无需重新定义。
请注意,本文件的目标不是重新定义现有 HTTP 字段的语法;此处描述的机制仅打算用于明确选择采用它们的字段。
第 2 节 描述了如何指定一个结构化字段。
第 3 节 定义了可在结构化字段中使用的若干抽象数据类型。
这些抽象类型可以使用 第 4 节 中描述的算法序列化为 HTTP 字段值或从中解析。
1.1. 有意的严格处理
本规范有意使用逐步算法定义严格的解析和序列化行为;唯一定义的错误处理是完全失败并终止整个操作。
这样设计是为了鼓励忠实实现和良好互操作性。因此,一个试图通过更容错来“帮忙”的实现会使互操作性变差,因为这会对其他实现施加压力,促使它们也实现类似但可能微妙不同的变通方式。
换句话说,严格处理是本规范的一个有意特性;它允许尽早发现并由生产者修正不合规的输入,从而避免可能出现的互操作性和安全问题。
请注意,由于这种严格性,如果字段由多个方追加(例如中间件或发送方的不同组件),某一方的值中的错误很可能导致整个字段值解析失败。
1.2. 符号约定
本文档中关键字 “MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、“NOT RECOMMENDED”、“MAY” 和 “OPTIONAL” 按照 BCP 14 的定义进行解释,仅当它们全部大写时才适用,详见 [RFC2119] 和 [RFC8174]。
本文档使用来自 [RFC5234] 的 VCHAR、SP、DIGIT、ALPHA 和 DQUOTE 规则来指定字符和/或它们对应的 ASCII 字节,具体取决于上下文。它还使用来自 [HTTP] 的 tchar 和 OWS 规则来达到相同目的。
本文档使用算法来指定解析和序列化行为。从 HTTP 字段解析时,实现方 MUST 的行为应与遵循这些算法不可区分。
对于序列化到 HTTP 字段,算法定义了推荐的生成方式。实现方 MAY 在不影响第 4.2 节 中描述的解析算法正确处理输出的前提下,偏离指定行为。
2. 定义新的结构化字段
要将某个 HTTP 字段指定为结构化字段,其作者需要:
-
在规范中规范性地引用本规范。字段的接收者和生成者需要知道本文件的要求正在生效。
-
指出该字段是结构化报头(即仅可用于报头部分——常见情况)、结构化尾部(仅用于尾部)还是结构化字段(两者皆可)。
-
定义字段值的语义。
-
指定对字段值的任何额外约束,以及在这些约束被违反时的后果。
通常,这意味着字段定义将指定顶层类型——列表、字典或项——然后定义其允许的类型及对它们的约束。例如,一个被定义为列表的报头其成员可能全为整数,或是混合类型;被定义为项的报头可能只允许字符串,并且额外要求只以字母 “Q” 开头,或全部为小写。同样,只有在字段定义明确允许的情况下,内层列表 (第 3.1.1 节) 才是有效的。
使用显示字符串类型的字段建议仔细指定其允许的 Unicode 代码点;例如,指定使用来自 [PRECIS] 的某个配置文件。
字段定义只能对整个字段值使用本规范,而不能只用于字段值的一部分。
规范可以在适当时将字段名称为“结构化报头名”、“结构化尾部名”或“结构化字段名”。同样,也可以根据需要将其字段值称为“结构化报头值”、“结构化尾部值”或“结构化字段值”。
本规范定义了实现方应支持的各种结构的最小长度或数量。它在大多数情况下不指定最大大小,但作者应注意 HTTP 实现确实对单个字段的大小、字段的总数和/或整个报头或尾部部分的大小施加各种限制。
2.1. 示例
一个虚构的 Foo-Example 报头字段可能被指定为:
42. Foo-Example Header Field
Foo-Example HTTP 报头字段传达有关消息中有多少 Foo 的信息。
Foo-Example 是一个项(Item)结构化报头字段 [RFC9651]。其值MUST为整数([RFC9651] 的第 3.3.1 节)。
其值表示消息中 Foo 的数量,且其值MUST在 0 到 10(包含)之间;其他值MUST导致整个报头字段被忽略。
定义了以下参数:
- 一个键为 "foourl" 的参数,其值为字符串([RFC9651] 的第 3.3.3 节),用于传达该消息的 Foo URL。见下文的处理要求。
"foourl" 包含一个 URI-引用([RFC3986] 的第 4.1 节)。如果其值不是有效的 URI-引用,则整个报头字段MUST被忽略。如果其值是相对引用([RFC3986] 的第 4.2 节),则在使用之前MUST进行解析([RFC3986] 第 5 节)。
例如:
Foo-Example: 2; foourl="https://foo.example.com/"
2.2. 错误处理
当解析失败时,整个字段将被忽略(参见 第 4.2 节)。字段定义不能覆盖此行为,因为那样会阻止通用软件的处理;它们只能添加额外约束(例如整数和小数的数值范围、字符串和标记的格式、字典值允许的类型,或列表中项的数量)。
当违反字段特定约束时,整个字段也将被忽略,除非字段定义明确规定了其他处理要求。例如,如果某个报头字段被定义为项且要求为整数,但接收到的是字符串,则应将其忽略,除非该字段的定义另有明确说明。
2.3. 保持可扩展性
结构化字段被设计为可扩展的,因为经验表明,即使未预见到,也常常需要以受控方式修改和添加字段的允许语法和语义。
项和内层列表均允许参数作为扩展机制;这意味着它们的值以后可以扩展以容纳更多信息(如有需要)。为了保持向前兼容,建议字段规范不要将未识别参数的存在定义为错误条件。
字段规范被要求为项、列表或字典之一以保留可扩展性。错误地定义为其他类型(例如整数)的字段被假定为项(即它们允许参数)。
为进一步确保未来可用此类扩展,并鼓励消费者使用完整的解析器实现,字段定义可以指定发送方添加“grease”(润滑)参数。规范可以规定所有符合某一定义模式的参数保留用于此用途,并鼓励在某些请求中发送它们。这有助于阻止接收方编写不考虑参数的解析器。
使用字典的规范还可以通过要求忽略未知键的存在——以及与之关联的值和类型——来允许向前兼容。后续规范可以添加额外的键,并在适当时对它们指定约束。
对结构化字段的扩展可以要求理解该扩展的接收方在其定义的值约束不满足时忽略整个字段值。
2.4. 在扩展中使用新的结构化类型
因为字段定义需要引用某个特定的用于结构化字段的 RFC,所以其值中可用的类型受限于该 RFC 中定义的类型。例如,引用本文件的字段可以使用日期类型(第 3.3.7 节),而引用 RFC 8941 的字段则不能,因为实现该规范的解析器会将其视为无效(因此丢弃)。
此限制也适用于字段的未来扩展;例如,用 RFC 8941 引用定义的字段不能使用日期类型,因为某些接收方仍可能使用基于 RFC 8941 的解析器来处理它。
然而,本文件被设计为与 RFC 8941 向后兼容;实现了此处要求的解析器也可以解析引用 RFC 8941 的有效结构化字段。
将结构化字段实现升级为支持规范的较新修订(例如本文件)带来了这样的可能性:某些根据早期 RFC 无效的字段值在处理后可能变为有效。
例如,某个字段实例可能包含语法上有效的日期(第 3.3.7 节),尽管该字段的定义不支持日期。基于 RFC 8941 的实现会因该实例未在规范中定义而解析失败。如果该实现升级到本规范,解析可能成功。在某些情况下,所得的日期值会被字段特定逻辑拒绝,但在那些本来会被忽略的字段(例如扩展参数)中,可能不会被检测到,从而该字段随后可能被接受和处理。
3. 结构化数据类型
本节概述了结构化字段使用的抽象类型,并简要描述以及示例说明每种类型如何序列化为文本形式的 HTTP 字段。第 4 节 指定了如何从文本 HTTP 字段解析以及序列化到文本 HTTP 字段的详细算法。
概括如下:
-
HTTP 字段可以被定义为三种顶层类型之一:列表(Lists)、字典(Dictionaries)和项(Items)。
-
列表和字典是容器;它们的成员可以是项(Items)或内层列表(Inner Lists,内层列表本身是项的数组)。
-
项和内层列表都可以带有参数(Parameters)。
3.1. 列表
空列表通过完全不序列化该字段来表示。这意味着被定义为列表的字段具有默认的空值。
当序列化为文本 HTTP 字段时,每个成员由逗号和可选空白分隔。例如,一个其值被定义为 Token 列表的字段可能如下所示:
Example-List: sugar, tea, rum
注意,根据 [HTTP] 第 5.3 节,列表可以将其成员分布在同一报头或尾部节的多行上;例如,下列形式等价:
Example-List: sugar, tea, rum
以及
Example-List: sugar, tea Example-List: rum
但是,列表的单个成员不能安全地拆分到多行;详情见 第 4.2 节。
解析器MUST 支持包含至少 1024 个成员的列表。字段规范可以根据需要约束各个列表成员的类型和基数。
3.1.1. 内层列表
当在文本 HTTP 字段中序列化时,内层列表用圆括号表示,内部的值由一个或多个空格分隔。一个其值被定义为字符串内层列表的字段可能如下所示:
Example-List: ("foo" "bar"), ("baz"), ("bat" "one"), ()
注意此示例中的最后一个成员是一个空的内层列表。
一个其值被定义为在两个层级上都带参数的内层列表的报头字段可能如下:
Example-List: ("foo"; a=1;b=2);lvl=5, ("bar" "baz");lvl=1
解析器MUST 支持包含至少 256 个成员的内层列表。字段规范可以根据需要约束内层列表单个成员的类型和基数。
3.1.2. 参数
实现方MUST 提供按索引和按键访问参数的能力。规范MAY 使用任一访问方式。
注意参数是有序的,且参数键不能包含大写字母。
当在文本 HTTP 字段中序列化时,参数与其所属的项或内层列表以及其他参数之间用分号分隔。例如:
Example-List: abc;a=1;b=2; cde_456, (ghi;jk=4 l);q="9";r=w
当参数的值是布尔值(参见 第 3.3.6 节)且为 true 时,序列化时MUST 省略该值。例如,下例中的 "a" 参数为 true,而 "b" 参数为 false:
Example-Integer: 1; a; b=?0
注意该要求仅适用于序列化;解析器仍然必须在参数中出现 true 值时正确处理该值。
解析器MUST 支持项或内层列表上至少 256 个参数,并支持参数键长度至少为 64 个字符。字段规范可以按需要约束单个参数的顺序以及它们值的类型。
3.2. 字典
实现方MUST 提供按索引和按键访问字典的能力。规范MAY 使用任一访问方式来访问成员。
与列表类似,空字典通过省略整个字段来表示。这意味着被定义为字典的字段具有默认的空值。
通常,字段规范通过按键指定单个成员允许的类型以及它们是否为必需或可选,从而定义字典的语义。接收方MUST 忽略键未定义或未知的成员,除非字段规范明确不允许它们存在。
当序列化为文本 HTTP 字段时,成员按序序列化并由逗号与可选空白分隔。成员键不能包含大写字符。键和值之间用 "=" 分隔(无空白)。例如:
Example-Dict: en="Applepie", da=:w4ZibGV0w6ZydGU=:
注意在此示例中,末尾的 "=" 是由于包含了字节序列;参见 第 3.3.5 节。
当成员的值为布尔 true(参见 第 3.3.6 节)时,序列化时MUST 省略该值。例如,这里 "b" 和 "c" 都为 true:
Example-Dict: a=?0, b, c; foo=bar
注意该要求仅适用于序列化;解析器仍然必须在字典值中出现 true 布尔值时正确处理该值。
一个其成员值为 Token 内层列表的字典:
Example-Dict: rating=1.5, feelings=(joy sadness)
一个混合了项与内层列表(有些带参数)的字典:
Example-Dict: a=(1 2), b=3, c=4;aa=bb, d=(5 6);valid
注意字典的成员也可以分布在同一报头或尾部节的多行上;例如,下列形式等价:
Example-Dict: foo=1, bar=2
以及
Example-Dict: foo=1 Example-Dict: bar=2
但是,字典的单个成员不能安全地拆分到多行;详情见 第 4.2 节。
解析器MUST 支持包含至少 1024 个键/值对的字典以及键长度至少为 64 个字符。字段规范可以按需要约束字典中单个成员的顺序以及它们值的类型。
3.3. 项
项可以是整数(第 3.3.1 节)、小数(第 3.3.2 节)、字符串(第 3.3.3 节)、标记(第 3.3.4 节)、字节序列(第 3.3.5 节)、布尔值(第 3.3.6 节)或日期(第 3.3.7 节)。项可以带有相关联的参数(第 3.1.2 节)。
例如,一个被定义为整数项的报头字段可能如下:
Example-Integer: 5
或者带参数:
Example-Integer: 5; foo=bar
3.3.1. 整数
整数的取值范围为 -999,999,999,999,999 到 999,999,999,999,999(含),即最多十五位数字并允许符号,以与 IEEE 754 兼容 [IEEE754]。
例如:
Example-Integer: 42
虽然可以序列化带前导零的整数(例如 "0002", "-01")以及带符号的零("-0"),但这些区分可能不会被实现保留。
注意,本节文字中整数内使用的逗号仅用于可读性;它们在传输格式中并不合法。
3.3.2. 小数
小数由整数部分和小数部分组成。整数部分最多 12 位数字;小数部分最多 3 位数字。
例如,一个其值被定义为小数的报头可能如下:
Example-Decimal: 4.5
虽然可以序列化带前导零的小数(例如 "0002.5", "-01.334"),带尾随零的(例如 "5.230", "-0.40")以及带符号的零(例如 "-0.0"),但这些区分可能不会被实现保留。
注意序列化算法(第 4.1.5 节)会对小数部分精度超过三位的输入进行舍入。如果需要不同的舍入策略,应由字段定义在序列化之前指定。
3.3.3. 字符串
字符串由零个或多个可打印的 ASCII 字符(参见 [RFC0020],即代码点范围 %x20 到 %x7E)组成。注意这排除了制表符、换行、回车等字符。
字符串不直接支持非 ASCII 字符,因为这会引发许多互操作性问题,而且在大多数情况下字段值并不需要它们。
当需要在字段值中传递非 ASCII 内容时,可以指定显示字符串(第 3.3.8 节)。
在文本 HTTP 字段中序列化时,字符串用双引号界定,并使用反斜杠("\")对双引号和反斜杠进行转义。例如:
Example-String: "hello world"
注意字符串仅使用 DQUOTE 作为定界符;单引号不用于定界字符串。此外,只有 DQUOTE 和 "\" 可以被转义;遇到 "\" 之后的其它字符会导致解析失败,MUST 如此处理。
解析器MUST 支持(在任何解码之后)至少 1024 字符的字符串。
3.3.4. 标记
标记是以字母字符或 "*" 开始的短文本单词,后面跟零个或多个 token 字符。token 字符与 [HTTP] 中定义的 "token" ABNF 规则允许的字符相同,并额外允许 ":" 和 "/" 字符。
例如:
Example-Token: foo123/456
解析器MUST 支持至少 512 字符的标记。
注意标记主要为与现有 HTTP 字段的数据模型兼容而定义,可能在某些实现中需要额外处理步骤。因此鼓励新字段使用字符串。
3.3.5. 字节序列
字节序列可以在结构化字段中传输。
Example-ByteSequence: :cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==:
解析器MUST 支持解码后至少 16384 字节的字节序列。
3.3.6. 布尔值
布尔值可以在结构化字段中传递。
在文本 HTTP 字段中序列化时,布尔值以前导字符 "?" 表示,随后用 "1" 表示 true,"0" 表示 false。例如:
Example-Boolean: ?1
3.3.7. 日期
日期值可以在结构化字段中传递。
日期的数据模型类似于整数,表示自 1970-01-01T00:00:00Z 起(不含闰秒)的秒数增量(可能为负)。因此,它们在文本 HTTP 字段中的序列化类似于整数,但以前导 "@" 与之区分。
例如:
Example-Date: @1659578233
解析器MUST 支持其值覆盖公元 1 年至 9999 年所有天数的日期(即从 1970-01-01T00:00:00Z 的增量秒范围为 -62,135,596,800 到 253,402,214,400)。
3.3.8. 显示字符串
显示字符串类似于字符串,由零个或多个字符组成,但它们允许 Unicode 标量值(即除代理项之外的所有 Unicode 代码点),这与字符串不同。
显示字符串旨在用于需要向最终用户显示的值,因此可能需要承载非 ASCII 内容。除非字符串(第 3.3.3 节)或标记(第 3.3.4 节)不足以满足需求,否则NOT RECOMMENDED 使用显示字符串,因为 Unicode 存在规范化等处理问题以及诸如同形异义攻击等安全考虑,使得正确处理更为困难。
注意显示字符串并不表示其所使用的语言;如有必要,可以另行指示语言(例如通过参数)。
在文本 HTTP 字段中,显示字符串的表示方式类似于字符串,但非 ASCII 字符会进行百分号编码;并以前导 "%" 与字符串区分。
例如:
Example-DisplayString: %"This is intended for display to %c3%bcsers."
关于处理显示字符串的额外安全注意事项,请参见 第 6 节。
4. 在 HTTP 中使用结构化字段
本节定义了如何将第 3 节 所定义的抽象类型序列化和解析为文本形式的 HTTP 字段值以及与之兼容的其他编码(例如,在 HTTP/2 [HTTP/2] 中在使用 HPACK [HPACK] 压缩之前)。
4.1. 结构化字段的序列化
给定本规范中定义的结构,返回适用于 HTTP 字段值的 ASCII 字符串。
-
如果该结构是 Dictionary 或 List 并且其值为空(即没有成员),则不要序列化该字段(即省略 field-name 和 field-value)。
-
如果该结构是 List,则令 output_string 为对该结构运行 "Serializing a List"(第 4.1.1 节)的结果。
-
否则,如果该结构是 Dictionary,则令 output_string 为对该结构运行 "Serializing a Dictionary"(第 4.1.2 节)的结果。
-
否则,如果该结构是 Item,则令 output_string 为对该结构运行 "Serializing an Item"(第 4.1.3 节)的结果。
-
否则,序列化失败。
-
返回将 output_string 转换为字节数组后的结果,使用 ASCII 编码([RFC0020])。
4.1.1. 序列化列表
给定一个由 (member_value, parameters) 元组组成的数组 input_list,返回适用于 HTTP 字段值的 ASCII 字符串。
-
令 output 为空字符串。
-
对于 input_list 中的每个 (member_value, parameters):
-
如果 member_value 是数组,则将对 (member_value, parameters) 运行 "Serializing an Inner List"(第 4.1.1.1 节)的结果追加到 output。
-
否则,将对 (member_value, parameters) 运行 "Serializing an Item"(第 4.1.3 节)的结果追加到 output。
-
如果 input_list 中还有更多 member_values:
-
向 output 追加 ","。
-
向 output 追加一个 SP。
-
-
-
返回 output。
4.1.1.1. 序列化内层列表
给定一个由 (member_value, parameters) 元组组成的数组 inner_list,以及 list_parameters,返回适用于 HTTP 字段值的 ASCII 字符串。
-
令 output 为字符串 "("。
-
对于 inner_list 中的每个 (member_value, parameters):
-
将对 (member_value, parameters) 运行 "Serializing an Item"(第 4.1.3 节)的结果追加到 output。
-
如果 inner_list 中还有更多值,则向 output 追加一个 SP。
-
-
向 output 追加 ")"。
-
将对 list_parameters 运行 "Serializing Parameters"(第 4.1.1.2 节)的结果追加到 output。
-
返回 output。
4.1.1.2. 序列化参数
给定一个有序的 Dictionary 作为 input_parameters(每个成员有 param_key 和 param_value),返回适用于 HTTP 字段值的 ASCII 字符串。
-
令 output 为空字符串。
-
对于 input_parameters 中的每个 param_key 及其 param_value:
-
向 output 追加 ";"。
-
将对 param_key 运行 "Serializing a Key"(第 4.1.1.3 节)的结果追加到 output。
-
如果 param_value 不是布尔 true:
-
向 output 追加 "="。
-
将对 param_value 运行 "Serializing a bare Item"(第 4.1.3.1 节)的结果追加到 output。
-
-
-
返回 output。
4.1.1.3. 序列化键
给定一个键作为 input_key,返回适用于 HTTP 字段值的 ASCII 字符串。
-
将 input_key 转换为 ASCII 字符序列;如果转换失败,则序列化失败。
-
如果 input_key 包含不在 lcalpha、DIGIT、"_"、"-"、"." 或 "*" 的字符,则序列化失败。
-
如果 input_key 的第一个字符不是 lcalpha 或 "*",则序列化失败。
-
令 output 为空字符串。
-
将 input_key 追加到 output。
-
返回 output。
4.1.2. 序列化字典
给定一个有序的 Dictionary 作为 input_dictionary(每个成员具有 member_key 和值为 (member_value, parameters) 的元组),返回适用于 HTTP 字段值的 ASCII 字符串。
-
令 output 为空字符串。
-
对于 input_dictionary 中的每个 member_key 及其 (member_value, parameters):
-
将对 member 的 member_key 运行 "Serializing a Key"(第 4.1.1.3 节)的结果追加到 output。
-
如果 member_value 是布尔 true:
-
将对 parameters 运行 "Serializing Parameters"(第 4.1.1.2 节)的结果追加到 output。
-
-
否则:
-
向 output 追加 "="。
-
如果 member_value 是数组,则将对 (member_value, parameters) 运行 "Serializing an Inner List"(第 4.1.1.1 节)的结果追加到 output。
-
否则,将对 (member_value, parameters) 运行 "Serializing an Item"(第 4.1.3 节)的结果追加到 output。
-
-
如果 input_dictionary 中还有更多成员:
-
向 output 追加 ","。
-
向 output 追加一个 SP。
-
-
-
返回 output。
4.1.3. 序列化项
给定一个 Item 作为 bare_item 和参数作为 item_parameters,返回适用于 HTTP 字段值的 ASCII 字符串。
-
令 output 为空字符串。
-
将对 bare_item 运行 "Serializing a Bare Item"(第 4.1.3.1 节)的结果追加到 output。
-
将对 item_parameters 运行 "Serializing Parameters"(第 4.1.1.2 节)的结果追加到 output。
-
返回 output。
4.1.3.1. 序列化裸项
给定一个 Item 作为 input_item,返回适用于 HTTP 字段值的 ASCII 字符串。
-
如果 input_item 是 Integer,返回对 input_item 运行 "Serializing an Integer"(第 4.1.4 节)的结果。
-
如果 input_item 是 Decimal,返回对 input_item 运行 "Serializing a Decimal"(第 4.1.5 节)的结果。
-
如果 input_item 是 String,返回对 input_item 运行 "Serializing a String"(第 4.1.6 节)的结果。
-
如果 input_item 是 Token,返回对 input_item 运行 "Serializing a Token"(第 4.1.7 节)的结果。
-
如果 input_item 是 Byte Sequence,返回对 input_item 运行 "Serializing a Byte Sequence"(第 4.1.8 节)的结果。
-
如果 input_item 是 Boolean,返回对 input_item 运行 "Serializing a Boolean"(第 4.1.9 节)的结果。
-
如果 input_item 是 Date,返回对 input_item 运行 "Serializing a Date"(第 4.1.10 节)的结果。
-
如果 input_item 是 Display String,返回对 input_item 运行 "Serializing a Display String"(第 4.1.11 节)的结果。
-
否则,序列化失败。
4.1.4. 序列化整数
给定一个 Integer 作为 input_integer,返回适用于 HTTP 字段值的 ASCII 字符串。
-
如果 input_integer 不是在 -999,999,999,999,999 到 999,999,999,999,999(含)范围内的整数,则序列化失败。
-
令 output 为空字符串。
-
如果 input_integer 小于(但不等于)0,则向 output 追加 "-"。
-
将 input_integer 的十进制数字表示(仅使用十进制数字)追加到 output。
-
返回 output。
4.1.5. 序列化小数
给定一个小数作为 input_decimal,返回适用于 HTTP 字段值的 ASCII 字符串。
-
如果 input_decimal 不是小数,则序列化失败。
-
如果 input_decimal 在小数点右侧具有超过三位有效数字,则将其四舍五入到三位小数,最后一位按四舍五入到最接近值,若正好中间则取偶数。
-
如果在舍入之后 input_decimal 在小数点左侧具有超过 12 位有效数字,则序列化失败。
-
令 output 为空字符串。
-
如果 input_decimal 小于(但不等于)0,则向 output 追加 "-"。
-
将 input_decimal 的整数部分(以十进制数字表示;若为零则追加 "0")追加到 output。
-
向 output 追加 "."。
-
如果 input_decimal 的小数部分为零,则向 output 追加 "0"。
-
否则,将 input_decimal 小数部分的有效数字(以十进制数字表示)追加到 output。
-
返回 output。
4.1.6. 序列化字符串
给定一个 String 作为 input_string,返回适用于 HTTP 字段值的 ASCII 字符串。
-
将 input_string 转换为 ASCII 字符序列;如果转换失败,则序列化失败。
-
如果 input_string 包含 %x00-1f 或 %x7f-ff 范围内的字符(即不在 VCHAR 或 SP 中),则序列化失败。
-
令 output 为字符串 DQUOTE。
-
对于 input_string 中的每个字符 char:
-
如果 char 为 "\" 或 DQUOTE:
-
向 output 追加 "\"。
-
-
将 char 追加到 output。
-
-
向 output 追加 DQUOTE。
-
返回 output。
4.1.7. 序列化标记
给定一个 Token 作为 input_token,返回适用于 HTTP 字段值的 ASCII 字符串。
-
将 input_token 转换为 ASCII 字符序列;如果转换失败,则序列化失败。
-
如果 input_token 的第一个字符不是 ALPHA 或 "*",或其余部分包含不在 tchar、":" 或 "/" 中的字符,则序列化失败。
-
令 output 为空字符串。
-
将 input_token 追加到 output。
-
返回 output。
4.1.8. 序列化字节序列
给定一个 Byte Sequence 作为 input_bytes,返回适用于 HTTP 字段值的 ASCII 字符串。
-
如果 input_bytes 不是字节序列,则序列化失败。
-
令 output 为空字符串。
-
向 output 追加 ":"。
-
将按 [RFC4648] 第 4 节对 input_bytes 进行 base64 编码的结果追加到 output(并符合下述要求)。
-
向 output 追加 ":"。
-
返回 output。
编码数据必须按 [RFC4648] 第 3.2 节 的要求使用 "=" 进行填充。
同样,根据 [RFC4648] 第 3.5 节 的规定,除非由于实现限制而无法做到,否则编码数据应将填充位设为零,SHOULD 如此处理。
4.1.9. 序列化布尔值
给定一个 Boolean 作为 input_boolean,返回适用于 HTTP 字段值的 ASCII 字符串。
-
如果 input_boolean 不是布尔值,则序列化失败。
-
令 output 为空字符串。
-
向 output 追加 "?"。
-
如果 input_boolean 为 true,则向 output 追加 "1"。
-
如果 input_boolean 为 false,则向 output 追加 "0"。
-
返回 output。
4.1.10. 序列化日期
给定一个 Date 作为 input_date,返回适用于 HTTP 字段值的 ASCII 字符串。
-
令 output 为 "@"。
-
将对 input_date 运行 "Serializing an Integer"(第 4.1.4 节)的结果追加到 output。
-
返回 output。
4.1.11. 序列化显示字符串
给定一个 Unicode 代码点序列作为 input_sequence,返回适用于 HTTP 字段值的 ASCII 字符串。
-
如果 input_sequence 不是 Unicode 代码点序列,则序列化失败。
-
令 byte_array 为对 input_sequence 应用 UTF-8 编码(参见 [UTF8] 第 3 节)的结果;如果编码失败,则序列化失败。
-
令 encoded_string 为包含 "%" 后接 DQUOTE 的字符串。
-
对于 byte_array 中的每个字节:
-
如果字节是 %x25 ("%")、%x22 (DQUOTE),或属于 %x00-1f 或 %x7f-ff 范围:
-
向 encoded_string 追加 "%"。
-
令 encoded_byte 为对字节进行 base16 编码(参见 [RFC4648] 第 8 节)的结果,并将任何字母字符转换为小写。
-
将 encoded_byte 追加到 encoded_string。
-
-
否则,将字节解码为 ASCII 字符并将结果追加到 encoded_string。
-
-
向 encoded_string 追加 DQUOTE。
-
返回 encoded_string。
注意,[UTF8] 禁止对 U+D800 到 U+DFFF(代理项)之间的代码点进行编码;如果它们出现在 input_sequence 中,则序列化将失败。
4.2. 解析结构化字段
当接收方实现解析已知为结构化字段的 HTTP 字段时,必须谨慎,因为存在许多边缘情况可能导致互操作性甚至安全问题。本节指定了执行该操作的算法。
给定表示所选字段的 field-value 的字节数组 input_bytes(如果该字段不存在则为空)和 field_type("dictionary"、"list" 或 "item" 之一),返回解析后的字段值。
-
将 input_bytes 转换为 ASCII 字符串 input_string;如果转换失败,解析失败。
-
丢弃 input_string 的任何前导 SP 字符。
-
如果 field_type 为 "list",令 output 为对 input_string 运行 "Parsing a List"(第 4.2.1 节)的结果。
-
如果 field_type 为 "dictionary",令 output 为对 input_string 运行 "Parsing a Dictionary"(第 4.2.2 节)的结果。
-
如果 field_type 为 "item",令 output 为对 input_string 运行 "Parsing an Item"(第 4.2.3 节)的结果。
-
丢弃 input_string 的任何前导 SP 字符。
-
如果 input_string 非空,则解析失败。
-
否则,返回 output。
在生成 input_bytes 时,解析器MUST 将同一节(头部或尾部)中所有与字段名大小写不敏感匹配的字段行合并为一个以逗号分隔的 field-value(参见 [HTTP] 第 5.2 节);这可确保整个字段值被正确处理。
对于 Lists 和 Dictionaries,这会在不将顶层结构的单个成员拆分到多个字段实例的情况下正确地连接该字段的所有行。两种类型的解析算法都允许制表符字符,因为某些实现可能使用制表符来合并字段行。
跨多个字段行拆分的 Strings 将产生不可预测的结果,因为一个或多个逗号(及可选空白)将成为解析器输出字符串的一部分。由于连接可能由上游中间件执行,结果不受序列化器或解析器的控制,即使它们都由同一方控制也一样。
Tokens、Integers、Decimals 和 Byte Sequences 不能跨多个字段行拆分,因为插入的逗号将导致解析失败。
当某条字段行无法作为该字段解析时,解析器MAY 在处理分布在多行的字段值时失败。例如,处理被定义为 sf-string 的 Example-String 字段的解析器在处理下列字段节时被允许失败:
Example-String: "foo Example-String: bar"
如果解析失败,则整个字段值MUST 被忽略(即视为该节中不存在该字段),或者整个 HTTP 消息MUST 被视为格式错误。这种严格性是有意为之,以提高互操作性和安全性,使用结构化字段的字段规范不得放宽此要求。
注意,该要求不适用于未解析该字段的实现;例如,中间件不要求在转发消息之前剥离解析失败的字段。
4.2.1. 解析列表
给定一个 ASCII 字符串作为 input_string,返回由 (item_or_inner_list, parameters) 元组组成的数组。input_string 在解析后会被修改以移除已解析的值。
-
令 members 为一个空数组。
-
当 input_string 非空时:
-
将对 input_string 运行 "Parsing an Item or Inner List"(第 4.2.1.1 节)的结果追加到 members。
-
从 input_string 丢弃任何前导 OWS 字符。
-
如果 input_string 为空,则返回 members。
-
消费 input_string 的第一个字符;如果不是 ",",则解析失败。
-
从 input_string 丢弃任何前导 OWS 字符。
-
如果 input_string 为空,则存在尾随逗号;解析失败。
-
-
未找到任何结构化数据;返回 members(此时为空)。
4.2.1.1. 解析项或内层列表
给定一个 ASCII 字符串作为 input_string,返回元组 (item_or_inner_list, parameters),其中 item_or_inner_list 可以是单个裸项或由 (bare_item, parameters) 元组组成的数组。input_string 在解析后会被修改以移除已解析的值。
-
如果 input_string 的第一个字符是 "(":返回对 input_string 运行 "Parsing an Inner List"(第 4.2.1.2 节)的结果。
-
返回对 input_string 运行 "Parsing an Item"(第 4.2.3 节)的结果。
4.2.1.2. 解析内层列表
给定一个 ASCII 字符串作为 input_string,返回元组 (inner_list, parameters),其中 inner_list 是由 (bare_item, parameters) 元组组成的数组。input_string 在解析后会被修改以移除已解析的值。
-
消费 input_string 的第一个字符;如果不是 "(", 则解析失败。
-
令 inner_list 为空数组。
-
当 input_string 非空时:
-
从 input_string 丢弃任何前导 SP 字符。
-
如果 input_string 的第一个字符是 ")":
-
消费 input_string 的第一个字符。
-
令 parameters 为对 input_string 运行 "Parsing Parameters"(第 4.2.3.2 节)的结果。
-
返回元组 (inner_list, parameters)。
-
-
令 item 为对 input_string 运行 "Parsing an Item"(第 4.2.3 节)的结果。
-
将 item 追加到 inner_list。
-
如果 input_string 的第一个字符既不是 SP 也不是 ")",则解析失败。
-
-
未找到内层列表的结束;解析失败。
4.2.2. 解析字典
给定一个 ASCII 字符串作为 input_string,返回一个有序映射,其值为 (item_or_inner_list, parameters) 元组。input_string 在解析后会被修改以移除已解析的值。
-
令 dictionary 为一个空的、有序的映射。
-
当 input_string 非空时:
-
令 this_key 为对 input_string 运行 "Parsing a Key"(第 4.2.3.3 节)的结果。
-
如果 input_string 的第一个字符是 "=":
-
消费 input_string 的第一个字符。
-
令 member 为对 input_string 运行 "Parsing an Item or Inner List"(第 4.2.1.1 节)的结果。
-
-
否则:
-
令 value 为布尔 true。
-
令 parameters 为对 input_string 运行 "Parsing Parameters"(第 4.2.3.2 节)的结果。
-
令 member 为元组 (value, parameters)。
-
-
如果 dictionary 已包含键 this_key(逐字符比较),则用 member 覆盖其值。
-
否则,将键 this_key 及其值 member 追加到 dictionary。
-
从 input_string 丢弃任何前导 OWS 字符。
-
如果 input_string 为空,则返回 dictionary。
-
消费 input_string 的第一个字符;如果不是 ",",则解析失败。
-
从 input_string 丢弃任何前导 OWS 字符。
-
如果 input_string 为空,则存在尾随逗号;解析失败。
-
-
未找到结构化数据;返回 dictionary(此时为空)。
注意,当遇到重复的字典键时,除最后一次出现之外的所有实例都被忽略。
4.2.3. 解析项
给定一个 ASCII 字符串作为 input_string,返回一个 (bare_item, parameters) 元组。input_string 在解析后会被修改以移除已解析的值。
-
令 bare_item 为对 input_string 运行 "Parsing a Bare Item"(第 4.2.3.1 节)的结果。
-
令 parameters 为对 input_string 运行 "Parsing Parameters"(第 4.2.3.2 节)的结果。
-
返回元组 (bare_item, parameters)。
4.2.3.1. 解析裸项
给定一个 ASCII 字符串作为 input_string,返回一个裸项。input_string 在解析后会被修改以移除已解析的值。
-
如果 input_string 的第一个字符是 "-" 或 DIGIT,则返回对 input_string 运行 "Parsing an Integer or Decimal"(第 4.2.4 节)的结果。
-
如果 input_string 的第一个字符是 DQUOTE,则返回对 input_string 运行 "Parsing a String"(第 4.2.5 节)的结果。
-
如果 input_string 的第一个字符是 ALPHA 或 "*",则返回对 input_string 运行 "Parsing a Token"(第 4.2.6 节)的结果。
-
如果 input_string 的第一个字符是 ":", 则返回对 input_string 运行 "Parsing a Byte Sequence"(第 4.2.7 节)的结果。
-
如果 input_string 的第一个字符是 "?", 则返回对 input_string 运行 "Parsing a Boolean"(第 4.2.8 节)的结果。
-
如果 input_string 的第一个字符是 "@", 则返回对 input_string 运行 "Parsing a Date"(第 4.2.9 节)的结果。
-
如果 input_string 的第一个字符是 "%", 则返回对 input_string 运行 "Parsing a Display String"(第 4.2.10 节)的结果。
-
否则,项类型无法识别;解析失败。
4.2.3.2. 解析参数
给定一个 ASCII 字符串作为 input_string,返回一个值为裸项的有序映射。input_string 在解析后会被修改以移除已解析的值。
-
令 parameters 为一个空的、有序的映射。
-
当 input_string 非空时:
-
如果 input_string 的第一个字符不是 ";",则退出循环。
-
从 input_string 开头消费 ";" 字符。
-
从 input_string 丢弃任何前导 SP 字符。
-
令 param_key 为对 input_string 运行 "Parsing a Key"(第 4.2.3.3 节)的结果。
-
令 param_value 为布尔 true。
-
如果 input_string 的第一个字符是 "=":
-
消费 input_string 开头的 "=" 字符。
-
令 param_value 为对 input_string 运行 "Parsing a Bare Item"(第 4.2.3.1 节)的结果。
-
-
如果 parameters 已包含键 param_key(逐字符比较),则用 param_value 覆盖其值。
-
否则,将键 param_key 及其值 param_value 追加到 parameters。
-
-
返回 parameters。
注意,当遇到重复的参数键时,除最后一次出现之外的所有实例都被忽略。
4.2.3.3. 解析键
给定一个 ASCII 字符串作为 input_string,返回一个键。input_string 在解析后会被修改以移除已解析的值。
-
如果 input_string 的第一个字符不是 lcalpha 或 "*",则解析失败。
-
令 output_string 为空字符串。
-
当 input_string 非空时:
-
如果 input_string 的第一个字符不是 lcalpha、DIGIT、"_"、"-"、"." 或 "*",则返回 output_string。
-
令 char 为消费 input_string 的第一个字符的结果。
-
将 char 追加到 output_string。
-
-
返回 output_string。
4.2.4. 解析整数或小数
给定一个 ASCII 字符串作为 input_string,返回一个 Integer 或 Decimal。input_string 在解析后会被修改以移除已解析的值。
-
令 type 为 "integer"。
-
令 sign 为 1。
-
令 input_number 为空字符串。
-
如果 input_string 的第一个字符是 "-",则消费它并将 sign 设为 -1。
-
如果 input_string 为空,则存在空整数;解析失败。
-
如果 input_string 的第一个字符不是 DIGIT,则解析失败。
-
当 input_string 非空时:
-
令 char 为消费 input_string 的第一个字符的结果。
-
如果 char 是 DIGIT,则将其追加到 input_number。
-
否则,如果 type 为 "integer" 并且 char 是 ".":
-
如果 input_number 包含超过 12 个字符,则解析失败。
-
否则,将 char 追加到 input_number 并将 type 设为 "decimal"。
-
-
否则,将 char 放回 input_string 前端,并退出循环。
-
如果 type 为 "integer" 并且 input_number 包含超过 15 个字符,则解析失败。
-
如果 type 为 "decimal" 并且 input_number 包含超过 16 个字符,则解析失败。
-
-
如果 type 为 "integer":
-
令 output_number 为将 input_number 解析为整数后的 Integer。
-
-
否则:
-
如果 input_number 的最后一个字符是 ".",则解析失败。
-
如果 input_number 中 "." 之后的字符数大于三,则解析失败。
-
令 output_number 为将 input_number 解析为小数后的 Decimal。
-
-
令 output_number 为 output_number 与 sign 的乘积。
-
返回 output_number。
4.2.5. 解析字符串
给定一个 ASCII 字符串作为 input_string,返回一个去引号的 String。input_string 在解析后会被修改以移除已解析的值。
-
令 output_string 为空字符串。
-
如果 input_string 的第一个字符不是 DQUOTE,则解析失败。
-
丢弃 input_string 的第一个字符。
-
当 input_string 非空时:
-
令 char 为消费 input_string 的第一个字符的结果。
-
如果 char 是反斜杠 ("\"):
-
如果此时 input_string 为空,则解析失败。
-
令 next_char 为消费 input_string 的第一个字符的结果。
-
如果 next_char 不是 DQUOTE 或 "\",则解析失败。
-
将 next_char 追加到 output_string。
-
-
否则,如果 char 是 DQUOTE,则返回 output_string。
-
否则,如果 char 属于 %x00-1f 或 %x7f-ff 范围(即不在 VCHAR 或 SP 中),则解析失败。
-
否则,将 char 追加到 output_string。
-
-
到达 input_string 结尾但未找到闭合的 DQUOTE;解析失败。
4.2.6. 解析标记
给定一个 ASCII 字符串作为 input_string,返回一个 Token。input_string 在解析后会被修改以移除已解析的值。
-
如果 input_string 的第一个字符不是 ALPHA 或 "*",则解析失败。
-
令 output_string 为空字符串。
-
当 input_string 非空时:
-
如果 input_string 的第一个字符不在 tchar、":" 或 "/" 中,则返回 output_string。
-
令 char 为消费 input_string 的第一个字符的结果。
-
将 char 追加到 output_string。
-
-
返回 output_string。
4.2.7. 解析字节序列
给定一个 ASCII 字符串作为 input_string,返回一个 Byte Sequence。input_string 在解析后会被修改以移除已解析的值。
-
如果 input_string 的第一个字符不是 ":",则解析失败。
-
丢弃 input_string 的第一个字符。
-
如果在 input_string 结束之前没有 ":" 字符,解析失败。
-
令 b64_content 为消费 input_string 中直到但不包括第一个 ":" 字符的内容的结果。
-
消费 input_string 开头的 ":" 字符。
-
如果 b64_content 包含不在 ALPHA、DIGIT、"+"、"/" 和 "=" 中的字符,解析失败。
-
令 binary_content 为对 b64_content 按 [RFC4648] 进行 base64 解码的结果,必要时合成填充(注意下文关于接收方行为的要求)。如果 base64 解码失败,则解析失败。
-
返回 binary_content。
由于某些 base64 实现不允许拒绝未正确使用 "=" 填充的编码数据(参见 [RFC4648] 第 3.2 节),解析器SHOULD NOT 在缺少 "=" 填充时失败,除非无法配置为不这样做。
由于某些 base64 实现不允许拒绝具有非零填充位的编码数据(参见 [RFC4648] 第 3.5 节),解析器SHOULD NOT 在存在非零填充位时失败,除非无法配置为不这样做。
本规范并未放宽 [RFC4648] 第 3.1 节 和 第 3.3 节 的要求;因此,解析器MUST 在遇到 base64 字母表之外的字符或编码数据中的换行符时失败。
4.2.8. 解析布尔值
给定一个 ASCII 字符串作为 input_string,返回一个 Boolean。input_string 在解析后会被修改以移除已解析的值。
-
如果 input_string 的第一个字符不是 "?",解析失败。
-
丢弃 input_string 的第一个字符。
-
如果 input_string 的第一个字符是 "1",则消费该字符并返回 true。
-
如果 input_string 的第一个字符是 "0",则消费该字符并返回 false。
-
没有匹配的值;解析失败。
4.2.9. 解析日期
给定一个 ASCII 字符串作为 input_string,返回一个 Date。input_string 在解析后会被修改以移除已解析的值。
-
如果 input_string 的第一个字符不是 "@", 则解析失败。
-
丢弃 input_string 的第一个字符。
-
令 output_date 为对 input_string 运行 "Parsing an Integer or Decimal"(第 4.2.4 节)的结果。
-
如果 output_date 是 Decimal,则解析失败。
-
返回 output_date。
4.2.10. 解析显示字符串
给定一个 ASCII 字符串作为 input_string,返回一个 Unicode 代码点序列。input_string 在解析后会被修改以移除已解析的值。
-
如果 input_string 的前两个字符不是 "%" 后接 DQUOTE,则解析失败。
-
丢弃 input_string 的前两个字符。
-
令 byte_array 为空字节数组。
-
当 input_string 非空时:
-
令 char 为消费 input_string 的第一个字符的结果。
-
如果 char 属于 %x00-1f 或 %x7f-ff 范围(即不在 VCHAR 或 SP 中),则解析失败。
-
如果 char 是 "%":
-
令 octet_hex 为从 input_string 消费两个字符的结果;如果不足两个字符,则解析失败。
-
如果 octet_hex 包含不在 %x30-39 或 %x61-66 范围内的字符(即不是 0-9 或小写 a-f),则解析失败。
-
令 octet 为对 octet_hex 进行十六进制解码(参见 [RFC4648] 第 8 节)的结果。
-
将 octet 追加到 byte_array。
-
-
如果 char 是 DQUOTE:
-
令 unicode_sequence 为将 byte_array 解码为 UTF-8 字符串(参见 [UTF8] 第 3 节)的结果;如果解码失败,则解析失败。
-
返回 unicode_sequence。
-
-
否则,如果 char 既不是 "%" 也不是 DQUOTE:
-
令 byte 为对 char 应用 ASCII 编码的结果。
-
将 byte 追加到 byte_array。
-
-
-
到达 input_string 结尾但未找到闭合的 DQUOTE;解析失败。
5. IANA 注意事项
IANA 已向“超文本传输协议(HTTP)字段名称注册表”添加了以下说明:
“Structured Type” 列指示字段的类型(依据 RFC 9651),如果有的话,可能为 “Dictionary”、 “List” 或 “Item”。
注意,以非 ALPHA 或 "*" 字符开头的字段名将无法表示为 Structured Fields Token,因此可能与映射到引用该字段的字段值不兼容。
注册表中新增了一列 “Structured Type”。
表 1(Existing Fields)中列出的每个现有注册表条目的指示 Structured Type 也已被添加。
| Field Name | Structured Type |
|---|---|
| Accept-CH | List |
| Cache-Status | List |
| CDN-Cache-Control | Dictionary |
| Cross-Origin-Embedder-Policy | Item |
| Cross-Origin-Embedder-Policy-Report-Only | Item |
| Cross-Origin-Opener-Policy | Item |
| Cross-Origin-Opener-Policy-Report-Only | Item |
| Origin-Agent-Cluster | Item |
| Priority | Dictionary |
| Proxy-Status | List |
6. 安全注意事项
结构化字段定义的大多数类型并未限制大小;因此,极大的字段可能成为一种攻击向量(例如用于消耗资源)。大多数 HTTP 实现对单个字段以及整体报头或尾部节的大小施加限制以缓解此类攻击。
具有注入新 HTTP 字段能力的各方可能会改变结构化字段的含义。在某些情况下,这会导致解析失败,但并不可能在所有此类情况中可靠地导致失败。
显示字符串(Display String)类型可以在未经清理的情况下传递任意 Unicode 代码点;例如,可能包含未分配的代码点、控制点(包括 NUL)或非字符。因此,使用显示字符串的应用在向终端用户显示之前需要考虑过滤或转义不受信任的内容等策略。参见 [PRECIS] 和 [UNICODE-SECURITY]。
7. 参考文献
7.1. 规范性参考文献
- [HTTP]
- Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., “HTTP Semantics”, STD 97, RFC 9110, DOI 10.17487/RFC9110, June 2022, <https://www.rfc-editor.org/info/rfc9110>.
- [RFC0020]
- Cerf, V., “ASCII format for network interchange”, STD 80, RFC 20, DOI 10.17487/RFC0020, October 1969, <https://www.rfc-editor.org/info/rfc20>.
- [RFC2119]
- Bradner, S., “Key words for use in RFCs to Indicate Requirement Levels”, BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, <https://www.rfc-editor.org/info/rfc2119>.
- [RFC4648]
- Josefsson, S., “The Base16, Base32, and Base64 Data Encodings”, RFC 4648, DOI 10.17487/RFC4648, October 2006, <https://www.rfc-editor.org/info/rfc4648>.
- [RFC8174]
- Leiba, B., “Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words”, BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017, <https://www.rfc-editor.org/info/rfc8174>.
- [UTF8]
- Yergeau, F., “UTF-8, a transformation format of ISO 10646”, STD 63, RFC 3629, DOI 10.17487/RFC3629, November 2003, <https://www.rfc-editor.org/info/rfc3629>.
7.2. 参考资料(信息性)
- [HPACK]
- Peon, R. and H. Ruellan, “HPACK: Header Compression for HTTP/2”, RFC 7541, DOI 10.17487/RFC7541, May 2015, <https://www.rfc-editor.org/info/rfc7541>.
- [HTTP/2]
- Thomson, M., Ed. and C. Benfield, Ed., “HTTP/2”, RFC 9113, DOI 10.17487/RFC9113, June 2022, <https://www.rfc-editor.org/info/rfc9113>.
- [IEEE754]
- IEEE, “IEEE Standard for Floating-Point Arithmetic”, IEEE Std 754-2019, DOI 10.1109/IEEESTD.2019.8766229, ISBN 978-1-5044-5924-2, July 2019, <https://ieeexplore.ieee.org/document/8766229>.
- [PRECIS]
- Saint-Andre, P. and M. Blanchet, “PRECIS Framework: Preparation, Enforcement, and Comparison of Internationalized Strings in Application Protocols”, RFC 8264, DOI 10.17487/RFC8264, October 2017, <https://www.rfc-editor.org/info/rfc8264>.
- [RFC5234]
- Crocker, D., Ed. and P. Overell, “Augmented BNF for Syntax Specifications: ABNF”, STD 68, RFC 5234, DOI 10.17487/RFC5234, January 2008, <https://www.rfc-editor.org/info/rfc5234>.
- [RFC7493]
- Bray, T., Ed., “The I-JSON Message Format”, RFC 7493, DOI 10.17487/RFC7493, March 2015, <https://www.rfc-editor.org/info/rfc7493>.
- [RFC8259]
- Bray, T., Ed., “The JavaScript Object Notation (JSON) Data Interchange Format”, STD 90, RFC 8259, DOI 10.17487/RFC8259, December 2017, <https://www.rfc-editor.org/info/rfc8259>.
- [UNICODE-SECURITY]
- Davis, M. and M. Suignard, “Unicode Security
Considerations”, Unicode Technical Report #36, September 2014, <https://www.unicode.org/reports/tr36/tr36-15.html>.
最新版本见 <https://www.unicode.org/reports/tr36/>。
附录 A. 常见问题
A.1. 为什么不使用 JSON?
早期关于结构化字段的提案基于 JSON [RFC8259]。 然而,为使其适用于 HTTP 字段而对其使用加以约束,要求发送方和接收方实现特定的额外处理。
例如,JSON 在大整数和具有重复成员的对象方面存在规范问题。尽管有避免这些问题的建议(例如 [RFC7493]),但不能完全依赖这些建议。
同样,JSON 字符串默认是 Unicode 字符串,这会带来许多潜在的互操作性问题(例如比较操作)。尽管可以建议实现者在不必要时避免非 ASCII 内容,但这很难强制执行。
另一个例子是 JSON 能够任意深度嵌套内容。由于由此产生的内存占用可能不适合嵌入式或其他受限的服务器部署,因此有必要以某种方式对其进行限制;然而,现有的 JSON 实现没有此类限制,即使指定了限制,也可能会有字段定义需要突破它。
由于 JSON 被广泛采用并已有大量实现,很难在所有实现中施加此类额外约束;某些部署将无法强制执行这些约束,从而损害互操作性。简而言之,如果长得像 JSON,人们会倾向于对字段值使用 JSON 解析器/序列化器。
鉴于结构化字段的主要目标是改善互操作性并简化实现,这些担忧促使采用需要专用解析器和序列化器的格式。
此外,普遍存在的观点认为 JSON 在 HTTP 字段中“看起来不合适”。
附录 B. 实现说明
为实现互操作性,通用实现应尽可能完整并严格遵循算法;参见 第 1.1 节。为此,社区正在维护一个通用测试套件,见 <https://github.com/httpwg/structured-field-tests>。
实现者应注意字典(Dictionaries)和参数(Parameters)是保持顺序的映射。有些字段在这些数据类型的顺序上可能不传达语义,但仍应暴露该顺序,以便需要使用它的应用可以访问。
同样,实现在区分 Token 和 String 的区别时应保持注意。虽然大多数编程语言具有与其他类型很好映射的内置类型,但可能需要创建一个包装的“token”对象或在函数上使用参数以确保这些类型保持分离。
序列化算法的定义并非在每种情况下严格限制于第 3 节 中定义的数据类型。例如,小数(Decimals)被设计为接受更广泛的输入并舍入到允许的值。
实现允许对不同结构的大小设置限制,但须遵守为每种类型定义的最小值。当结构超过实现的限制时,该结构的解析或序列化将失败。
附录 C. ABNF
本节使用扩展巴科斯-诺尔范式(ABNF)表示法 [RFC5234] 来示例结构化字段的期望语法。然而,它不能用于验证其语法,因为它未涵盖所有要求。
本节为非规范性。如果解析算法与 ABNF 有分歧,以所指定的算法为准。
sf-list = list-member *( OWS "," OWS list-member )
list-member = sf-item / inner-list
inner-list = "(" *SP [ sf-item *( 1*SP sf-item ) *SP ] ")"
parameters
parameters = *( ";" *SP parameter )
parameter = param-key [ "=" param-value ]
param-key = key
key = ( lcalpha / "*" )
*( lcalpha / DIGIT / "_" / "-" / "." / "*" )
lcalpha = %x61-7A ; a-z
param-value = bare-item
sf-dictionary = dict-member *( OWS "," OWS dict-member )
dict-member = member-key ( parameters / ( "=" member-value ))
member-key = key
member-value = sf-item / inner-list
sf-item = bare-item parameters
bare-item = sf-integer / sf-decimal / sf-string / sf-token
/ sf-binary / sf-boolean / sf-date / sf-displaystring
sf-integer = ["-"] 1*15DIGIT
sf-decimal = ["-"] 1*12DIGIT "." 1*3DIGIT
sf-string = DQUOTE *( unescaped / "%" / bs-escaped ) DQUOTE
sf-token = ( ALPHA / "*" ) *( tchar / ":" / "/" )
sf-binary = ":" base64 ":"
sf-boolean = "?" ( "0" / "1" )
sf-date = "@" sf-integer
sf-displaystring = "%" DQUOTE *( unescaped / "\" / pct-encoded )
DQUOTE
base64 = *( ALPHA / DIGIT / "+" / "/" ) *"="
unescaped = %x20-21 / %x23-24 / %x26-5B / %x5D-7E
bs-escaped = "\" ( DQUOTE / "\" )
pct-encoded = "%" lc-hexdig lc-hexdig
lc-hexdig = DIGIT / %x61-66 ; 0-9, a-f
附录 D. 相对于 RFC 8941 的更改
本次对“HTTP 的结构化字段值”规范的修订做出了如下更改:
致谢
非常感谢 Matthew Kerwin 在本规范开发过程中提供的详细反馈和审慎考虑。
同时感谢 Ian Clelland、Roy Fielding、Anne van Kesteren、Kazuho Oku、Evert Pot、Julian Reschke、Martin Thomson、Mike West 和 Jeffrey Yasskin 的贡献。