openvibers/openvibe.vip Commits
Take VIP cache headers from openvibe-shared/cache-policy (#3)
Changes
@@@ -19,6 +19,7 @@ const path = require('path');const express = require('express');const cookieParser = require('cookie-parser');const { http } = require('openvibe-contracts');++const cache = require('openvibe-shared/cache-policy');const { loadConfig } = require('./config');const { openDb } = require('./db');const { createKeyProvider, createUserAuth } = require('./network');@@@ -112,7 +113,7 @@ async function createApp(opts = {}) {setHeaders(res, filePath) {const rel = path.relative(PUBLIC_DIR, filePath).split(path.sep).join('/');const v = res.req && res.req.query && res.req.query.v;-- res.setHeader('Cache-Control', v && v === assetVersion(rel) ? 'public, max-age=31536000, immutable' : 'no-cache');++ res.setHeader('Cache-Control', cache.assetHeaders(rel, { hashed: v && v === assetVersion(rel) }));},}));app.use(createWebRoutes({ domain, config, layout, userAuth }));@@@ -21,6 +21,7 @@*/const crypto = require('crypto');const express = require('express');++const cache = require('openvibe-shared/cache-policy');const { esc } = require('../util');const USERNAME_RE = /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,39}$/;@@@ -120,10 +121,10 @@ function createEmbedRoutes({ domain, config }) {});res.removeHeader('X-Frame-Options');if (!found) {-- return res.status(404).set('Cache-Control', 'public, max-age=60').type('html')++ return res.status(404).set('Cache-Control', cache.htmlHeaders({ maxAge: 60 })).type('html').send(`<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="robots" content="noindex"><title>OpenVibe.VIP</title><style>${STYLES[theme]}</style></head><body><main class="w"><p class="k">OpenVibe.VIP</p><p class="n">No memberships to show.</p></main></body></html>\n`);}-- return res.status(200).set('Cache-Control', 'public, max-age=60').type('html').send(widgetHtml({ card: found.card, theme }));++ return res.status(200).set('Cache-Control', cache.htmlHeaders({ maxAge: 60 })).type('html').send(widgetHtml({ card: found.card, theme }));} catch (e) { return next(e); }});nnew file mode 100644@@@ -0,0 +1,50 @@++'use strict';++/**++ * One cache rule for every header (openvibe-shared/cache-policy, plan T11 lane D): a static asset++ * served with the version it was built at (`?v=<hash>`) is immutable for a year; one served without++ * a matching ?v= gets the short public window with stale-while-revalidate. An HTML page takes the++ * HTML policy, and an API/JSON response keeps its own value.++ */++const assert = require('assert');++const { boot, harness } = require('./helpers/app');++const { assetVersion } = require('../server/web/layout');++++const { test, run } = harness('asset-cache');++++(async () => {++ const t = await boot();++ const get = async (p) => {++ const r = await fetch(t.base + p, { redirect: 'manual' });++ return { status: r.status, headers: r.headers, text: await r.text() };++ };++++ test('a static asset under its current ?v= is immutable for a year', async () => {++ const v = assetVersion('css/vip.css');++ const r = await get(`/css/vip.css?v=${v}`);++ assert.strictEqual(r.status, 200, r.text);++ assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=31536000, immutable');++ });++++ test('a static asset with a wrong or missing ?v= gets the short public window', async () => {++ const wrong = await get('/css/vip.css?v=deadbeefdeadbeef');++ assert.strictEqual(wrong.status, 200, wrong.text);++ assert.strictEqual(wrong.headers.get('cache-control'), 'public, max-age=300, stale-while-revalidate=86400');++ const none = await get('/css/vip.css');++ assert.strictEqual(none.status, 200, none.text);++ assert.strictEqual(none.headers.get('cache-control'), 'public, max-age=300, stale-while-revalidate=86400');++ });++++ test('an HTML widget takes the HTML policy', async () => {++ const r = await get('/embed/nobody/widget');++ assert.strictEqual(r.status, 404, r.text);++ assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=60, stale-while-revalidate=3600');++ });++++ test('card.json stays a public API window', async () => {++ const r = await get('/embed/nobody/card.json');++ assert.strictEqual(r.status, 404, r.text);++ assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=60');++ });++++ await run();++})().catch((e) => { console.error(e); process.exit(1); });
@@ -19,6 +19,7 @@ const path = require('path');
@@ -19,6 +19,7 @@ const path = require('path');
const express = require('express');
const express = require('express');
const cookieParser = require('cookie-parser');
const cookieParser = require('cookie-parser');
const { http } = require('openvibe-contracts');
const { http } = require('openvibe-contracts');
+const cache = require('openvibe-shared/cache-policy');
const { loadConfig } = require('./config');
const { loadConfig } = require('./config');
const { openDb } = require('./db');
const { openDb } = require('./db');
const { createKeyProvider, createUserAuth } = require('./network');
const { createKeyProvider, createUserAuth } = require('./network');
@@ -112,7 +113,7 @@ async function createApp(opts = {}) {
@@ -112,7 +113,7 @@ async function createApp(opts = {}) {
setHeaders(res, filePath) {
setHeaders(res, filePath) {
const rel = path.relative(PUBLIC_DIR, filePath).split(path.sep).join('/');
const rel = path.relative(PUBLIC_DIR, filePath).split(path.sep).join('/');
const v = res.req && res.req.query && res.req.query.v;
const v = res.req && res.req.query && res.req.query.v;
- res.setHeader('Cache-Control', v && v === assetVersion(rel) ? 'public, max-age=31536000, immutable' : 'no-cache');
+ res.setHeader('Cache-Control', cache.assetHeaders(rel, { hashed: v && v === assetVersion(rel) }));
},
},
}));
}));
app.use(createWebRoutes({ domain, config, layout, userAuth }));
app.use(createWebRoutes({ domain, config, layout, userAuth }));
@@ -21,6 +21,7 @@
@@ -21,6 +21,7 @@
*/
*/
const crypto = require('crypto');
const crypto = require('crypto');
const express = require('express');
const express = require('express');
+const cache = require('openvibe-shared/cache-policy');
const { esc } = require('../util');
const { esc } = require('../util');
const USERNAME_RE = /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,39}$/;
const USERNAME_RE = /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,39}$/;
@@ -120,10 +121,10 @@ function createEmbedRoutes({ domain, config }) {
@@ -120,10 +121,10 @@ function createEmbedRoutes({ domain, config }) {
});
});
res.removeHeader('X-Frame-Options');
res.removeHeader('X-Frame-Options');
if (!found) {
if (!found) {
- return res.status(404).set('Cache-Control', 'public, max-age=60').type('html')
+ return res.status(404).set('Cache-Control', cache.htmlHeaders({ maxAge: 60 })).type('html')
.send(`<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="robots" content="noindex"><title>OpenVibe.VIP</title><style>${STYLES[theme]}</style></head><body><main class="w"><p class="k">OpenVibe.VIP</p><p class="n">No memberships to show.</p></main></body></html>\n`);
.send(`<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="robots" content="noindex"><title>OpenVibe.VIP</title><style>${STYLES[theme]}</style></head><body><main class="w"><p class="k">OpenVibe.VIP</p><p class="n">No memberships to show.</p></main></body></html>\n`);
}
}
- return res.status(200).set('Cache-Control', 'public, max-age=60').type('html').send(widgetHtml({ card: found.card, theme }));
+ return res.status(200).set('Cache-Control', cache.htmlHeaders({ maxAge: 60 })).type('html').send(widgetHtml({ card: found.card, theme }));
} catch (e) { return next(e); }
} catch (e) { return next(e); }
});
});
new file mode 100644
new file mode 100644
@@ -0,0 +1,50 @@
@@ -0,0 +1,50 @@
+'use strict';
+/**
+ * One cache rule for every header (openvibe-shared/cache-policy, plan T11 lane D): a static asset
+ * served with the version it was built at (`?v=<hash>`) is immutable for a year; one served without
+ * a matching ?v= gets the short public window with stale-while-revalidate. An HTML page takes the
+ * HTML policy, and an API/JSON response keeps its own value.
+ */
+const assert = require('assert');
+const { boot, harness } = require('./helpers/app');
+const { assetVersion } = require('../server/web/layout');
+
+const { test, run } = harness('asset-cache');
+
+(async () => {
+ const t = await boot();
+ const get = async (p) => {
+ const r = await fetch(t.base + p, { redirect: 'manual' });
+ return { status: r.status, headers: r.headers, text: await r.text() };
+ };
+
+ test('a static asset under its current ?v= is immutable for a year', async () => {
+ const v = assetVersion('css/vip.css');
+ const r = await get(`/css/vip.css?v=${v}`);
+ assert.strictEqual(r.status, 200, r.text);
+ assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=31536000, immutable');
+ });
+
+ test('a static asset with a wrong or missing ?v= gets the short public window', async () => {
+ const wrong = await get('/css/vip.css?v=deadbeefdeadbeef');
+ assert.strictEqual(wrong.status, 200, wrong.text);
+ assert.strictEqual(wrong.headers.get('cache-control'), 'public, max-age=300, stale-while-revalidate=86400');
+ const none = await get('/css/vip.css');
+ assert.strictEqual(none.status, 200, none.text);
+ assert.strictEqual(none.headers.get('cache-control'), 'public, max-age=300, stale-while-revalidate=86400');
+ });
+
+ test('an HTML widget takes the HTML policy', async () => {
+ const r = await get('/embed/nobody/widget');
+ assert.strictEqual(r.status, 404, r.text);
+ assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=60, stale-while-revalidate=3600');
+ });
+
+ test('card.json stays a public API window', async () => {
+ const r = await get('/embed/nobody/card.json');
+ assert.strictEqual(r.status, 404, r.text);
+ assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=60');
+ });
+
+ await run();
+})().catch((e) => { console.error(e); process.exit(1); });