0

This is about some code I inherited; the intent is clear, but (at least in Firefox and Chrome) it is not behaving as intended.

The idea is clearly to build a PNG based on client-side data and to cache it unless and until that data changes. The intent presumably is that the state of the PNG is preserved regardless of whether or not the client is using cookies, local storage, etc., but at the same time the server does not preserve data about this client.

Client-side JavaScript:

function read_or_write_png(name, value) {
    // WRITE if value is defined, non-null, etc., get otherwise
    if (value) {
        // WRITE

        // Use cookie to convey new data to server
        document.cookie = 'bx_png=' + value + '; path=/';        

        // bx_png.php generates the image
        // based off of the http cookie and returns it cached
        var img = new Image();
        img.style.visibility = 'hidden';
        img.style.position = 'absolute';
        img.src = 'bx_png.php?name=' + name; // the magic saying "load this".
                                             // 'name' is not consulted server-side,
                                             //  it's here just to get uniqueness
                                             //  for what is cached.
    } else {
        // READ

        // Kill cookie so server should send a 304 header
        document.cookie = 'bx_png=; expires=Mon, 20 Sep 2010 00:00:00 UTC; path=/';

        // load the cached .png
        var img = new Image();
        img.style.visibility = 'hidden';
        img.style.position = 'absolute';
        img.src = 'bx_png.php?name=' + name;            
    }
}

Server-side PHP in bx_png.php:

if (!array_key_exists('bx_png', $_COOKIE) || !isset($_COOKIE['bx_png'])) {
    // we don't have a cookie. Client side code does this on purpose. Force cache.
    header("HTTP/1.1 304 Not Modified");
} else {
    header('Content-Type: image/png');
    header('Last-Modified: Wed, 30 Jun 2010 21:36:48 GMT');
    header('Expires: Tue, 31 Dec 2030 23:30:45 GMT');
    header('Cache-Control: private, max-age=630720000');
    // followed by the content of the PNG        
}

This works fine to write the PNG the first time and cache it, but clearly the intention is to be able to call this again, pass a different value for the same name, and have that cached. In practice, once the PNG has been cached, it would appear (via Fiddler) that the server is not called at all. That is, on an attempted read, rather than go to the server and get a 304 back, the browser just takes the content from the cache without ever talking to the server. In and of itself, that part is harmless, but of course what is harmful is that the same thing happens on an attempted write, and the server never has a chance to send back a distinct PNG based on the new value.

Does anyone have any idea how to tweak this to fulfill its apparent intention? Maybe something a bit different in the headers? Maybe some way of clearing the cache from client-side? Maybe something else entirely that I haven't thought of? I'm a very solid developer in terms of both server-side and client-side, but less experienced with trickiness like this around the HTTP protocol as such.

Joe Mabel
  • 1,372
  • 10
  • 29

2 Answers2

2

You need to add must-revalidate to your Cache-Control header to tell the browser to do that.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • Sounds very plausible, I'll try it. – Joe Mabel Oct 21 '13 at 16:41
  • Hmm. Well. Perfect in Firefox, which is at least an enormous step in the right direction. Thanks! Still doesn't work in Chrome, though. I'll investigate. Should I ask the Chrome part as a separate question or just keep this one open? – Joe Mabel Oct 21 '13 at 17:01
  • After more extensive experimentation, it's not even consistently working right in Firefox. I'm sure that what you said is part of the solution, but it is clearly just part. – Joe Mabel Oct 21 '13 at 20:51
0

Try cache-control: no-store as it fixed this exact same problem for me in Safari/WebKit. (I think Chrome fixed it in the time since your question.)

It's still an open WebKit bug but they added a fix for this header.

bbrown
  • 6,370
  • 5
  • 37
  • 43
  • Thanks. Project in question is currently on hold, so I'm not following this up right now. – Joe Mabel Jul 23 '15 at 03:51
  • 1
    Sure, just know that it fixed it for someone besides myself - https://discussions.apple.com/thread/5531657?tstart=0 – bbrown Jul 23 '15 at 16:11