From 1badd16d60e05da958c605b20c4c7c063b42f2c2 Mon Sep 17 00:00:00 2001 From: Eugene Mayer Date: Sat, 16 Oct 2021 20:01:03 +0300 Subject: [PATCH 1/9] =?UTF-8?q?=F0=9F=90=9B=20Fix=20FileNotFoundError=20if?= =?UTF-8?q?=20`404.html`=20and=20`index.html`=20doesn't=20exist=20in=20htm?= =?UTF-8?q?l-mode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- starlette/staticfiles.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/starlette/staticfiles.py b/starlette/staticfiles.py index 39a697260..f7057539f 100644 --- a/starlette/staticfiles.py +++ b/starlette/staticfiles.py @@ -111,20 +111,6 @@ async def get_response(self, path: str, scope: Scope) -> Response: full_path, stat_result = await anyio.to_thread.run_sync( self.lookup_path, path ) - except (FileNotFoundError, NotADirectoryError): - if self.html: - # Check for '404.html' if we're in HTML mode. - full_path, stat_result = await anyio.to_thread.run_sync( - self.lookup_path, "404.html" - ) - if stat_result and stat.S_ISREG(stat_result.st_mode): - return FileResponse( - full_path, - stat_result=stat_result, - method=scope["method"], - status_code=404, - ) - raise HTTPException(status_code=404) except PermissionError: raise HTTPException(status_code=401) except OSError: @@ -149,6 +135,18 @@ async def get_response(self, path: str, scope: Scope) -> Response: return RedirectResponse(url=url) return self.file_response(full_path, stat_result, scope) + if self.html: + # Check for '404.html' if we're in HTML mode. + full_path, stat_result = await anyio.to_thread.run_sync( + self.lookup_path, "404.html" + ) + if stat_result and stat.S_ISREG(stat_result.st_mode): + return FileResponse( + full_path, + stat_result=stat_result, + method=scope["method"], + status_code=404, + ) raise HTTPException(status_code=404) def lookup_path( @@ -161,7 +159,10 @@ def lookup_path( # Don't allow misbehaving clients to break out of the static files # directory. continue - return full_path, os.stat(full_path) + try: + return full_path, os.stat(full_path) + except (FileNotFoundError, NotADirectoryError): + continue return "", None def file_response( From 2565fc91f0e43e0dec4cfc0111d9421877040cba Mon Sep 17 00:00:00 2001 From: Eugene Mayer Date: Mon, 18 Oct 2021 15:51:46 +0300 Subject: [PATCH 2/9] =?UTF-8?q?=E2=9C=85=20Add=20staticfiles=20tests=20for?= =?UTF-8?q?=20missing=20index.html?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_staticfiles.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/tests/test_staticfiles.py b/tests/test_staticfiles.py index 48fdaf1a5..f4d25f647 100644 --- a/tests/test_staticfiles.py +++ b/tests/test_staticfiles.py @@ -229,7 +229,7 @@ def test_staticfiles_304_with_last_modified_compare_last_req( assert response.content == b"" -def test_staticfiles_html(tmpdir, test_client_factory): +def test_staticfiles_html_normal(tmpdir, test_client_factory): path = os.path.join(tmpdir, "404.html") with open(path, "w") as file: file.write("

Custom not found page

") @@ -262,6 +262,31 @@ def test_staticfiles_html(tmpdir, test_client_factory): assert response.text == "

Custom not found page

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

Custom not found page

") + path = os.path.join(tmpdir, "dir") + os.mkdir(path) + + app = StaticFiles(directory=tmpdir, html=True) + client = test_client_factory(app) + + response = client.get("/dir/") + assert response.url == "http://testserver/dir/" + assert response.status_code == 404 + assert response.text == "

Custom not found page

" + + response = client.get("/dir") + assert response.url == "http://testserver/dir/" + assert response.status_code == 404 + assert response.text == "

Custom not found page

" + + 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, test_client_factory ): From ccf3d2b3fb4464c864544cc29a293f625e773f74 Mon Sep 17 00:00:00 2001 From: Eugene Mayer Date: Mon, 18 Oct 2021 15:55:30 +0300 Subject: [PATCH 3/9] =?UTF-8?q?=E2=9C=85=20Add=20staticfiles=20tests=20for?= =?UTF-8?q?=20missing=20404.html?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_staticfiles.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/test_staticfiles.py b/tests/test_staticfiles.py index f4d25f647..96a129262 100644 --- a/tests/test_staticfiles.py +++ b/tests/test_staticfiles.py @@ -287,6 +287,31 @@ def test_staticfiles_html_without_index(tmpdir, test_client_factory): assert response.text == "

Custom not found page

" +def test_staticfiles_html_without_404(tmpdir, test_client_factory): + path = os.path.join(tmpdir, "dir") + os.mkdir(path) + path = os.path.join(path, "index.html") + with open(path, "w") as file: + file.write("

Hello

") + + app = StaticFiles(directory=tmpdir, html=True) + client = test_client_factory(app) + + response = client.get("/dir/") + assert response.url == "http://testserver/dir/" + assert response.status_code == 200 + assert response.text == "

Hello

" + + response = client.get("/dir") + assert response.url == "http://testserver/dir/" + assert response.status_code == 200 + assert response.text == "

Hello

" + + response = client.get("/missing") + assert response.status_code == 404 + assert response.text == "Not Found" + + def test_staticfiles_cache_invalidation_for_deleted_file_html_mode( tmpdir, test_client_factory ): From cc56d6d0f3e66381c72cd95f33f97dfa15c74fe9 Mon Sep 17 00:00:00 2001 From: Eugene Mayer Date: Mon, 18 Oct 2021 15:59:45 +0300 Subject: [PATCH 4/9] =?UTF-8?q?=E2=9C=85=20Add=20staticfiles=20tests=20for?= =?UTF-8?q?=20missing=20index-=20and=20404.html?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_staticfiles.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/test_staticfiles.py b/tests/test_staticfiles.py index 96a129262..5e716c206 100644 --- a/tests/test_staticfiles.py +++ b/tests/test_staticfiles.py @@ -312,6 +312,28 @@ def test_staticfiles_html_without_404(tmpdir, test_client_factory): assert response.text == "Not Found" +def test_staticfiles_html_only_files(tmpdir, test_client_factory): + path = os.path.join(tmpdir, "hello.html") + with open(path, "w") as file: + file.write("

Hello

") + + app = StaticFiles(directory=tmpdir, html=True) + client = test_client_factory(app) + + response = client.get("/dir") + assert response.url == "http://testserver/" + assert response.status_code == 404 + assert response.text == "Not Found" + + response = client.get("/hello.html") + assert response.status_code == 200 + assert response.text == "

Hello

" + + response = client.get("/missing") + assert response.status_code == 404 + assert response.text == "Not Found" + + def test_staticfiles_cache_invalidation_for_deleted_file_html_mode( tmpdir, test_client_factory ): From b564f1fb04ac3898dbed7e94ad84d0238190b26d Mon Sep 17 00:00:00 2001 From: Eugene Mayer Date: Mon, 18 Oct 2021 16:13:27 +0300 Subject: [PATCH 5/9] =?UTF-8?q?=F0=9F=92=A9=20Bad=20fix:=20if=20404.html?= =?UTF-8?q?=20not=20found,=20returned=20url=20without=20`/`=20at=20the=20e?= =?UTF-8?q?nd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_staticfiles.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/test_staticfiles.py b/tests/test_staticfiles.py index 5e716c206..bac783f58 100644 --- a/tests/test_staticfiles.py +++ b/tests/test_staticfiles.py @@ -273,12 +273,10 @@ def test_staticfiles_html_without_index(tmpdir, test_client_factory): client = test_client_factory(app) response = client.get("/dir/") - assert response.url == "http://testserver/dir/" assert response.status_code == 404 assert response.text == "

Custom not found page

" response = client.get("/dir") - assert response.url == "http://testserver/dir/" assert response.status_code == 404 assert response.text == "

Custom not found page

" @@ -298,12 +296,10 @@ def test_staticfiles_html_without_404(tmpdir, test_client_factory): client = test_client_factory(app) response = client.get("/dir/") - assert response.url == "http://testserver/dir/" assert response.status_code == 200 assert response.text == "

Hello

" response = client.get("/dir") - assert response.url == "http://testserver/dir/" assert response.status_code == 200 assert response.text == "

Hello

" @@ -321,7 +317,6 @@ def test_staticfiles_html_only_files(tmpdir, test_client_factory): client = test_client_factory(app) response = client.get("/dir") - assert response.url == "http://testserver/" assert response.status_code == 404 assert response.text == "Not Found" From 6af261f395def893d501df31a74017f590dad090 Mon Sep 17 00:00:00 2001 From: Eugene Mayer Date: Mon, 18 Oct 2021 17:02:21 +0300 Subject: [PATCH 6/9] =?UTF-8?q?=E2=9C=85=20Fix=20HTTPException=20errors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_staticfiles.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/tests/test_staticfiles.py b/tests/test_staticfiles.py index bac783f58..07c855ebc 100644 --- a/tests/test_staticfiles.py +++ b/tests/test_staticfiles.py @@ -303,9 +303,8 @@ def test_staticfiles_html_without_404(tmpdir, test_client_factory): assert response.status_code == 200 assert response.text == "

Hello

" - response = client.get("/missing") - assert response.status_code == 404 - assert response.text == "Not Found" + with pytest.raises(HTTPException) as exc_info: + response = client.get("/missing") def test_staticfiles_html_only_files(tmpdir, test_client_factory): @@ -316,18 +315,13 @@ def test_staticfiles_html_only_files(tmpdir, test_client_factory): app = StaticFiles(directory=tmpdir, html=True) client = test_client_factory(app) - response = client.get("/dir") - assert response.status_code == 404 - assert response.text == "Not Found" + with pytest.raises(HTTPException) as exc_info: + response = client.get("/") response = client.get("/hello.html") assert response.status_code == 200 assert response.text == "

