From d83925dbc9dfba6aed860157fa94cbc31cc4ddb2 Mon Sep 17 00:00:00 2001 From: Ilya Lunev Date: Sun, 9 Aug 2020 17:21:53 +0300 Subject: [PATCH 1/2] StaticFiles: Fix cache validation bug for deleted files in html mode Previously StaticFiles would return 304 for a deleted file if its Last-Modified date was the same as that of 404.html --- starlette/staticfiles.py | 5 +++-- tests/test_staticfiles.py | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/starlette/staticfiles.py b/starlette/staticfiles.py index 41df98062..db988e75c 100644 --- a/starlette/staticfiles.py +++ b/starlette/staticfiles.py @@ -134,8 +134,9 @@ async def get_response(self, path: str, scope: Scope) -> Response: # Check for '404.html' if we're in HTML mode. full_path, stat_result = await self.lookup_path("404.html") if stat_result is not None and stat.S_ISREG(stat_result.st_mode): - return self.file_response( - full_path, stat_result, scope, status_code=404 + return FileResponse( + full_path, stat_result=stat_result, + method=scope["method"], status_code=404 ) return PlainTextResponse("Not Found", status_code=404) diff --git a/tests/test_staticfiles.py b/tests/test_staticfiles.py index e2cae08f1..dc89c16e2 100644 --- a/tests/test_staticfiles.py +++ b/tests/test_staticfiles.py @@ -245,3 +245,40 @@ def test_staticfiles_html(tmpdir): response = client.get("/missing") assert response.status_code == 404 assert response.text == "

Custom not found page

" + + +def test_staticfiles_cache_invalidation_for_deleted_file_html_mode(tmpdir): + path_404 = os.path.join(tmpdir, "404.html") + with open(path_404, "w") as file: + file.write("

404 file

") + path_some = os.path.join(tmpdir, "some.html") + with open(path_some, "w") as file: + file.write("

some file

") + + common_modified_time = time.mktime( + time.strptime("2013-10-10 23:40:00", "%Y-%m-%d %H:%M:%S") + ) + os.utime(path_404, (common_modified_time, common_modified_time)) + os.utime(path_some, (common_modified_time, common_modified_time)) + + app = StaticFiles(directory=tmpdir, html=True) + client = TestClient(app) + + resp_exists = client.get("/some.html") + assert resp_exists.status_code == 200 + assert resp_exists.text == "

some file

" + + resp_cached = client.get( + "/some.html", + headers={"If-Modified-Since": resp_exists.headers["last-modified"]} + ) + assert resp_cached.status_code == 304 + + os.remove(path_some) + + resp_deleted = client.get( + "/some.html", + headers={"If-Modified-Since": resp_exists.headers["last-modified"]} + ) + assert resp_deleted.status_code == 404 + assert resp_deleted.text == "

404 file

" From 868805a39583e95e7624b8ab070cc53b83bf7825 Mon Sep 17 00:00:00 2001 From: Ilya Lunev Date: Sun, 9 Aug 2020 18:57:00 +0300 Subject: [PATCH 2/2] Use black formatter --- starlette/staticfiles.py | 6 ++++-- tests/test_staticfiles.py | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/starlette/staticfiles.py b/starlette/staticfiles.py index db988e75c..a707b5ec3 100644 --- a/starlette/staticfiles.py +++ b/starlette/staticfiles.py @@ -135,8 +135,10 @@ async def get_response(self, path: str, scope: Scope) -> Response: full_path, stat_result = await self.lookup_path("404.html") if stat_result is not None and stat.S_ISREG(stat_result.st_mode): return FileResponse( - full_path, stat_result=stat_result, - method=scope["method"], status_code=404 + full_path, + stat_result=stat_result, + method=scope["method"], + status_code=404, ) return PlainTextResponse("Not Found", status_code=404) diff --git a/tests/test_staticfiles.py b/tests/test_staticfiles.py index dc89c16e2..9de2ba63c 100644 --- a/tests/test_staticfiles.py +++ b/tests/test_staticfiles.py @@ -270,7 +270,7 @@ def test_staticfiles_cache_invalidation_for_deleted_file_html_mode(tmpdir): resp_cached = client.get( "/some.html", - headers={"If-Modified-Since": resp_exists.headers["last-modified"]} + headers={"If-Modified-Since": resp_exists.headers["last-modified"]}, ) assert resp_cached.status_code == 304 @@ -278,7 +278,7 @@ def test_staticfiles_cache_invalidation_for_deleted_file_html_mode(tmpdir): resp_deleted = client.get( "/some.html", - headers={"If-Modified-Since": resp_exists.headers["last-modified"]} + headers={"If-Modified-Since": resp_exists.headers["last-modified"]}, ) assert resp_deleted.status_code == 404 assert resp_deleted.text == "

404 file

"