Skip to content

Commit

Permalink
implement caching strategy (ampproject#1366)
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastianbenz authored Feb 4, 2019
1 parent 3a0356c commit 2286587
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 2 deletions.
2 changes: 1 addition & 1 deletion boilerplate/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
{{> 'amp-stories.svg' }}
{{> 'amp-websites.svg' }}
{{> 'amp-ads.svg' }}
{{> 'amp-emails.svg' }}
{{> 'amp-email.svg' }}
</svg>
<amp-state id="config">
<script type="application/json">
Expand Down
4 changes: 4 additions & 0 deletions platform/lib/platform.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@

const signale = require('signale');
const express = require('express');
const compression = require('compression');
const ampCors = require('amp-toolbox-cors');
const defaultCachingStrategy = require('./utils/CachingStrategy.js').defaultStrategy;

const config = require('./config.js');
const routers = {
Expand Down Expand Up @@ -54,6 +56,8 @@ class Platform {
});
}

this.server.use(compression());
this.server.use(defaultCachingStrategy);
this._enableCors();

this._check();
Expand Down
66 changes: 66 additions & 0 deletions platform/lib/utils/CachingStrategy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* Copyright 2018 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

const mime = require('mime-types');
const ms = require('ms');
const {setMaxAge, setImmutable} = require('./cacheHelpers.js');

const IMMUTABLE = 'immutable';

const maxAgePerMimeType = [
[/text\/.+$/i, maxAge('1h')],
[/image\/.+$/i, maxAge('1d')],
[/video\/.+$/i, maxAge('1w')],
[/application\/json$/i, maxAge('1h')],
[/application\/javascript$/i, maxAge('1h')],
[/font\/.+$/i, IMMUTABLE],
];

const defaultMaxAge = maxAge('1m');

function defaultStrategy(request, response, next) {
const mimeType = mime.lookup(request.path) ||
extractMimeFromAcceptHeader(request.headers.accept);
if (!mimeType) {
setMaxAge(response, defaultMaxAge);
next();
return;
}
const maxAgeMapping = maxAgePerMimeType.find((mapping) => mapping[0].test(mimeType));
const maxAge = maxAgeMapping ? maxAgeMapping[1] : defaultMaxAge;
if (maxAge === IMMUTABLE) {
setImmutable(response);
} else {
setMaxAge(response, maxAge);
}
next();
}

function extractMimeFromAcceptHeader(string) {
if (!string) {
return '';
}
return string.split(',')[0];
}

function maxAge(string) {
return Math.floor(ms(string) / 1000);
}

module.exports = {
defaultStrategy,
};

49 changes: 49 additions & 0 deletions platform/lib/utils/cacheHelpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* Copyright 2018 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

function setNoCache(response) {
response.setHeader('Cache-Control', 'private, no-cache, no-store, must-revalidate');
}

function setMaxAge(response, maxAge) {
response.setHeader('Cache-Control', `public, max-age=${maxAge}`);
}

function setImmutable(response) {
response.setHeader('Cache-Control', `max-age=365000000, immutable`);
}

function setNoSniff(response) {
response.setHeader('x-content-type-options', 'nosniff');
}

function setHsts(response) {
response.setHeader('strict-transport-security', 'max-age=31536000; includeSubDomains; preload');
}

function setXssProtection(response) {
response.setHeader('x-xss-protection', '1; mode=block');
}

function setAmpCSP(response) {
response.setHeader('content-security-policy', `default-src * blob: data:; script-src blob: https://cdn.ampproject.org/esm/ https://cdn.ampproject.org/mp/ https://cdn.ampproject.org/rtv/ https://cdn.ampproject.org/sp/ https://cdn.ampproject.org/sw/ https://cdn.ampproject.org/v0.js https://cdn.ampproject.org/v0/ https://cdn.ampproject.org/viewer/; object-src 'none'; style-src 'unsafe-inline' https://cdn.ampproject.org/rtv/ https://cdn.materialdesignicons.com https://cloud.typography.com https://fast.fonts.net https://fonts.googleapis.com https://maxcdn.bootstrapcdn.com https://p.typekit.net https://pro.fontawesome.com https://use.fontawesome.com https://use.typekit.net; report-uri https://csp-collector.appspot.com/csp/amp`)
}

module.exports = {
setNoCache,
setMaxAge,
setImmutable,
};
3 changes: 3 additions & 0 deletions playground/backend/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
const express = require('express');
const fetch = require('node-fetch');
const URL = require('url').URL;
const {setMaxAge} = require('../../platform/lib/utils/cacheHelpers.js');
// eslint-disable-next-line new-cap
const api = express.Router();

const ONE_HOUR = 60 * 60;
const VALID_ORIGINS = new Set([
'amp.dev',
'api.amp.dev',
Expand All @@ -40,6 +42,7 @@ api.get('/fetch', async (request, response) => {
try {
const doc = await fetchDocument(url, request.headers.host);
response.send(doc);
setMaxAge(response, ONE_HOUR);
} catch (error) {
console.error('Could not fetch URL', error);
response.send(`Could not fetch URL ${url}`).status(400).end();
Expand Down
13 changes: 12 additions & 1 deletion playground/backend/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,24 @@

const path = require('path');
const express = require('express');
const {setImmutable} = require('../../platform/lib/utils/cacheHelpers.js');
// eslint-disable-next-line new-cap
const playground = express.Router();

playground.use(express.static(
path.join(__dirname, '../dist'),
{extensions: ['html']},
{
extensions: ['html'],
setHeaders: setCustomCacheControl,
},
));
playground.use('/api', require('./api.js'));

function setCustomCacheControl(response, path) {
// playground assets are versioned
if (path.endsWith('.js') || path.endsWith('.css')) {
setImmutable(response);
}
}

module.exports = playground;

0 comments on commit 2286587

Please sign in to comment.