Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are browsers supposed to handle 304 responses automagically?

Might be a silly question, but I haven't found any clear answer yet.

My server handles ETag caching for some quite big JSON responses we have, returning 304 NOT MODIFIED with an empty body if the If-None-Match header contains the same hash as the one newly generated (shallow ETags).

Are browsers supposed to handle this automagically, or do the in-browser client apps consuming the API asynchronously need to implement some logic to handle such responses (i.e. use the cached version if 304 is responded, create/update the cached version otherwise)?

Because so far, I've manually implemented this logic client-side, but I'm wondering whether I just reinvented a square wheel...


In other words, with the Cache-Control header for example, the in-browser client apps don't need to parse the value, check for max-age for instance, stores it somehow, setup a timeout, etc.: everything is handled ahead by the browsers directly. The question is: are browsers supposed to behave the same way when they receive a 304?


Here is how I wrote my client so far (built with AngularJS, running in browsers):

myModule

  .factory("MyRepository", ($http) => {

    return {
      fetch: (etag) => {
        return $http.get(
          "/api/endpoint",
          etag ? { headers: { "If-None-Match": etag } } : undefined
        );
      }
    };

  })

  .factory("MyService", (MyRepository, $q) => {

    let latestEtag = null;
    let latestVersion = null;

    return {
      fetch: () => {
        return MyRepository
          .fetch(latestEtag)
          .then((response) => {
            latestEtag = response.headers("ETag");
            latestVersion = response.data;
            return angular.copy(latestVersion);
          })
          .catch((response) => {
            return 304 === error.status
              ? angular.copy(latestVersion)
              : $q.reject(response)
          });
      }
    };

  });

So basically, is the above logic effectively needed, or am I supposed to be able to simply use $http.get("/api/endpoint") directly?

This code above is working fine, which seems to mean that it needs to be handled programmatically, although I've never seen such "custom" implementations on the articles I read.

like image 718
sp00m Avatar asked Apr 10 '18 10:04

sp00m


2 Answers

The 304 responses are automagically handled by browser as such

So I created a simple page

<html>
<head>
<script src="./axios.min.js"></script>
<script src="./jquery-3.3.1.js"></script>
</head>
<body>
<h1>this is a test</page>
</body>
</html>

and the added a test.json file

root@vagrant:/var/www/html# cat test.json
{

"name": "tarun"

}

And then in nginx added below

location ~*  \.(jpg|jpeg|png|gif|ico|css|js|json)$ {
   expires 365d;
}

Now the results

AXIOS

AXIOS

As you can see the first request is 200 and second one 304 but there is no impact on the JS code

jQuery

jQuery response

Same thing with jQuery as well

From the curl you can see that server didn't send anything on the 2nd 304 request

$ curl -v 'http://vm/test.json' -H 'If-None-Match: "5ad71064-17"' -H 'DNT: 1' -H 'Accept-Encoding: gzip, deflate' -H 'Accept-Language: en-US,en;q=0.9' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36' -H 'Accept: */*' -H 'Referer: http://vm/' -H 'X-Requested-With: XMLHttpRequest' -H 'Connection: keep-alive' -H 'If-Modified-Since: Wed, 18 Apr 2018 09:31:16 GMT' --compressed
*   Trying 192.168.33.100...
* TCP_NODELAY set
* Connected to vm (192.168.33.100) port 80 (#0)
> GET /test.json HTTP/1.1
> Host: vm
> If-None-Match: "5ad71064-17"
> DNT: 1
> Accept-Encoding: gzip, deflate
> Accept-Language: en-US,en;q=0.9
> User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36
> Accept: */*
> Referer: http://vm/
> X-Requested-With: XMLHttpRequest
> Connection: keep-alive
> If-Modified-Since: Wed, 18 Apr 2018 09:31:16 GMT
>
< HTTP/1.1 304 Not Modified
< Server: nginx
< Date: Wed, 18 Apr 2018 09:42:45 GMT
< Last-Modified: Wed, 18 Apr 2018 09:31:16 GMT
< Connection: keep-alive
< ETag: "5ad71064-17"
<
* Connection #0 to host vm left intact

So you don't need to handle a 304, browser will do that work for you.

like image 83
Tarun Lalwani Avatar answered Nov 17 '22 21:11

Tarun Lalwani


Yes, probably all modern major browsers handle response validation using conditional requests well. Relevant excerpt from The State of Browser Caching, Revisited article by Mark Nottingham:

Validation allows a cache to check with the server to see if a stale stored response can be reused.

All of the tested browsers support validation based upon ETag and Last-Modified. The tricky part is making sure that the 304 Not Modified response is correctly combined with the stored response; specifically, the headers in the 304 update the stored response headers.

All of the tested browsers do update stored headers upon a 304, both in the immediate response and subsequent ones served from cache.

This is good news; updating headers with a 304 is an important mechanism, and when they get out of sync it can cause problems.


For more information check HTTP Caching article by Ilya Grigorik.

like image 39
Leonid Vasilev Avatar answered Nov 17 '22 20:11

Leonid Vasilev