diff --git a/responder/api.py b/responder/api.py index ac34de2..59bce2d 100644 --- a/responder/api.py +++ b/responder/api.py @@ -76,7 +76,7 @@ class API: self.version = version self.openapi_version = openapi self.static_dir = Path(os.path.abspath(static_dir)) - self.static_route = static_route + self.static_route = f"/{static_route.strip('/')}" self.templates_dir = Path(os.path.abspath(templates_dir)) self.built_in_templates_dir = Path( os.path.abspath(os.path.dirname(__file__) + "/templates") @@ -104,7 +104,7 @@ class API: for _dir in (self.static_dir, self.templates_dir): os.makedirs(_dir, exist_ok=True) - self.whitenoise = WhiteNoise(application=self._default_wsgi_app) + self.whitenoise = WhiteNoise(application=self._notfound_wsgi_app) self.whitenoise.add_files(str(self.static_dir)) self.whitenoise.add_files( @@ -156,9 +156,14 @@ class API: ) #: A Requests session that is connected to the ASGI app. @staticmethod - def _default_wsgi_app(*args, **kwargs): + def _default_wsgi_app(environ, start_response): pass + @staticmethod + def _notfound_wsgi_app(environ, start_response): + start_response("404 NOT FOUND", [("Content-Type", "text/plain")]) + return [b"Not Found."] + @property def before_requests(self): def gen(): @@ -239,7 +244,7 @@ class API: async def _dispatch_ws(self, ws): route = self.path_matches_route(ws.url.path) route = self.routes.get(route) - # await self._dispatch(route, ws=ws) + try: try: # Run the view. @@ -250,7 +255,7 @@ class API: except TypeError as e: cont = True except Exception: - self.background( + await self.background( self.default_response, websocket=route.uses_websocket, error=True ) raise @@ -386,7 +391,7 @@ class API: # If it's async, await it. if hasattr(r, "send"): await r - except Exception as e: + except Exception: await self.background(self.default_response, req, resp, error=True) raise @@ -463,10 +468,12 @@ class API: def static_response(self, req, resp): index = (self.static_dir / "index.html").resolve() - resp.content = None if os.path.exists(index): with open(index, "r") as f: resp.text = f.read() + else: + resp.status_code = status_codes.HTTP_404 + resp.text = "Not found." def schema_response(self, req, resp): resp.status_code = status_codes.HTTP_200 diff --git a/tests/test_responder.py b/tests/test_responder.py index 2c479f7..7048d1b 100644 --- a/tests/test_responder.py +++ b/tests/test_responder.py @@ -2,8 +2,10 @@ import concurrent import pytest import yaml +import random import responder import requests +import string import io from starlette.responses import PlainTextResponse @@ -603,3 +605,78 @@ def test_allowed_hosts(): api._session = None r = api.session(base_url="http://tenant2.;").get(api.url_for(get)) assert r.status_code == 200 + + +def create_asset(static_dir, name=None, parent_dir=None): + if name is None: + name = random.choices(string.ascii_letters, k=6) + # :3 + ext = random.choices(string.ascii_letters, k=2) + name = f"{name}.{ext}" + + if parent_dir is None: + parent_dir = static_dir + else: + parent_dir = static_dir.mkdir(parent_dir) + + asset = parent_dir.join(name) + asset.write("body { color: blue; }") + return asset + + +def test_staticfiles(tmpdir): + static_dir = tmpdir.mkdir("static") + + asset1 = create_asset(static_dir) + parent_dir = "css" + asset2 = create_asset(static_dir, name="asset2", parent_dir=parent_dir) + + api = responder.API(static_dir=str(static_dir)) + session = api.session() + + static_route = api.static_route + + # ok + r = session.get(f"{static_route}/{asset1.basename}") + assert r.status_code == api.status_codes.HTTP_200 + + r = session.get(f"{static_route}/{parent_dir}/{asset2.basename}") + assert r.status_code == api.status_codes.HTTP_200 + + # Asset not found + r = session.get(f"{static_route}/not_found.css") + assert r.status_code == api.status_codes.HTTP_404 + + # Not found on dir listing + r = session.get(f"{static_route}") + assert r.status_code == api.status_codes.HTTP_404 + + r = session.get(f"{static_route}/{parent_dir}") + assert r.status_code == api.status_codes.HTTP_404 + + +def test_staticfiles_custom_route(tmpdir): + static_dir = tmpdir.mkdir("static") + static_route = "custom/static/route/" + + asset = create_asset(static_dir) + + api = responder.API(static_dir=str(static_dir), static_route=static_route) + session = api.session() + + # Check + assert api.static_route == "/custom/static/route" + + static_route = api.static_route + + # ok + r = session.get(f"{static_route}/{asset.basename}") + assert r.status_code == api.status_codes.HTTP_200 + + # Asset not found + r = session.get(f"{static_route}/not_found.css") + assert r.status_code == api.status_codes.HTTP_404 + + # Not found on dir listing + r = session.get(f"{static_route}") + assert r.status_code == api.status_codes.HTTP_404