If-None-Match

Hovav Shacham <[email protected]> 06 Jul 2002 20:49:21 -0700
Newsgroups gmane.comp.djb.publicfile
Message-ID <[email protected]>
This is in response to some discussion on this mailing list regarding
the If-Match-None: header.

Entity tags (RFC 2616 section 3.11) are opaque strings that function
like cryptographic hashes for content.  They provide an alternative to
modification time for determining whether content has changed.

Apache sends out entity tags in ETag: headers (section 14.19).  Apache
doesn't actually use a cryptographic checksum.  (See ap_make_etag() in
main/http_protocol.c.)

recent version of Internet Explorer will attempt to minimize network
usage by sending the cached version's entity tag in the If-None-Match:
header (section 14.26).

Publicfile does not implement If-None-Match:.  Thus if an existing web
site is moved from Apache to Publicfile, clients with cached copies of
pages will confuse Publicfile by sending If-None-Match: headers.

The correct behavior, as I understand it, is this.  If any of the
entity tags included in the If-None-Match: header matches the entity
tag of the current version of the content, the server should send a
304 (not modified) response to a GET or HEAD, and a 412 (precondition
failed) response to any other method, unless an If-Modified-Since:
header forces a 200 response after all.

If none of the entity tags in the If-None-Match: header matches
(``none'' is singular, but go tell that to the W3C), the server should
perform the requested action, ignoring any If-Modified-Since header.

More about the interaction of If-Modified-Since: (section 14.25) and
If-None-Match: is discussed in section 13.3.4.

Publicfile never sends ETag: headers, so the entity tag in the
If-None-Match: header can only match if it is the wildcard, ``*''.
Moreover, Publicfile handles only GET and HEAD requests, so it should
never send 412 responses.

A patch to publicfile-0.52 implementing this behavior is included.


Cheers --

Hovav.


--- httpd.c.old	Sat Jul  6 20:26:20 2002
+++ httpd.c	Sat Jul  6 20:28:37 2002
@@ -61,6 +61,8 @@
 struct tai mtimeage;
 stralloc mtimestr = {0};
 
+stralloc ifnonematch = {0};
+
 void header(char *code,char *message)
 {
   if (protocolnum == 1)
@@ -109,6 +111,8 @@
   int fd;
   int r;
 
+  int newbody;
+
   host.len = byte_chr(host.s,host.len,':');
   if (!host.len) {
     if (protocolnum > 1)
@@ -132,7 +136,12 @@
   if (protocolnum > 0) {
     tai_now(&now);
     if (!httpdate(&mtimestr,&mtime)) _exit(21);
-    if ((ims.len < mtimestr.len) || byte_diff(mtimestr.s,mtimestr.len,ims.s))
+    newbody = (ims.len < mtimestr.len) ||
+              byte_diff(mtimestr.s,mtimestr.len,ims.s);
+    if (ifnonematch.len)
+      newbody = !(ifnonematch.len == 1 && ifnonematch.s[0] == '*') ||
+                (ims.len && newbody);
+    if (newbody)
       header("200 ","OK");
     else {
       header("304 ","OK");
@@ -227,6 +236,7 @@
     if (!stralloc_copys(&path,"")) _exit(21);
     if (!stralloc_copys(&protocol,"")) _exit(21);
     if (!stralloc_copys(&ims,"")) _exit(21);
+    if (!stralloc_copys(&ifnonematch,"")) _exit(21);
     protocolnum = 2;
 
     spaces = 0;
@@ -291,7 +301,11 @@
           if (case_startb(field.s,field.len,"if-match:"))
             barf("412 ","I do not accept If-Match");
           if (case_startb(field.s,field.len,"if-none-match:"))
-            barf("412 ","I do not accept If-None-Match");
+            if (!ifnonematch.len)
+              for (i = 14;i < field.len;++i)
+                if (field.s[i] != ' ')
+                  if (field.s[i] != '\t')
+                    if (!stralloc_append(&ifnonematch,&field.s[i])) _exit(21);
           if (case_startb(field.s,field.len,"if-unmodified-since:"))
             barf("412 ","I do not accept If-Unmodified-Since");
           if (case_startb(field.s,field.len,"host:"))