Unverified Commit 183a653e authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] adjust index.html caching and insert cache busting URL in to...

[flutter_tools] adjust index.html caching and insert cache busting URL in to main.dart.js/sw request (#64976)

Attempt to bypass aggressive CDN by requesting main.dart.js/sw.js with a query param and defaulting index.html to online first. This will not stop aggressive CDN caching of the index.html

#64968
parent 66b01c1f
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:math';
import 'package:crypto/crypto.dart';
import 'package:package_config/package_config.dart';
......@@ -298,8 +300,25 @@ class WebReleaseBundle extends Target {
if (!outputFile.parent.existsSync()) {
outputFile.parent.createSync(recursive: true);
}
inputFile.copySync(outputFile.path);
outputResourcesFiles.add(outputFile);
// insert a random hash into the requests for main.dart.js and service_worker.js. This is
// not a content hash, because it would need to be the hash for the entire bundle and not
// just the resource in question.
if (environment.fileSystem.path.basename(inputFile.path) == 'index.html') {
final String randomHash = Random().nextInt(4294967296).toString();
final String resultString = inputFile.readAsStringSync()
.replaceFirst(
'<script src="main.dart.js" type="application/javascript"></script>',
'<script src="main.dart.js?v=$randomHash" type="application/javascript"></script>'
)
.replaceFirst(
"navigator.serviceWorker.register('flutter_service_worker.js')",
"navigator.serviceWorker.register('flutter_service_worker.js?v=$randomHash')",
);
outputFile.writeAsStringSync(resultString);
continue;
}
inputFile.copySync(outputFile.path);
}
final Depfile resourceFile = Depfile(inputResourceFiles, outputResourcesFiles);
depfileService.writeToFile(
......@@ -484,21 +503,26 @@ self.addEventListener("fetch", (event) => {
var origin = self.location.origin;
var key = event.request.url.substring(origin.length + 1);
// Redirect URLs to the index.html
if (event.request.url == origin || event.request.url.startsWith(origin + '/#')) {
if (key.indexOf('?v=') != -1) {
key = key.split('?v=')[0];
}
if (event.request.url == origin || event.request.url.startsWith(origin + '/#') || key == '') {
key = '/';
}
// If the URL is not the RESOURCE list, skip the cache.
if (!RESOURCES[key]) {
return event.respondWith(fetch(event.request));
}
// If the URL is the index.html, perform an online-first request.
if (key == '/') {
return onlineFirst(event);
}
event.respondWith(caches.open(CACHE_NAME)
.then((cache) => {
return cache.match(event.request).then((response) => {
// Either respond with the cached resource, or perform a fetch and
// lazily populate the cache. Ensure the resources are not cached
// by the browser for longer than the service worker expects.
var modifiedRequest = new Request(event.request, {'cache': 'reload'});
return response || fetch(modifiedRequest).then((response) => {
// lazily populate the cache.
return response || fetch(event.request).then((response) => {
cache.put(event.request, response.clone());
return response;
});
......@@ -539,5 +563,27 @@ async function downloadOffline() {
}
return contentCache.addAll(resources);
}
// Attempt to download the resource online before falling back to
// the offline cache.
function onlineFirst(event) {
return event.respondWith(
fetch(event.request).then((response) => {
return caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, response.clone());
return response;
});
}).catch((error) => {
return caches.open(CACHE_NAME).then((cache) => {
return cache.match(event.request).then((response) => {
if (response != null) {
return response;
}
throw error;
});
});
})
);
}
''';
}
......@@ -96,7 +96,15 @@ void main() {
environment.defines[kBuildMode] = 'release';
final Directory webResources = environment.projectDir.childDirectory('web');
webResources.childFile('index.html')
.createSync(recursive: true);
..createSync(recursive: true)
..writeAsStringSync('''
<html>
<script src="main.dart.js" type="application/javascript"></script>
<script>
navigator.serviceWorker.register('flutter_service_worker.js');
</script>
</html>
''');
webResources.childFile('foo.txt')
.writeAsStringSync('A');
environment.buildDir.childFile('main.dart.js').createSync();
......@@ -117,6 +125,11 @@ void main() {
expect(environment.outputDir.childFile('foo.txt')
.readAsStringSync(), 'B');
// Appends number to requests for service worker and main.dart.js
expect(environment.outputDir.childFile('index.html').readAsStringSync(), allOf(
contains('<script src="main.dart.js?v='),
contains('flutter_service_worker.js?v='),
));
}));
test('WebEntrypointTarget generates an entrypoint for a file outside of main', () => testbed.run(() async {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment