async def get_response(self, path: str, scope: Scope) -> Response:
        """
        Returns an HTTP response, given the incoming path, method and request headers.
        """
        if scope["method"] not in ("GET", "HEAD"):
            return PlainTextResponse("Method Not Allowed", status_code=405)

        full_path, stat_result = await self.lookup_path(path)

        if stat_result and stat.S_ISREG(stat_result.st_mode):
            # We have a static file to serve.
            return self.file_response(full_path, stat_result, scope)

        elif stat_result and stat.S_ISDIR(stat_result.st_mode) and self.html:
            # We're in HTML mode, and have got a directory URL.
            # Check if we have 'index.html' file to serve.
            index_path = os.path.join(path, "index.html")
            full_path, stat_result = await self.lookup_path(index_path)
            if stat_result is not None and stat.S_ISREG(stat_result.st_mode):
                if not scope["path"].endswith("/"):
                    # Directory URLs should redirect to always end in "/".
                    url = URL(scope=scope)
                    url = url.replace(path=url.path + "/")
                    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 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 PlainTextResponse("Not Found", status_code=404)
Exemplo n.º 2
0
    async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
        if self.allow_any or scope["type"] not in (
            "http",
            "websocket",
        ):  # pragma: no cover
            await self.app(scope, receive, send)
            return

        headers = Headers(scope=scope)
        host = headers.get("host", "").split(":")[0]
        is_valid_host = False
        found_www_redirect = False
        for pattern in self.allowed_hosts:
            if host == pattern or (
                pattern.startswith("*") and host.endswith(pattern[1:])
            ):
                is_valid_host = True
                break
            elif "www." + host == pattern:
                found_www_redirect = True

        if is_valid_host:
            await self.app(scope, receive, send)
        else:
            response: Response
            if found_www_redirect and self.www_redirect:
                url = URL(scope=scope)
                redirect_url = url.replace(netloc="www." + url.netloc)
                response = RedirectResponse(url=str(redirect_url))
            else:
                response = PlainTextResponse("Invalid host header", status_code=400)
            await response(scope, receive, send)
Exemplo n.º 3
0
    async def __call__(self, scope: Scope, receive: Receive,
                       send: Send) -> None:
        scope["app"] = self

        if scope["type"] in ("http",
                             "websocket"):  # Handle some special routes
            url = URL(scope=scope)
            response: typing.Optional[Response] = None

            # Force jump to https/wss
            if Config().FORCE_SSL and scope["scheme"] in ("http", "ws"):
                redirect_scheme = {"http": "https", "ws": "wss"}[url.scheme]
                netloc = url.hostname if url.port in (80, 443) else url.netloc
                url = url.replace(scheme=redirect_scheme, netloc=netloc)
                response = RedirectResponse(
                    url, status_code=301)  # for SEO, status code must be 301

            if url.path == "/favicon.ico":
                if os.path.exists(os.path.normpath("favicon.ico")):
                    response = FileResponse("favicon.ico")

            if response is not None:
                await response(scope, receive, send)
                return

        await self.asgiapp(scope, receive, send)
Exemplo n.º 4
0
    async def __call__(self, scope: Scope, receive: Receive,
                       send: Send) -> None:
        handler = getattr(self, scope["type"])
        url = URL(scope=scope)

        if url.path.endswith("/index"):
            handler = RedirectResponse(
                url.replace(path=url.path[:-len("/index")]),
                status_code=301,
            )
        elif "_" in url.path and not self.allow_underline:
            handler = RedirectResponse(
                url.replace(path=url.path.replace("_", "-")),
                status_code=301,
            )

        await handler(scope, receive, send)
Exemplo n.º 5
0
    def __call__(self, scope: Scope) -> ASGIInstance:
        if scope["type"] in ("http",
                             "websocket") and scope["scheme"] in ("http",
                                                                  "ws"):
            redirect_scheme = {"http": "https", "ws": "wss"}[scope["scheme"]]
            url = URL(scope=scope)
            url = url.replace(scheme=redirect_scheme, netloc=url.hostname)
            return RedirectResponse(url, status_code=301)

        return self.app(scope)