Hello

" - response = client.get("/missing") - assert response.status_code == 404 - assert response.text == "Not Found" - def test_staticfiles_cache_invalidation_for_deleted_file_html_mode( tmpdir, test_client_factory From 21edbc8db80af22bd6858b138c39265de436a3da Mon Sep 17 00:00:00 2001 From: Eugene Mayer Date: Mon, 18 Oct 2021 17:06:49 +0300 Subject: [PATCH 7/9] =?UTF-8?q?=E2=9C=85=20Fix=20`/dir/`=20and=20`/dir`=20?= =?UTF-8?q?asserts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_staticfiles.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_staticfiles.py b/tests/test_staticfiles.py index 07c855ebc..adb3ed3f4 100644 --- a/tests/test_staticfiles.py +++ b/tests/test_staticfiles.py @@ -273,10 +273,12 @@ def test_staticfiles_html_without_index(tmpdir, test_client_factory): client = test_client_factory(app) response = client.get("/dir/") + assert response.url == "http://testserver/dir/" assert response.status_code == 404 assert response.text == "

Custom not found page

" response = client.get("/dir") + assert response.url == "http://testserver/dir/" assert response.status_code == 404 assert response.text == "

Custom not found page

" @@ -296,10 +298,12 @@ def test_staticfiles_html_without_404(tmpdir, test_client_factory): client = test_client_factory(app) response = client.get("/dir/") + assert response.url == "http://testserver/dir/" assert response.status_code == 200 assert response.text == "

Hello

" response = client.get("/dir") + assert response.url == "http://testserver/dir/" assert response.status_code == 200 assert response.text == "

Hello

" From 5ce62f8cbc6ff3ec657ac45affa98fb1b4e0fc2e Mon Sep 17 00:00:00 2001 From: Eugene Mayer Date: Mon, 18 Oct 2021 17:06:49 +0300 Subject: [PATCH 8/9] =?UTF-8?q?=E2=9C=85=20Fix=20`/dir/`=20and=20`/dir`=20?= =?UTF-8?q?asserts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_staticfiles.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_staticfiles.py b/tests/test_staticfiles.py index 07c855ebc..adb3ed3f4 100644 --- a/tests/test_staticfiles.py +++ b/tests/test_staticfiles.py @@ -273,10 +273,12 @@ def test_staticfiles_html_without_index(tmpdir, test_client_factory): client = test_client_factory(app) response = client.get("/dir/") + assert response.url == "http://testserver/dir/" assert response.status_code == 404 assert response.text == "

Custom not found page

" response = client.get("/dir") + assert response.url == "http://testserver/dir/" assert response.status_code == 404 assert response.text == "

Custom not found page

" @@ -296,10 +298,12 @@ def test_staticfiles_html_without_404(tmpdir, test_client_factory): client = test_client_factory(app) response = client.get("/dir/") + assert response.url == "http://testserver/dir/" assert response.status_code == 200 assert response.text == "

Hello

" response = client.get("/dir") + assert response.url == "http://testserver/dir/" assert response.status_code == 200 assert response.text == "

Hello

" From bcd0546598c0bfcc1102eba92860d4026b36fd78 Mon Sep 17 00:00:00 2001 From: Eugene Mayer Date: Mon, 18 Oct 2021 17:21:10 +0300 Subject: [PATCH 9/9] =?UTF-8?q?=F0=9F=92=9A=20Fix=20unused=20variable=20an?= =?UTF-8?q?d=20add=20status=5Fcode=20check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_staticfiles.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_staticfiles.py b/tests/test_staticfiles.py index adb3ed3f4..8057af689 100644 --- a/tests/test_staticfiles.py +++ b/tests/test_staticfiles.py @@ -278,7 +278,7 @@ def test_staticfiles_html_without_index(tmpdir, test_client_factory): assert response.text == "

Custom not found page

" response = client.get("/dir") - assert response.url == "http://testserver/dir/" + assert response.url == "http://testserver/dir" assert response.status_code == 404 assert response.text == "

Custom not found page

" @@ -309,6 +309,7 @@ def test_staticfiles_html_without_404(tmpdir, test_client_factory): with pytest.raises(HTTPException) as exc_info: response = client.get("/missing") + assert exc_info.value.status_code == 404 def test_staticfiles_html_only_files(tmpdir, test_client_factory): @@ -321,6 +322,7 @@ def test_staticfiles_html_only_files(tmpdir, test_client_factory): with pytest.raises(HTTPException) as exc_info: response = client.get("/") + assert exc_info.value.status_code == 404 response = client.get("/hello.html") assert response.status_code == 200