5. Fields (字段)
HTTP使用"字段" (Fields) 以可扩展的名称/值对的形式提供数据, 并具有注册的键命名空间. 字段在消息的头部和尾部部分 (第6节) 中发送和接收.
5.1. Field Names (字段名称)
字段名称 (Field Name) 将相应的字段值标记为具有该名称定义的语义. 例如, Date头字段在第6.6.1节中定义为包含其所在消息的发起时间戳.
field-name = token
字段名称不区分大小写, 应该在"超文本传输协议 (HTTP) 字段名称注册表"中注册; 参见第16.3.1节.
字段的解释在同一HTTP主版本的次版本之间不会改变, 尽管在没有此类字段的情况下接收方的默认行为可能会改变. 除非另有规定, 字段为所有HTTP版本定义. 特别是, 无论HTTP实现是否声称符合HTTP/1.1, 都应该识别Host和Connection字段.
如果新字段的定义语义允许不识别它们的接收方安全地忽略它们, 则可以在不更改协议版本的情况下引入新字段; 参见第16.3节.
代理必须 (MUST) 转发未识别的头字段, 除非字段名称列在Connection头字段 (第7.6.1节) 中, 或者代理被特别配置为阻止或以其他方式转换此类字段. 其他接收方应该 (SHOULD) 忽略未识别的头字段和尾部字段. 遵守这些要求允许扩展HTTP的功能, 而无需更新或删除已部署的中间人.
5.2. Field Lines and Combined Field Value (字段行和组合字段值)
字段部分 (Field Sections) 由任意数量的"字段行" (Field Lines) 组成, 每个字段行都有一个"字段名称" (参见第5.1节) 标识字段, 以及一个"字段行值" (Field Line Value) 传达该字段实例的数据.
当字段名称在一个部分中仅出现一次时, 该字段的组合"字段值" (Field Value) 由相应的字段行值组成. 当字段名称在一个部分中重复时, 其组合字段值由该部分内相应字段行值的列表组成, 按顺序连接, 每个字段行值由逗号分隔.
例如, 此部分:
Example-Field: Foo, Bar
Example-Field: Baz
包含两个字段行, 都具有字段名称 "Example-Field". 第一个字段行的字段行值为 "Foo, Bar", 而第二个字段行值为 "Baz". "Example-Field" 的字段值是列表 "Foo, Bar, Baz".
5.3. Field Order (字段顺序)
接收方可以 (MAY) 将字段部分内具有相同字段名称的多个字段行组合成一个字段行, 而不改变消息的语义, 方法是按顺序将每个后续字段行值附加到初始字段行值, 由逗号 (",") 和可选空格 (OWS, 在第5.6.3节中定义) 分隔. 为了一致性, 使用逗号SP.
因此, 接收具有相同名称的字段行的顺序对字段值的解释很重要; 代理禁止 (MUST NOT) 在转发消息时更改这些字段行值的顺序.
这意味着, 除了下面提到的众所周知的例外, 发送方禁止 (MUST NOT) 在消息中 (无论是在头部还是尾部) 生成具有相同名称的多个字段行, 或在消息中已存在相同名称的字段行时附加字段行, 除非该字段的定义允许将多个字段行值重新组合为逗号分隔的列表 (即, 字段定义的至少一个替代允许逗号分隔的列表, 例如第5.6.1节中定义的 #(values) 的ABNF规则).
注意: 实际上, "Set-Cookie" 头字段 ([COOKIE]) 经常出现在跨多个字段行的响应消息中, 并且不使用列表语法, 违反了上述关于具有相同字段名称的多个字段行的要求. 由于它不能组合成单个字段值, 接收方应该在处理字段时将 "Set-Cookie" 作为特殊情况处理. (有关详细信息, 请参见 [Kri2001] 的附录A.2.3.)
在部分中接收具有不同字段名称的字段行的顺序并不重要. 但是, 最好首先发送包含附加控制数据的头字段, 例如请求上的Host和响应上的Date, 以便实现可以尽早决定何时不处理消息.
服务器禁止 (MUST NOT) 在接收到整个请求头部部分之前将请求应用于目标资源, 因为后面的头字段行可能包括条件、认证凭据或故意误导的重复头字段, 这些可能影响请求处理.
5.4. Field Limits (字段限制)
HTTP不对每个字段行、字段值的长度或整个头部或尾部部分的长度设置预定义的限制, 如第2节所述. 实际中发现对各个长度有各种临时限制, 通常取决于特定字段的语义.
接收到比它希望处理的更大的请求头字段行、字段值或字段集的服务器必须 (MUST) 使用适当的4xx (客户端错误) 状态码进行响应. 忽略此类头字段会增加服务器对请求走私攻击的脆弱性 ([HTTP/1.1] 第11.2节).
如果字段语义使得丢弃的值可以安全地忽略而不改变消息帧或响应语义, 则客户端可以 (MAY) 丢弃或截断接收到的比客户端希望处理的更大的字段行.
5.5. Field Values (字段值)
HTTP字段值 (Field Values) 由字段语法定义的格式中的字符序列组成. 每个字段的语法通常使用ABNF ([RFC5234]) 定义.
field-value = *field-content
field-content = field-vchar
[ 1*( SP / HTAB / field-vchar ) field-vchar ]
field-vchar = VCHAR / obs-text
obs-text = %x80-FF
字段值不包括前导或尾随空格. 当特定版本的HTTP允许此类空格出现在消息中时, 字段解析实现必须 (MUST) 在评估字段值之前排除此类空格.
字段值通常限制在US-ASCII字符 [USASCII] 的范围内. 需要更大字符范围的字段可以使用编码, 例如 [RFC8187] 中定义的编码. 历史上, HTTP允许字段内容具有ISO-8859-1字符集 [ISO-8859-1] 中的文本, 仅通过使用 [RFC2047] 编码支持其他字符集. 新定义字段的规范应该 (SHOULD) 将其值限制为可见的US-ASCII八位字节 (VCHAR)、SP和HTAB. 接收方应该 (SHOULD) 将字段内容中的其他允许八位字节 (即obs-text) 视为不透明数据.
包含CR、LF或NUL字符的字段值是无效的和危险的, 因为实现可能以不同方式解析和解释这些字符; 字段值内接收到CR、LF或NUL的接收方必须 (MUST) 拒绝消息或在进一步处理或转发该消息之前将这些字符中的每一个替换为SP. 包含其他CTL字符的字段值也是无效的; 但是, 当它们出现在安全上下文中 (例如, 不会被任何下游HTTP解析器处理的特定于应用程序的引用字符串) 时, 接收方可以 (MAY) 保留此类字符以实现健壮性.
仅预期单个成员作为字段值的字段称为"单例字段" (Singleton Fields).
允许多个成员作为字段值的字段称为"基于列表的字段" (List-Based Fields). 第5.6.1节的列表运算符扩展用作定义可以包含多个成员的字段值的通用表示法.
因为逗号 (",") 用作成员之间的分隔符, 如果允许它们作为成员内的数据, 则需要小心处理. 这对基于列表的字段和单例字段都是如此, 因为单例字段可能被错误地发送多个成员, 检测此类错误可以提高互操作性. 期望在成员内包含逗号的字段, 例如在HTTP日期或URI引用元素内, 应该在该元素周围定义分隔符, 以将该数据内的任何逗号与潜在的列表分隔符区分开来.
例如, 文本日期和URI (两者都可能包含逗号) 可以安全地携带在基于列表的字段值中, 如下所示:
Example-URIs: "http://example.com/a.html,foo",
"http://without-a-comma.example.com/"
Example-Dates: "Sat, 04 May 1996", "Wed, 14 Sep 2005"
请注意, 双引号分隔符几乎总是与quoted-string产生式 (第5.6.4节) 一起使用; 在双引号内使用不同的语法可能会造成不必要的混淆.
许多字段 (例如在第8.3节中定义的Content-Type) 对参数使用通用语法, 该语法允许参数值使用不带引号 (token) 和带引号 (quoted-string) 的语法 (第5.6.6节). 使用通用语法允许接收方重用现有的解析器组件. 当允许两种形式时, 无论参数值是作为token还是quoted-string接收的, 其含义都应该相同.
注意: 对于定义字段值语法, 本规范使用以字段名称命名的ABNF规则来定义该字段值的允许语法 (在从底层消息语法中提取该值并将多个实例组合成列表之后).
5.6. Common Rules for Defining Field Values (定义字段值的通用规则)
5.6.1. Lists (#rule ABNF Extension) (列表 #规则ABNF扩展)
对 [RFC5234] 的ABNF规则的 #规则扩展用于提高某些基于列表的字段值定义的可读性.
定义了类似于 "*" 的构造 "#", 用于定义以逗号分隔的元素列表. 完整形式是 "<n>#<m>element", 表示至少 <n> 个和最多 <m> 个元素, 每个元素由单个逗号 (",") 和可选空格 (OWS, 在第5.6.3节中定义) 分隔.
5.6.1.1. Sender Requirements (发送方要求)
在使用列表构造的任何产生式中, 发送方禁止 (MUST NOT) 生成空列表元素. 换句话说, 发送方必须生成满足以下语法的列表:
1#element => element *( OWS "," OWS element )
和:
#element => [ 1#element ]
对于 n >= 1 和 m > 1:
<n>#<m>element => element <n-1>*<m-1>( OWS "," OWS element )
附录A显示了在列表构造扩展后发送方的收集ABNF.
5.6.1.2. Recipient Requirements (接收方要求)
空元素不计入存在的元素数量. 接收方必须 (MUST) 解析并忽略合理数量的空列表元素: 足以处理发送方合并值的常见错误, 但不要太多以至于它们可以用作拒绝服务机制. 换句话说, 接收方必须 (MUST) 接受满足以下语法的列表:
#element => [ element ] *( OWS "," OWS [ element ] )
请注意, 由于空列表元素的潜在存在, RFC 5234 ABNF无法强制执行列表元素的基数, 因此所有情况都映射为好像没有指定基数.
例如, 给定这些ABNF产生式:
example-list = 1#example-list-elmt
example-list-elmt = token ; see Section 5.6.2
那么以下是example-list的有效值 (不包括双引号, 双引号仅用于定界):
"foo,bar"
"foo ,bar,"
"foo , ,bar,charlie"
相反, 以下值将是无效的, 因为example-list产生式至少需要一个非空元素:
""
","
", ,"
5.6.2. Tokens (令牌)
令牌 (Tokens) 是不包括空格或分隔符的短文本标识符.
token = 1*tchar
tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
/ "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
/ DIGIT / ALPHA
; any VCHAR, except delimiters
许多HTTP字段值使用由空格或特定定界字符分隔的通用语法组件定义. 分隔符从令牌中不允许的US-ASCII可见字符集中选择 (DQUOTE和 "(),/:;<=>?@[\]{}).
5.6.3. Whitespace (空格)
本规范使用三个规则来表示线性空格的使用: OWS (可选空格, Optional Whitespace)、RWS (必需空格, Required Whitespace) 和BWS ("坏"空格, "Bad" Whitespace).
OWS规则用于可能出现零个或多个线性空格八位字节的地方. 对于可选空格优先提高可读性的协议元素, 发送方应该 (SHOULD) 将可选空格生成为单个SP; 否则, 发送方不应该 (SHOULD NOT) 生成可选空格, 除非在就地消息过滤期间需要覆盖无效或不需要的协议元素.
RWS规则用于至少需要一个线性空格八位字节来分隔字段令牌时. 发送方应该 (SHOULD) 将RWS生成为单个SP.
OWS和RWS具有与单个SP相同的语义. 已知定义为OWS或RWS的任何内容可以 (MAY) 在解释或向下游转发消息之前替换为单个SP.
BWS规则用于语法仅出于历史原因允许可选空格的地方. 发送方禁止 (MUST NOT) 在消息中生成BWS. 接收方必须 (MUST) 解析此类坏空格并在解释协议元素之前将其删除.
BWS没有语义. 已知定义为BWS的任何内容可以 (MAY) 在解释或向下游转发消息之前删除.
OWS = *( SP / HTAB )
; optional whitespace
RWS = 1*( SP / HTAB )
; required whitespace
BWS = OWS
; "bad" whitespace
5.6.4. Quoted Strings (引用字符串)
如果使用双引号标记引用文本字符串, 则将其解析为单个值.
quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
qdtext = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text
反斜杠八位字节 ("") 可以用作quoted-string和comment构造中的单八位字节引用机制. 处理quoted-string值的接收方必须 (MUST) 处理quoted-pair, 就好像它被反斜杠后面的八位字节替换一样.
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
发送方不应该 (SHOULD NOT) 在quoted-string中生成quoted-pair, 除非在需要引用该字符串中出现的DQUOTE和反斜杠八位字节的地方. 发送方不应该 (SHOULD NOT) 在注释中生成quoted-pair, 除非在需要引用该注释中出现的括号 ["(" 和 ")"] 和反斜杠八位字节的地方.
5.6.5. Comments (注释)
通过将注释文本用括号包围, 可以在某些HTTP字段中包含注释. 注释仅允许在字段值定义中包含 "comment" 的字段中.
comment = "(" *( ctext / quoted-pair / comment ) ")"
ctext = HTAB / SP / %x21-27 / %x2A-5B / %x5D-7E / obs-text
5.6.6. Parameters (参数)
参数 (Parameters) 是名称/值对的实例; 它们通常在字段值中用作向项目附加辅助信息的通用语法. 每个参数通常由紧接在前面的分号分隔.
parameters = *( OWS ";" OWS [ parameter ] )
parameter = parameter-name "=" parameter-value
parameter-name = token
parameter-value = ( token / quoted-string )
参数名称不区分大小写. 参数值可能区分大小写或不区分大小写, 具体取决于参数名称的语义. 参数示例和一些等效形式可以在媒体类型 (第8.3.1节) 和Accept头字段 (第12.5.1节) 中看到.
匹配token产生式的参数值可以作为token或在quoted-string内传输. 带引号和不带引号的值是等效的.
注意: 参数不允许在 "=" 字符周围有空格 (甚至不是"坏"空格).
5.6.7. Date/Time Formats (日期/时间格式)
在1995年之前, 服务器通常使用三种不同的格式来传达时间戳. 为了与旧实现兼容, 这里定义了所有三种格式. 首选格式是互联网消息格式 [RFC5322] 使用的日期和时间规范的固定长度和单时区子集.
HTTP-date = IMF-fixdate / obs-date
首选格式的示例是
Sun, 06 Nov 1994 08:49:37 GMT ; IMF-fixdate
两种过时格式的示例是
Sunday, 06-Nov-94 08:49:37 GMT ; obsolete RFC 850 format
Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
在HTTP字段中解析时间戳值的接收方必须 (MUST) 接受所有三种HTTP-date格式. 当发送方生成包含一个或多个定义为HTTP-date的时间戳的字段时, 发送方必须 (MUST) 以IMF-fixdate格式生成这些时间戳.
HTTP-date值将时间表示为协调世界时 (Coordinated Universal Time, UTC) 的实例. 前两种格式通过格林威治标准时间的三字母缩写 "GMT" 表示UTC, GMT是UTC名称的前身; asctime格式中的值假定为UTC.
"时钟" (Clock) 是能够提供UTC中当前时刻的合理近似值的实现. 时钟实现应该使用NTP ([RFC5905]) 或某些类似协议与UTC同步.
首选格式:
IMF-fixdate = day-name "," SP date1 SP time-of-day SP GMT
; fixed length/zone/capitalization subset of the format
; see Section 3.3 of [RFC5322]
day-name = %s"Mon" / %s"Tue" / %s"Wed"
/ %s"Thu" / %s"Fri" / %s"Sat" / %s"Sun"
date1 = day SP month SP year
; e.g., 02 Jun 1982
day = 2DIGIT
month = %s"Jan" / %s"Feb" / %s"Mar" / %s"Apr"
/ %s"May" / %s"Jun" / %s"Jul" / %s"Aug"
/ %s"Sep" / %s"Oct" / %s"Nov" / %s"Dec"
year = 4DIGIT
GMT = %s"GMT"
time-of-day = hour ":" minute ":" second
; 00:00:00 - 23:59:60 (leap second)
hour = 2DIGIT
minute = 2DIGIT
second = 2DIGIT
过时格式:
obs-date = rfc850-date / asctime-date
rfc850-date = day-name-l "," SP date2 SP time-of-day SP GMT
date2 = day "-" month "-" 2DIGIT
; e.g., 02-Jun-82
day-name-l = %s"Monday" / %s"Tuesday" / %s"Wednesday"
/ %s"Thursday" / %s"Friday" / %s"Saturday"
/ %s"Sunday"
asctime-date = day-name SP date3 SP time-of-day SP year
date3 = month SP ( 2DIGIT / ( SP 1DIGIT ))
; e.g., Jun 2
HTTP-date区分大小写. 请注意, [CACHING] 第4.2节为缓存接收方放宽了这一要求.
发送方禁止 (MUST NOT) 在HTTP-date中生成超出语法中明确包含为SP的附加空格. day-name、day、month、year和time-of-day的语义与互联网消息格式中具有相应名称的构造定义的语义相同 ([RFC5322], 第3.3节).
接收rfc850-date格式时间戳值 (使用两位数年份) 的接收方必须 (MUST) 将看起来超过未来50年的时间戳解释为代表过去具有相同后两位数字的最近年份.
鼓励时间戳值的接收方在解析时间戳时保持健壮性, 除非字段定义另有限制. 例如, 消息偶尔会通过HTTP从可能生成互联网消息格式定义的任何日期和时间规范的非HTTP源转发.
注意: HTTP对时间戳格式的要求仅适用于它们在协议流中的使用. 实现不需要将这些格式用于用户呈现、请求日志记录等.