Skip to content

Commit

Permalink
send longer cache headers by default (#1725)
Browse files Browse the repository at this point in the history
* tell browsers and downstream caches to cache for
  `env.BADGE_MAX_AGE_SECONDS`, default 0 for dev

* set Cache-Control: no-cache, no-store, must-revalidate if maxAge=0

* add servertime badge to help with cache header debugging

* if service category is 'debug', exclude from examples

* ignore maxAge GET param if less than `env.BADGE_MAX_AGE_SECONDS`
  • Loading branch information
chris48s authored Jul 20, 2018
1 parent fd76819 commit b9db222
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 10 deletions.
2 changes: 1 addition & 1 deletion frontend/components/usage.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ export default class Usage extends React.PureComponent {
<td>
<code>?maxAge=3600</code>
</td>
<td>Set the HTTP cache lifetime in secs</td>
<td>Set the HTTP cache lifetime in secs (values below the default will be ignored)</td>
</tr>
</tbody>
</table>
Expand Down
4 changes: 4 additions & 0 deletions lib/all-badge-examples.js
Original file line number Diff line number Diff line change
Expand Up @@ -2222,6 +2222,10 @@ function loadExamples() {
loadServiceClasses().forEach(ServiceClass => {
const category = findCategory(ServiceClass.category);
if (category === undefined) {
if (ServiceClass.category === 'debug') {
// we don't want to show debug services on the examples page
return;
}
throw Error(`Unknown category ${ServiceClass.category} referenced in ${ServiceClass.name}`);
}
const prepared = ServiceClass.prepareExamples();
Expand Down
23 changes: 17 additions & 6 deletions lib/request-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,24 @@ function handleRequest (makeBadge, handlerOptions) {
return (queryParams, match, end, ask) => {
const reqTime = new Date();

if (queryParams.maxAge !== undefined && /^[0-9]+$/.test(queryParams.maxAge)) {
ask.res.setHeader('Cache-Control', 'max-age=' + queryParams.maxAge);
ask.res.setHeader('Expires', new Date(+reqTime + queryParams.maxAge * 1000).toGMTString());
} else {
// Cache management - no cache, so it won't be cached by GitHub's CDN.
let maxAge = parseInt(process.env.BADGE_MAX_AGE_SECONDS) || 0;
if (
queryParams.maxAge !== undefined
&& /^[0-9]+$/.test(queryParams.maxAge)
&& parseInt(queryParams.maxAge) > maxAge
) {
// only queryParams.maxAge to override the default
// if it is greater than env.BADGE_MAX_AGE_SECONDS
maxAge = parseInt(queryParams.maxAge);
}
// send both Cache-Control max-age and Expires
// in case the client implements HTTP/1.0 but not HTTP/1.1
if (maxAge === 0) {
ask.res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
ask.res.setHeader('Expires', reqTime.toGMTString()); // Proxies, GitHub, see #221.
ask.res.setHeader('Expires', reqTime.toGMTString());
} else {
ask.res.setHeader('Cache-Control', 'max-age=' + maxAge);
ask.res.setHeader('Expires', new Date(+reqTime + maxAge * 1000).toGMTString());
}

ask.res.setHeader('Date', reqTime.toGMTString());
Expand Down
36 changes: 33 additions & 3 deletions lib/request-handler.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ describe('The request handler', function() {
before(analytics.load);

let camp;
const initialBadgeMaxAge = process.env.BADGE_MAX_AGE_SECONDS;

beforeEach(function (done) {
camp = Camp.start({ port: config.port, hostname: '::' });
camp.on('listening', () => done());
Expand All @@ -43,6 +45,7 @@ describe('The request handler', function() {
camp.close(() => done());
camp = null;
}
process.env.BADGE_MAX_AGE_SECONDS = initialBadgeMaxAge;
});

describe('the options object calling style', function() {
Expand Down Expand Up @@ -105,15 +108,42 @@ describe('The request handler', function() {
expect(handlerCallCount).to.equal(1);
});

it('should set the expires header to current time', async function () {
it('should set the expires header to current time + BADGE_MAX_AGE_SECONDS', async function () {
process.env.BADGE_MAX_AGE_SECONDS = 900;
const res = await fetch(`${baseUri}/testing/123.json`);
expect(res.headers.get('expires')).to.equal(res.headers.get('date'));
const expectedExpiry = new Date(+(new Date(res.headers.get('date'))) + 900000).toGMTString();
expect(res.headers.get('expires')).to.equal(expectedExpiry);
expect(res.headers.get('cache-control')).to.equal('max-age=900');
});

it('should set the expires header to current time + max-age', async function () {
it('should set the expires header to current time + maxAge', async function () {
process.env.BADGE_MAX_AGE_SECONDS = 0;
const res = await fetch(`${baseUri}/testing/123.json?maxAge=3600`);
const expectedExpiry = new Date(+(new Date(res.headers.get('date'))) + 3600000).toGMTString();
expect(res.headers.get('expires')).to.equal(expectedExpiry);
expect(res.headers.get('cache-control')).to.equal('max-age=3600');
});

it('should ignore maxAge if maxAge < BADGE_MAX_AGE_SECONDS', async function () {
process.env.BADGE_MAX_AGE_SECONDS = 600;
const res = await fetch(`${baseUri}/testing/123.json?maxAge=300`);
const expectedExpiry = new Date(+(new Date(res.headers.get('date'))) + 600000).toGMTString();
expect(res.headers.get('expires')).to.equal(expectedExpiry);
expect(res.headers.get('cache-control')).to.equal('max-age=600');
});

it('should set Cache-Control: no-cache, no-store, must-revalidate if maxAge=0', async function () {
process.env.BADGE_MAX_AGE_SECONDS = 0;
const res = await fetch(`${baseUri}/testing/123.json`);
expect(res.headers.get('expires')).to.equal(res.headers.get('date'));
expect(res.headers.get('cache-control')).to.equal('no-cache, no-store, must-revalidate');
});

it('should set Cache-Control: no-cache, no-store, must-revalidate if BADGE_MAX_AGE_SECONDS not set', async function () {
delete process.env.BADGE_MAX_AGE_SECONDS;
const res = await fetch(`${baseUri}/testing/123.json`);
expect(res.headers.get('expires')).to.equal(res.headers.get('date'));
expect(res.headers.get('cache-control')).to.equal('no-cache, no-store, must-revalidate');
});

describe('the cache key', function () {
Expand Down
31 changes: 31 additions & 0 deletions services/time/time.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
'use strict';

const BaseService = require('../base');

module.exports = class Time extends BaseService {

async handle() {
return { message: new Date() };
}

// Metadata
static get defaultBadgeData() {
return {
label: 'time',
color: 'blue',
};
}

static get category() {
return 'debug';
}

static get url() {
return {
base: 'servertime',
format: '',
capture: []
};
}

};

0 comments on commit b9db222

Please sign in to comment.