0

I created this repository to investigate why my custom headers were not being sent to the client when the response code is 304.

I had previously read answers to this question, which explains why headers are not sent on a 304 response. However, I noticed something strange. I ran the tests below against the same application (observing x-test-header, which I generate randomly for every request), first directly to the application, then via Apache reverse proxy. Interestingly, they behaved the same. I received a different header on both cases even when I receive a 304 status code

const chaiHttp = require('chai-http');
const chai = require('chai');
const assert = chai.assert;
const server = require('../main');

chai.use(chaiHttp);

suite("chai etag support", () => {

   test("repeating a request to /data with if-none-match should give 304", async () => {

      let res = await chai
         .request(server)
         .get('/data');

      const etag = res.get('etag');


      res = await chai
         .request(server)
         .get('/data')
         .set('if-none-match', etag);

      assert.equal(res.status, 304);

   });

});

function behaviourTest(server, suiteName) {

   suite(suiteName, function() {

      test('Two subsequent GET /data requests should give the same etag', async () => {
         
         const firstRequest = await chai
            .request(server)
            .get('/data');

         const secondRequest = await chai
            .request(server)
            .get('/data');

         
         assert.notEqual(secondRequest.get('x-test-header'), firstRequest.get('x-test-header'), "x-test-headers should be different");
         assert.equal(secondRequest.get('etag'), firstRequest.get('etag'), "Etags should be the same");

         const etag = secondRequest.get('etag');

         const thirdRequest = await chai
            .request(server)
            .get('/data')
            .set('if-none-match', etag);

         assert.equal(thirdRequest.status, 304);
         assert.notEqual(thirdRequest.get('x-test-header'), secondRequest.get('x-test-header'), "x-test-headers should be different");

      });
   });

}


behaviourTest(process.env.URL, 'Without reverse proxy');
behaviourTest(process.env.REVERSE_PROXY_URL, 'With reverse proxy');

The above got me to assume that Apache is sending the headers every time. I then ran basically the same tests but inside an HTML page. Only then did the requests via the reverse proxy fail to send an updated x-test-header value. Or maybe it got sent, and the browser ignored it and used the value from the cache.


<html>

<head>
   
   <meta name="viewport" content="width=device-width, initial-scale=1.0">

   <title>Etag Test</title>

   <style>

      .full-screen {
         position: fixed;
         top: 0;
         bottom: 0;
         left: 0;
         right: 0;
      }

      .vh-align {
         display: flex;
         align-items: center;
         justify-content: center;
      }

      #div-results {
         width: 360px;
         min-height: 400px;
         font-family: 'Courier New', Courier, monospace;
         border: 1px solid #ccc;
         border-radius: 2px;
         overflow-y: auto;
         background-color: black;
         color: white;
         padding: 20px;
      }

      .red-text {
         color: red;
      }

      .green-text {
         color: green;
      }

   </style>
</head>

<body>

   <div class="full-screen vh-align">
      <div id="div-results">
         TEST RESULTS
      </div>
   </div>

   <script src="https://cdnjs.cloudflare.com/ajax/libs/chai/4.3.6/chai.min.js" integrity="sha512-sP7hp2cpEWsmOHQPc6taNuXFmvG59BhI3YS3tOvZ+nDEqOyyORLiY3nBjLkWgCNI8SFZ5koUyiBO42Q3wzZDrQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
   <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.27.2/axios.min.js" integrity="sha512-odNmoc1XJy5x1TMVMdC7EMs3IVdItLPlCeL5vSUPN2llYKMJ2eByTTAIiiuqLg+GdNr9hF6z81p27DArRFKT7A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
   
   <script>

      function getHeadersFromFetchAPIResponse(res) {
         const keys = Array.from(res.headers.keys());

         const headers = {};

         for (let i in keys) {
            const key = keys[i]
            headers[key] = res.headers.get(key);
         }

         return headers;

      }

      async function test(url, suiteName) {

         let icon, message = '';

         try {

            const firstRes = await fetch(`${url}/data`);
            const secondRes = await fetch(`${url}/data`);

            assert.equal(firstRes.headers.get('etag'), secondRes.headers.get('etag'),);
            assert.notEqual(firstRes.headers.get('x-test-header'), secondRes.headers.get('x-test-header'));

            icon = tick;

         } catch (err) {
            icon = xMark;
            message = `<br>${tab}${String(err)}`;
            console.log(err);
         }

         document.getElementById('div-results').innerHTML += `<br>${icon} ${suiteName}${message}`;

      }

      const { assert } = chai;

      const proxiedUrl = window.prompt("Reverse proxied url");
      const url = window.prompt("URL without proxy");

      const tick = '<span class="green-text">&#10003;</span>';
      const xMark = '<span class="red-text">&#x2717;</span>';
      const tab = '&nbsp;&nbsp;&nbsp;';

      (async () => {
         await test(url, "No reverse proxy");
         await test(proxiedUrl, "Reverse proxy");
      })();


   </script>
</body>
</html>

Why am I getting different behaviors in Node instead of the browser?

Xavier Mukodi
  • 166
  • 1
  • 10

0 Answers0