Exemplo n.º 6
0
 async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
     if scope["type"] in ("http", "websocket") and scope["scheme"] in ("http", "ws"):
         url = URL(scope=scope)
         redirect_scheme = {"http": "https", "ws": "wss"}[url.scheme]
         netloc = url.hostname if url.port in (80, 443) else url.netloc
         url = url.replace(scheme=redirect_scheme, netloc=netloc)
         response = RedirectResponse(url, status_code=307)
         await response(scope, receive, send)
     else:
         await self.app(scope, receive, send)
Exemplo n.º 7
0
def test_url():
    u = URL("https://example.org:123/path/to/somewhere?abc=123#anchor")
    assert u.scheme == "https"
    assert u.hostname == "example.org"
    assert u.port == 123
    assert u.netloc == "example.org:123"
    assert u.username is None
    assert u.password is None
    assert u.path == "/path/to/somewhere"
    assert u.query == "abc=123"
    assert u.fragment == "anchor"

    new = u.replace(scheme="http")
    assert new == "http://example.org:123/path/to/somewhere?abc=123#anchor"
    assert new.scheme == "http"

    new = u.replace(port=None)
    assert new == "https://example.org/path/to/somewhere?abc=123#anchor"
    assert new.port is None

    new = u.replace(hostname="example.com")
    assert new == "https://example.com:123/path/to/somewhere?abc=123#anchor"
    assert new.hostname == "example.com"
Exemplo n.º 8
0
    def __call__(self, scope: Scope) -> ASGIInstance:
        if scope["type"] in ("http"):

            host = None
            for key, value in scope["headers"]:
                if key == b"host":
                    host = value.decode("latin-1")
                    break

            if host and not host.startswith("www."):
                url = URL(scope=scope)
                url = url.replace(hostname=f"www.{host}")
                return RedirectResponse(url, status_code=301)

        return self.app(scope)
Exemplo n.º 9
0
    async def get_response(self, path: str, scope: Scope) -> Response:
        """
        Returns an HTTP response, given the incoming path, method and request headers.
        """
        if scope["method"] not in ("GET", "HEAD"):
            raise HTTPException(status_code=405)

        try:
            full_path, stat_result = await anyio.to_thread.run_sync(
                self.lookup_path, path)
        except PermissionError:
            raise HTTPException(status_code=401)
        except OSError:
            raise

        if stat_result and stat.S_ISREG(stat_result.st_mode):
            # We have a static file to serve.
            return self.file_response(full_path, stat_result, scope)

        elif stat_result and stat.S_ISDIR(stat_result.st_mode) and self.html:
            # We're in HTML mode, and have got a directory URL.
            # Check if we have 'index.html' file to serve.
            index_path = os.path.join(path, "index.html")
            full_path, stat_result = await anyio.to_thread.run_sync(
                self.lookup_path, index_path)
            if stat_result is not None and stat.S_ISREG(stat_result.st_mode):
                if not scope["path"].endswith("/"):
                    # Directory URLs should redirect to always end in "/".
                    url = URL(scope=scope)
                    url = url.replace(path=url.path + "/")
                    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)
Exemplo n.º 10
0
    def __call__(self, scope: Scope) -> ASGIInstance:
        if scope["type"] in ("http", "websocket") and not self.allow_any:
            headers = Headers(scope=scope)
            host = headers.get("host", "").split(":")[0]
            found_www_redirect = False
            for pattern in self.allowed_hosts:
                if host == pattern or (pattern.startswith("*")
                                       and host.endswith(pattern[1:])):
                    break
                elif "www." + host == pattern:
                    found_www_redirect = True
            else:
                if found_www_redirect and self.www_redirect:
                    url = URL(scope=scope)
                    redirect_url = url.replace(netloc="www." + url.netloc)
                    return RedirectResponse(url=str(redirect_url))
                return PlainTextResponse("Invalid host header",
                                         status_code=400)

        return self.app(scope)