diff --git a/tensorboard/plugins/core/core_plugin.py b/tensorboard/plugins/core/core_plugin.py index 4eeaf7acb63..629b6b64673 100644 --- a/tensorboard/plugins/core/core_plugin.py +++ b/tensorboard/plugins/core/core_plugin.py @@ -39,6 +39,11 @@ # If no port is specified, try to bind to this port. See help for --port # for more details. DEFAULT_PORT = 6006 +# Valid javascript mimetypes that we have seen configured, in practice. +# Historically (~2020-2022) we saw "application/javascript" exclusively but with +# RFC 9239 (https://www.rfc-editor.org/rfc/rfc9239) we saw some systems quickly +# transition to 'text/javascript'. +JS_MIMETYPES = ["text/javascript", "application/javascript"] JS_CACHE_EXPIRATION_IN_SECS = 86400 @@ -133,8 +138,7 @@ def _serve_asset(self, path, gzipped_asset_bytes, request): # Cache JS resources while keep others do not cache. expires = ( JS_CACHE_EXPIRATION_IN_SECS - if request.args.get("_file_hash") - and mimetype == "application/javascript" + if request.args.get("_file_hash") and mimetype in JS_MIMETYPES else 0 ) diff --git a/tensorboard/plugins/core/core_plugin_test.py b/tensorboard/plugins/core/core_plugin_test.py index c2c371d5559..7c63642f9fd 100644 --- a/tensorboard/plugins/core/core_plugin_test.py +++ b/tensorboard/plugins/core/core_plugin_test.py @@ -19,6 +19,7 @@ import contextlib import io import json +import mimetypes import os from unittest import mock import zipfile @@ -150,6 +151,9 @@ def setUp(self): app = application.TensorBoardWSGI([self.plugin]) self.server = werkzeug_test.Client(app, wrappers.Response) + def tearDown(self): + mimetypes.init() + def _add_run(self, run_name): run_path = os.path.join(self.logdir, run_name) with test_util.FileWriter(run_path) as writer: @@ -191,6 +195,34 @@ def test_js_no_cache(self): ) def test_js_cache(self): + """Tests cache header for js files marked for caching. + + The test relies on local system's defaults for the javascript mimetype + which, in practice, should be one of 'text/javascript' or + 'application/javascript'. It could be either, depending on the system. + + See test_js_cache_with_text_javascript() and + test_js_cache_with_application_javascript() for test cases where + the javascript mimetype have been explicitly configured. + """ + response = self.server.get("/index.js?_file_hash=meow") + self.assertEqual(200, response.status_code) + self.assertEqual( + ONE_DAY_CACHE_CONTROL_VALUE, response.headers.get("Cache-Control") + ) + + def test_js_cache_with_text_javascript(self): + """Tests cache header when js mimetype is 'text/javascript'.""" + mimetypes.add_type("text/javascript", ".js") + response = self.server.get("/index.js?_file_hash=meow") + self.assertEqual(200, response.status_code) + self.assertEqual( + ONE_DAY_CACHE_CONTROL_VALUE, response.headers.get("Cache-Control") + ) + + def test_js_cache_with_application_javascript(self): + """Tests cache header when js mimetype is 'application/javascript'.""" + mimetypes.add_type("application/javascript", ".js") response = self.server.get("/index.js?_file_hash=meow") self.assertEqual(200, response.status_code) self.assertEqual(