跳到主要内容

4.3. 验证 (Validation)

当缓存对请求的 URI 拥有一个或多个已存储的响应,但无法提供其中任何一个(例如,因为它们不新鲜,或者无法被选择;见第 4.1 节)时,它可以在转发的请求中使用条件请求机制(conditional request mechanism,[RFC7232]),让下一个入站服务器有机会选择一个有效的已存储响应来使用(在此过程中更新已存储的元数据),或者用新的响应替换已存储的响应。这个过程被称为对已存储响应进行"验证"(validating)或"重新验证"(revalidating)。

4.3.1. 发送验证请求 (Sending a Validation Request)​

当发送用于缓存验证的条件请求时,缓存会发送一个或多个包含其已存储响应验证器元数据的前提条件头字段(precondition header fields),接收方随后将其与已存储响应是否等同于资源的当前表示进行比较。

其中一种验证器是 Last-Modified 头字段([RFC7232] 的第 2.2 节)中给出的时间戳,它可以用于 If-Modified-Since 头字段进行响应验证,或用于 If-Unmodified-Since 或 If-Range 头字段进行选择表示(即,客户端特指引用了带有该时间戳的先前获得的表示)。

另一种验证器是 ETag 头字段([RFC7232] 的第 2.3 节)中给出的实体标签(entity-tag)。一个或多个指示一个或多个已存储响应的实体标签,可以用于 If-None-Match 头字段进行响应验证,或用于 If-Match 或 If-Range 头字段进行选择表示(即,客户端特指引用了带有所列实体标签的一个或多个先前获得的表示)。

4.3.2. 处理收到的验证请求 (Handling a Received Validation Request)​

请求链中的每个客户端都可能拥有自己的缓存,因此中间代理上的缓存经常会收到来自其他(出站)缓存的条件请求。同样地,一些用户代理利用条件请求来将数据传输限制为最近修改的表示,或者完成部分检索的表示的传输。

如果缓存收到一个可以通过重用其已存储的 200(OK)或 206(Partial Content)响应来满足的请求,缓存应该(SHOULD)针对所选响应中包含的相应验证器,评估该请求中收到的任何适用的条件头字段前提条件。缓存必须不(MUST NOT)评估:

  • 仅适用于源服务器的条件头字段,
  • 存在于语义无法由缓存响应满足的请求中的条件头字段,或
  • 应用于它没有已存储响应的目标资源的条件头字段;

此类前提条件很可能是针对某个其他(入站)服务器的。

缓存对条件请求的正确评估取决于收到的前提条件头字段及其优先级,如 [RFC7232] 第 6 节所定义。If-Match 和 If-Unmodified-Since 条件头字段不适用于缓存。

包含 If-None-Match 头字段([RFC7232] 的第 3.2 节)的请求表示客户端希望将其自己的一个或多个已存储响应与缓存所选择的已存储响应进行比较验证。如果字段值为 "*",或者字段值是一个实体标签列表且其中至少一个与所选已存储响应的实体标签匹配,缓存接收方应该(SHOULD)生成 304(Not Modified)响应(使用所选已存储响应的元数据),而不是发送该已存储响应。

当缓存决定为包含 If-None-Match 实体标签列表的请求重新验证其自己的已存储响应时,缓存可以(MAY)将收到的列表与其自己的已存储响应集合(新鲜的或陈旧的)中的实体标签列表合并,并将这两个列表的并集作为转发的请求中替换的 If-None-Match 头字段值发送。如果某个已存储响应只包含部分内容,缓存必须不(MUST NOT)将该响应的实体标签包含进并集中,除非该请求针对的范围能被该部分已存储响应完全满足。如果对转发请求的响应是 304(Not Modified)并且具有一个不在客户端列表中的实体标签的 ETag 头字段值,则缓存必须(MUST)通过重用其对应的已存储响应(按 304 响应元数据更新后,见第 4.3.4 节)为客户端生成 200(OK)响应。

如果不存在 If-None-Match 头字段,则包含 If-Modified-Since 头字段([RFC7232] 的第 3.3 节)的请求表示客户端希望按修改日期验证其自己的一个或多个已存储响应。在以下任一情况为真时,缓存接收方应该(SHOULD)生成 304(Not Modified)响应(使用所选已存储响应的元数据):1)所选已存储响应具有早于或等于该条件时间戳的 Last-Modified 字段值;2)所选已存储响应中没有 Last-Modified 字段,但它具有早于或等于该条件时间戳的 Date 字段值;或 3)所选已存储响应中既没有 Last-Modified 也没有 Date,但缓存记录其接收时间早于或等于该条件时间戳。

