Skip to content

Commit 54d7292

Browse files
authored
Allows immutable cache for static files in a directory (#1268)
1 parent ecd2822 commit 54d7292

File tree

4 files changed

+64
-2
lines changed

4 files changed

+64
-2
lines changed

jupyter_server/base/handlers.py

+15-2
Original file line numberDiff line numberDiff line change
@@ -942,7 +942,13 @@ def wrapper(self, *args, **kwargs):
942942

943943

944944
class FileFindHandler(JupyterHandler, web.StaticFileHandler):
945-
"""subclass of StaticFileHandler for serving files from a search path"""
945+
"""subclass of StaticFileHandler for serving files from a search path
946+
947+
The setting "static_immutable_cache" can be set up to serve some static
948+
file as immutable (e.g. file name containing a hash). The setting is a
949+
list of base URL, every static file URL starting with one of those will
950+
be immutable.
951+
"""
946952

947953
# cache search results, don't search for files more than once
948954
_static_paths: dict = {}
@@ -951,8 +957,15 @@ class FileFindHandler(JupyterHandler, web.StaticFileHandler):
951957
def set_headers(self):
952958
"""Set the headers."""
953959
super().set_headers()
960+
961+
immutable_paths = self.settings.get("static_immutable_cache", [])
962+
963+
# allow immutable cache for files
964+
if any(self.request.path.startswith(path) for path in immutable_paths):
965+
self.set_header("Cache-Control", "public, max-age=31536000, immutable")
966+
954967
# disable browser caching, rely on 304 replies for savings
955-
if "v" not in self.request.arguments or any(
968+
elif "v" not in self.request.arguments or any(
956969
self.request.path.startswith(path) for path in self.no_cache_paths
957970
):
958971
self.set_header("Cache-Control", "no-cache")

jupyter_server/serverapp.py

+14
Original file line numberDiff line numberDiff line change
@@ -1812,6 +1812,17 @@ def _default_terminals_enabled(self):
18121812
config=True,
18131813
)
18141814

1815+
static_immutable_cache = List(
1816+
Unicode(),
1817+
help="""
1818+
Paths to set up static files as immutable.
1819+
1820+
This allow setting up the cache control of static files as immutable.
1821+
It should be used for static file named with a hash for instance.
1822+
""",
1823+
config=True,
1824+
)
1825+
18151826
_starter_app = Instance(
18161827
default_value=None,
18171828
allow_none=True,
@@ -1990,6 +2001,9 @@ def init_webapp(self):
19902001
] = self.identity_provider.get_secure_cookie_kwargs
19912002
self.tornado_settings["token"] = self.identity_provider.token
19922003

2004+
if self.static_immutable_cache:
2005+
self.tornado_settings["static_immutable_cache"] = self.static_immutable_cache
2006+
19932007
# ensure default_url starts with base_url
19942008
if not self.default_url.startswith(self.base_url):
19952009
self.default_url = url_path_join(self.base_url, self.default_url)

tests/base/test_handlers.py

+25
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
APIVersionHandler,
1313
AuthenticatedFileHandler,
1414
AuthenticatedHandler,
15+
FileFindHandler,
1516
FilesRedirectHandler,
1617
JupyterHandler,
1718
RedirectWithParams,
@@ -126,3 +127,27 @@ def test_redirect_with_params(jp_serverapp):
126127
handler._transforms = []
127128
handler.get()
128129
assert handler.get_status() == 301
130+
131+
132+
async def test_static_handler(jp_serverapp, tmpdir):
133+
async def async_magic():
134+
pass
135+
136+
MagicMock.__await__ = lambda x: async_magic().__await__()
137+
138+
test_file = tmpdir / "foo"
139+
with open(test_file, "w") as fid:
140+
fid.write("hello")
141+
142+
app: ServerApp = jp_serverapp
143+
request = HTTPRequest("GET", str(test_file))
144+
request.connection = MagicMock()
145+
146+
handler = FileFindHandler(app.web_app, request, path=str(tmpdir))
147+
handler._transforms = []
148+
await handler.get("foo")
149+
assert handler._headers["Cache-Control"] == "no-cache"
150+
151+
handler.settings["static_immutable_cache"] = [str(tmpdir)]
152+
await handler.get("foo")
153+
assert handler._headers["Cache-Control"] == "public, max-age=31536000, immutable"

tests/test_serverapp.py

+10
Original file line numberDiff line numberDiff line change
@@ -561,3 +561,13 @@ def test_deprecated_notebook_dir_priority(jp_configurable_serverapp, tmp_path):
561561
cfg.ServerApp.notebook_dir = str(notebook_dir)
562562
app.update_config(cfg)
563563
assert app.root_dir == str(cli_dir)
564+
565+
566+
def test_immutable_cache_trait():
567+
# Verify we're working with a clean instance.
568+
ServerApp.clear_instance()
569+
kwargs = {"static_immutable_cache": "/test/immutable"}
570+
serverapp = ServerApp.instance(**kwargs)
571+
serverapp.init_configurables()
572+
serverapp.init_webapp()
573+
assert serverapp.web_app.settings["static_immutable_cache"] == ["/test/immutable"]

0 commit comments

Comments
 (0)