3. 在缓存中存储响应 (Storing Responses in Caches)
除非满足以下所有条件, 缓存 MUST NOT 存储对请求的响应:
-
缓存理解该请求方法;
-
响应状态码是最终状态 (final) (见 [HTTP] 第 15 节);
-
如果响应状态码为 206 或 304, 或者存在 must-understand 缓存指令 (见第 5.2.2.3 节): 缓存理解该响应状态码;
-
响应中不存在 no-store 缓存指令 (见第 5.2.2.5 节);
-
如果缓存是共享的: private 响应指令不存在, 或允许共享缓存存储修改后的响应; 见第 5.2.2.7 节;
-
如果缓存是共享的: 请求中不存在 Authorization 头字段 (见 [HTTP] 第 11.6.2 节), 或某个响应指令显式允许共享缓存; 见第 3.5 节; 且
-
响应至少包含以下一项:
-
public 响应指令 (见第 5.2.2.9 节);
-
如果缓存不是共享的, private 响应指令 (见第 5.2.2.7 节);
-
Expires 头字段 (见第 5.3 节);
-
max-age 响应指令 (见第 5.2.2.1 节);
-
如果缓存是共享的: s-maxage 响应指令 (见第 5.2.2.10 节);
-
允许缓存的缓存扩展 (见第 5.2.3 节); 或
-
被定义为启发式可缓存 (heuristically cacheable) 的状态码 (见第 4.2.2 节).
-
注意, 缓存扩展可以覆盖上面列出的任何要求; 见第 5.2.3 节.
在此上下文中, 如果缓存识别某个请求方法或响应状态码, 并实现所有已规定的缓存相关行为, 则称缓存"理解 (understands)"该请求方法或响应状态码.
注意, 在正常操作中, 一些缓存不会存储既没有缓存验证器 (cache validator), 也没有显式过期时间的响应, 因为这类响应通常不值得存储. 然而, 并不禁止缓存存储这类响应.
3.1 存储头字段和尾部字段 (Storing Header and Trailer Fields)
缓存存储响应时 MUST 包含所有收到的响应头字段, 包括无法识别的字段; 这确保新的 HTTP 头字段能够成功部署. 但存在以下例外:
-
按照 [HTTP] 第 7.6.1 节, Connection 头字段以及其中列出的字段 MUST 在转发消息之前移除. 这 MAY 通过在存储前移除它们来实现.
-
同样, 某些字段的语义要求在转发消息之前移除它们, 这 MAY 通过在存储前移除它们来实现; 一些示例见 [HTTP] 第 7.6.1 节.
-
no-cache (第 5.2.2.4 节) 和 private (第 5.2.2.7 节) 缓存指令可以带有参数, 分别阻止所有缓存和共享缓存存储头字段.
-
缓存在转发请求时使用的代理专用头字段 MUST NOT 被存储, 除非缓存将代理的身份纳入缓存键. 在实践中, 这限于 Proxy-Authenticate ([HTTP] 第 11.7.1 节), Proxy-Authentication-Info ([HTTP] 第 11.7.3 节) 和 Proxy-Authorization ([HTTP] 第 11.7.2 节).
缓存 MAY 将尾部字段 (trailer field) 与头字段分开存储, 或者丢弃它们. 缓存 MUST NOT 将尾部字段与头字段合并.
3.2 更新已存储的头字段 (Updating Stored Header Fields)
在多种情况下, 缓存需要用另一个响应 (通常是更新的响应) 来更新已存储响应的头字段; 例如见第 3.4, 4.3.4 和 4.3.5 节.
执行此操作时, 缓存 MUST 将提供响应中的每个头字段添加到已存储响应中, 替换任何已有字段值, 但以下情况除外:
-
按第 3.1 节排除在存储之外的头字段,
-
如下所述, 缓存的已存储响应所依赖的头字段,
-
如下所述, 由接收方自动处理并移除的头字段, 以及
-
Content-Length 头字段.
在某些情况下, 缓存 (尤其是用户代理中的缓存) 存储的是处理收到响应后的结果, 而不是响应本身; 更新会影响该处理过程的头字段可能导致行为不一致和安全问题. 在这些情况下, 缓存 MAY 在更新已存储响应时省略这些头字段, 但 SHOULD 将这种省略限制在确保已存储响应完整性所必需的字段上.
例如, 浏览器可能在收到响应时解码响应的内容编码 (content coding), 从而使其存储的数据与响应的原始元数据脱节. 使用不同的 Content-Encoding 头字段更新该已存储元数据会产生问题. 同样, 浏览器可能存储解析后的 HTML 树, 而不是响应中收到的内容; 在这种情况下更新 Content-Type 头字段并不可行, 因为解析期间对格式做出的任何假设都已经固化在结果中.
此外, 某些字段会被 HTTP 实现自动处理并移除, 例如 Content-Range 头字段. 即使实际上没有发生处理, 实现也 MAY 自动在更新中省略这些头字段.
注意, Content-* 前缀并不表示某个头字段会从更新中省略; 它是 MIME 头字段的约定, 而不是 HTTP 的约定.
3.3 存储不完整响应 (Storing Incomplete Responses)
如果请求方法是 GET, 响应状态码是 200 (OK), 并且已收到完整的响应头部节, 缓存 MAY 存储不完整响应 (见 [HTTP] 第 6.1 节), 前提是将已存储响应记录为不完整. 同样, 206 (Partial Content) 响应 MAY 作为不完整的 200 (OK) 响应存储. 但是, 如果缓存不支持 Range 和 Content-Range 头字段, 或不理解这些字段中使用的范围单位 (range unit), 缓存 MUST NOT 存储不完整响应或部分内容响应.
缓存 MAY 通过发出后续范围请求 (见 [HTTP] 第 14.2 节), 并按第 3.4 节的定义将成功响应与已存储响应组合, 来补全已存储的不完整响应. 除非该响应已经补全, 或者请求是部分请求且指定的范围完全位于该不完整响应之内, 缓存 MUST NOT 使用不完整响应来应答请求. 除非使用 206 (Partial Content) 状态码明确标记, 缓存 MUST NOT 向客户端发送部分响应.
3.4 组合部分内容 (Combining Partial Content)
如果连接过早关闭, 或请求使用一个或多个 Range 指定符 (见 [HTTP] 第 14.2 节), 响应可能只传输部分表示. 经过几次这样的传输后, 缓存可能已经收到了同一表示的若干范围. 如果这些范围都共享同一个强验证器 (strong validator), 且缓存符合 [HTTP] 第 15.3.7.3 节中的客户端要求, 缓存 MAY 将这些范围组合成一个已存储响应, 并重用该响应来满足后续请求.
当把新响应与一个或多个已存储响应组合时, 缓存 MUST 按第 3.2 节使用新响应中提供的头字段更新已存储响应的头字段.
3.5 存储对已认证请求的响应 (Storing Responses to Authenticated Requests)
共享缓存 MUST NOT 使用对带有 Authorization 头字段 (见 [HTTP] 第 11.6.2 节) 的请求所产生的缓存响应来满足任何后续请求, 除非该响应包含 Cache-Control 字段, 且其中的响应指令 (第 5.2.2 节) 允许共享缓存存储该响应, 并且缓存符合该指令对该响应的要求.
在本规范中, 以下响应指令具有此效果: must-revalidate (第 5.2.2.2 节), public (第 5.2.2.9 节) 和 s-maxage (第 5.2.2.10 节).