Skip to content

Commit 3e92be0

Browse files
committed
Add way for us to respond with clear-site-data: "cache"
1 parent abde724 commit 3e92be0

File tree

4 files changed

+80
-2
lines changed

4 files changed

+80
-2
lines changed

src/cookies.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* @param {import("express").Request} req
3+
* @returns {Map<string, string>}
4+
*/
5+
const parseCookies = (req) => {
6+
const header = req.header('cookie');
7+
if (typeof header === 'undefined') {
8+
return new Map();
9+
}
10+
11+
if (header.length > 1000) {
12+
return new Map();
13+
}
14+
15+
// maybe not fully spec compliant, but it's good enough for what we use this for
16+
const cookies = new Map();
17+
for (const part of header.split(';')) {
18+
const equalsIndex = part.indexOf('=');
19+
if (equalsIndex !== -1) {
20+
const name = part.substring(0, equalsIndex).trim();
21+
const value = part.substring(equalsIndex + 1).trim();
22+
cookies.set(name, value);
23+
}
24+
}
25+
return cookies;
26+
};
27+
28+
module.exports = parseCookies;

src/server.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const logger = require('./logger');
1313
const environment = require('./environment');
1414
const isSpider = require('./spider');
1515
const ScratchAPI = require('./scratch-api');
16+
const parseCookies = require('./cookies');
1617

1718
const notFoundFile = fs.readFileSync(path.join(__dirname, '404.html'));
1819
const userPageFile = fs.readFileSync(path.join(__dirname, 'userpage.html'));
@@ -128,6 +129,12 @@ app.use((req, res, next) => {
128129
return;
129130
}
130131

132+
const cookies = parseCookies(req);
133+
if (cookies.get('tw_clear_cache_once') === '1') {
134+
res.header('Clear-Site-Data', '"cache"');
135+
res.header('Set-Cookie', 'tw_clear_cache_once=; max-age=0; path=/; samesite=strict; secure');
136+
}
137+
131138
const host = hosts[hostname];
132139
const branches = host.branches;
133140
const path = req.path;

test/clear-cache.test.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
const supertest = require('supertest');
2+
const app = require('../src/server');
3+
const request = supertest(app);
4+
5+
it('does not serve clear-site-data by default', async () => {
6+
const data = await request.get('/').set('Host', 'localhost');
7+
expect(data.headers['clear-site-data']).toBeUndefined();
8+
});
9+
10+
it('servers clear-site-data for cookie', async () => {
11+
await request.get('/')
12+
.set('Host', 'localhost')
13+
.set('Cookie', 'tw_clear_cache_once=1')
14+
.expect('clear-site-data', '"cache"')
15+
.expect('set-cookie', /tw_clear_cache_once=;/);
16+
});
17+
18+
it('servers clear-site-data for slightly strange cookie', async () => {
19+
await request.get('/')
20+
.set('Host', 'localhost')
21+
.set('Cookie', 'a = b ; tw_clear_cache_once = 1 ; c= d')
22+
.expect('clear-site-data', '"cache"')
23+
.expect('set-cookie', /tw_clear_cache_once=;/);
24+
});

www/index.html

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1-
index.html (no encoding)
21
<head></head>
3-
<script src="js/test.js"></script>
2+
<body>
3+
<p>
4+
index.html (no encoding)
5+
</p>
6+
7+
<p>
8+
<button onclick="hardRefresh()">Activate template.ejs-style hardRefresh()</button>
9+
</p>
10+
<script>
11+
function hardRefresh() {
12+
var search = location.search.replace(/[?&]nocache=[\d.]+(?=$|&)/, '');
13+
// This is a functional cookie used so that we respond with Clear-Site-Data just once. No tracking.
14+
// Can't use nocache parameter to trigger this because people might accidentally bookmark that, and
15+
// then we would be clearing their cache on every visit.
16+
document.cookie = 'tw_clear_cache_once=1; max-age=60; path=/; samesite=strict; secure';
17+
location.replace(location.pathname + search + (search ? '&' : '?') + 'nocache=' + Math.floor(Math.random() * 10000000));
18+
}
19+
</script>
20+
21+
<script src="js/test.js"></script>
22+
</body>

0 commit comments

Comments
 (0)