11

我正在编写一种资源处理方法,在其中控制对各种文件的访问,并且我希望能够利用浏览器的缓存。我的问题有两个:

  1. 为了确定是否应该发送 304 响应,我需要检查哪些明确的 HTTP 标头,以及在检查它们时我在寻找什么?

  2. 此外,当我最初将文件(如“Last-Modified”)作为 200 响应发送时,是否需要发送任何标题?

一些伪代码可能是最有用的答案。


缓存控制头呢?它的各种可能值是否会影响您发送给客户端的内容(即 max-age),还是应该只遵守 if-modified-since ?

4

5 回答 5

8

这是我实现它的方式。该代码已经运行了一年多,并且适用于多个浏览器,所以我认为它非常可靠。这是基于RFC 2616并通过观察各种浏览器发送的内容和时间。

这是伪代码:

server_etag = gen_etag_for_this_file(myfile)
etag_from_browser = get_header("Etag")

如果 etag_from_browser 不存在:
    etag_from_browser = get_header("If-None-Match")
如果浏览器引用了 etag:
    去掉引号(例如 "foo" --> foo)

将 server_etag 设置为 http 标头

如果 etag_from_browser 匹配 server_etag
    向浏览器发送304返回码

这是我处理此问题的服务器逻辑的片段。

/* 客户端应该设置 Etag 或 If-None-Match */
/* 一些客户引用参数,如果是这样,去掉引号 */
mketag(etag, &sb);

etagin = apr_table_get(r->headers_in, "Etag");
如果(etagin == NULL)
    etagin = apr_table_get(r->headers_in, "如果-无匹配");
if (etag != NULL && etag[0] == '"') {
    诠释 sl;
    sl = strlen(etag);
    memmove(etag,etag+1,sl+1);
    etag[sl-2] = 0;
    logit(2,"etag=:%s:",etag);
}   
...
apr_table_add(r->headers_out, "ETag", etag);
...
if (etagin != NULL && strcmp(etagin, etag) == 0) {
    /* 如果 etag 匹配,我们返回 304 */
    rc = HTTP_NOT_MODIFIED;
}   

如果您在 etag 生成方面需要一些帮助,请发布另一个问题,我会挖掘出一些同样可以做到这一点的代码。!

于 2008-08-07T08:30:16.113 回答
4

带有 If-Modified-Since ("IMS") 或 If-Not-Match ("INM") 标头的 GET 或 HEAD 请求可能会导致 304 Not Modified 响应。

为了决定在收到这些标头时要做什么,假设您正在处理没有这些条件标头的 GET 请求。确定响应中的 ETag 和 Last-Modified 标头的值,并使用它们来做出决定。希望您已经构建了系统,以便确定这一点比构建完整响应的成本更低。

如果存在 INM,并且该标头的值与您将放置在 ETag 中的值相同,则以 304 响应。

如果存在 IMS,并且该标头中的日期值晚于您在 Last-Modified 中放置的日期值,则使用 304 响应。

否则,就好像请求不包含这些标头一样继续。

对于问题第 2 部分的最省力方法,请找出您可以在 Web 应用程序中轻松正确地生成哪些(Expires、ETag 和 Last-Modified)标头。

推荐阅读材料:

http://www.w3.org/Protocols/rfc2616/rfc2616.html

http://www.mnot.net/cache_docs/

于 2008-08-09T13:27:59.343 回答
3

如果客户端明确声明它可能已经在其缓存中拥有该页面,您应该发送 304。这称为条件 GET,它应该在请求中包含if-modified-since标头。

基本上,此请求标头包含客户端声称拥有缓存副本的日期。您应该检查在此日期之后内容是否发生了变化,如果没有,则发送 304。

有关 RFC 中的相关部分,请参见http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25

于 2008-08-07T08:01:54.203 回答
2

我们还在处理缓存但安全的资源。如果您发送/生成一个 ETAg 标头(RFC 2616 第 13.3 节建议您应该),那么客户端必须在条件请求中使用它(通常在 If-None-Match - HTTP_IF_NONE_MATCH - 标头中)。如果您发送 Last-Modified 标头(同样您应该),那么您应该检查 If-Modified-Since - HTTP_IF_MODIFIED_SINCE - 标头。如果你发送两个,那么客户端应该发送两个,但它必须发送 ETag。另请注意,验证只是定义为检查条件标头是否与您要发送的标头严格相等。此外,只有强验证器(例如 ETag)将用于范围请求(仅请求部分资源)。

在实践中,由于我们保护的资源是相当静态的,并且一秒钟的延迟时间是可以接受的,我们正在执行以下操作:

  1.  检查用户是否有权访问请求的资源

         如果不是,请根据需要重定向它们或发送 4xx 响应。我们将对看起来像黑客攻击或公然尝试执行安全最终运行的请求生成 404 响应。

  2.  将 If-Modified-Since 标头与我们将发送(见下文)的 Last-Modified 标头进行比较,以实现严格相等

         如果它们匹配,则发送 304 Not Modified 响应并退出页面处理

  3.  使用请求资源的修改时间创建 Last-Modified 标头

        在 RFC 2616 中查找 HTTP 日期格式

  4.  发送标头和资源内容以及适当的 Content-Type

我们决定避开 ETag 标头,因为它对我们的目的来说太过分了。我想我们也可以只使用日期时间戳作为 ETag。如果我们转向真正的 ETag 系统,我们可能会存储资源的计算哈希值并将其用作 ETags。

如果您的资源是从数据库内容动态生成的,那么 ETags 可能更适合您的需求,因为它们只是您认为合适的填充文本。

于 2009-10-13T14:05:05.560 回答
1

关于缓存控制:

在提供服务时,您不必担心缓存控制,除了将其设置为合理的值。它基本上告诉浏览器和其他下游实体(例如代理)在缓存超时之前应该经过的最长时间。

于 2008-08-07T08:47:09.700 回答