实现了对范围请求的部分响应的缓存(如 [RFC7233] 所定义),还需要针对其选定的已存储响应评估收到的 If-Range 头字段([RFC7233] 的第 3.2 节)。

4.3.3. 处理验证响应 (Handling a Validation Response)​

缓存对条件请求响应的处理取决于其状态码:

  • 304(Not Modified)响应状态码表示已存储的响应可以被更新并复用;见第 4.3.4 节。
  • 完整响应(即带有有效载荷主体的响应)表示条件请求中提名的已存储响应均不合适。相反,缓存必须(MUST)使用该完整响应来满足请求,并且可以(MAY)替换已存储的响应。
  • 然而,如果缓存在尝试验证响应时收到 5xx(Server Error)响应,它可以将该响应转发给请求客户端,或者表现得如同服务器未能响应。在后一种情况下,缓存可以(MAY)发送先前存储的响应(见第 4.2.4 节)。

4.3.4. 验证时刷新已存储响应 (Freshening Stored Responses upon Validation)​

当缓存收到 304(Not Modified)响应,并且对同一缓存键已经拥有一个或多个已存储的 200(OK)响应时,缓存需要确定哪些已存储的响应被该新响应更新,然后用 304 响应中提供的新信息更新这些已存储的响应。

用于更新的已存储响应通过以下第一项匹配(如果有)来识别:

  • 如果新响应包含强验证器(strong validator,见 [RFC7232] 的第 2.1 节),则该强验证器标识要更新的所选表示。所有具有相同强验证器的已存储响应都被选中。如果没有任何已存储的响应包含相同的强验证器,则缓存必须不(MUST NOT)使用新响应更新任何已存储的响应。
  • 如果新响应包含弱验证器(weak validator),并且该验证器对应于缓存的某个已存储响应,则选中这些匹配已存储响应中最近的一个进行更新。
  • 如果新响应不包含任何形式的验证器(例如,当客户端从 Last-Modified 响应头字段之外的来源生成 If-Modified-Since 请求时),并且只有一个已存储响应,且该已存储响应也缺少验证器,则该已存储响应被选中更新。

如果某个已存储响应被选中更新,缓存必须(MUST):

  • 删除已存储响应中 warn-code 为 1xx 的任何 Warning 头字段(见第 5.5 节);
  • 保留已存储响应中 warn-code 为 2xx 的任何 Warning 头字段;并且
  • 使用 304(Not Modified)响应中提供的其他头字段替换已存储响应中对应头字段的所有实例。

4.3.5. 通过 HEAD 刷新响应 (Freshening Responses via HEAD)​

对 HEAD 方法的响应与用 GET 发出的等价请求所得到的响应相同,只是它缺少主体。如果更高效的条件 GET 请求机制不可用(因为已存储响应中没有验证器),或者不希望传输表示主体(即使它已更改),则可以利用 HEAD 响应的这一特性来使缓存的 GET 响应失效或更新。

当缓存针对给定请求目标发起入站 HEAD 请求并收到 200(OK)响应时,缓存应该(SHOULD)更新或使该请求本可以选择的每个已存储的 GET 响应失效(见第 4.1 节)。

对于每个本可以被选中的已存储响应,如果已存储响应与 HEAD 响应在收到的任何验证器字段(ETag 和 Last-Modified)上具有匹配的值,并且如果 HEAD 响应具有 Content-Length 头字段,该 Content-Length 的值与已存储响应匹配,则缓存应该(SHOULD)按如下所述更新已存储响应;否则,缓存应该(SHOULD)将该已存储响应视为陈旧。

如果缓存用 HEAD 响应中提供的元数据更新已存储响应,缓存必须(MUST):

  • 删除已存储响应中 warn-code 为 1xx 的任何 Warning 头字段(见第 5.5 节);
  • 保留已存储响应中 warn-code 为 2xx 的任何 Warning 头字段;并且
  • 使用 HEAD 响应中提供的其他头字段替换已存储响应中对应头字段的所有实例,并将新的头字段附加到已存储响应的头节区,除非受 Cache-Control 头字段的限制。