def _get_router_path(scope: Scope) -> Optional[str]:
        """Returns the original router path (with url param names) for given request."""
        if not (scope.get("endpoint", None) and scope.get("router", None)):
            return None

        base_scope = {
            "type": scope.get("type"),
            "path": scope.get("root_path", "") + scope.get("path"),
            "path_params": scope.get("path_params", {}),
            "method": scope.get("method"),
        }

        try:
            path = get_matching_route_path(base_scope,
                                           scope.get("router").routes)
            return path
        except:
            # unhandled path
            pass

        return None
Ejemplo n.º 2
0
 def matches(self, scope: Scope) -> typing.Tuple[Match, Scope]:
     if scope["type"] == "http":
         match = self.path_regex.match(scope["path"])
         if match:
             matched_params = match.groupdict()
             for key, value in matched_params.items():
                 matched_params[key] = self.param_convertors[key].convert(
                     value)
             path_params = dict(scope.get("path_params", {}))
             path_params.update(matched_params)
             child_scope = {
                 "endpoint": self.endpoint,
                 "path_params": path_params
             }
             if self.methods and scope["method"] not in self.methods:
                 return Match.PARTIAL, child_scope
             else:
                 return Match.FULL, child_scope
     return Match.NONE, {}
Ejemplo n.º 3
0
Archivo: routing.py Proyecto: Ze6/flama
        async def _app(scope: Scope, receive: Receive, send: Send) -> None:
            app = scope["app"]

            route, route_scope = app.router.get_route_from_scope(scope)

            state = {
                "scope": scope,
                "receive": receive,
                "send": send,
                "exc": None,
                "app": app,
                "path_params": route_scope["path_params"],
                "route": route,
                "websocket": websockets.WebSocket(scope, receive, send),
            }

            injected_func = await app.injector.inject(endpoint, state)

            kwargs = scope.get("kwargs", {})
            await injected_func(**kwargs)
Ejemplo n.º 4
0
 async def lifespan(self, scope: Scope, receive: Receive, send: Send) -> None:
     """
     Handle ASGI lifespan messages, which allows us to manage application
     startup and shutdown events.
     """
     started = False
     app = scope.get("app")
     await receive()
     try:
         async with self.lifespan_context(app):
             await send({"type": "lifespan.startup.complete"})
             started = True
             await receive()
     except BaseException:
         exc_text = traceback.format_exc()
         if started:
             await send({"type": "lifespan.shutdown.failed", "message": exc_text})
         else:
             await send({"type": "lifespan.startup.failed", "message": exc_text})
         raise
     else:
         await send({"type": "lifespan.shutdown.complete"})
Ejemplo n.º 5
0
    async def app(self, scope: Scope, receive: Receive, send: Send) -> None:
        if scope["type"] in ("http", "websocket"):
            path = scope["path"]
            root_path = scope.get("root_path", "")

            # Call into a submounted app, if one exists.
            for path_prefix, app in self.mount_apps.items():
                if not path.startswith(path_prefix + "/"):
                    continue
                if isinstance(app, WSGIMiddleware):
                    if scope["type"] != "http":
                        continue
                subscope = copy.deepcopy(scope)
                subscope["path"] = path[len(path_prefix):]
                subscope["root_path"] = root_path + path_prefix
                await app(subscope, receive, send)
                return

            await self.indexfile(scope, receive, send)

        elif scope["type"] == "lifespan":
            await self.lifespan(scope, receive, send)
Ejemplo n.º 6
0
    def __init__(self, url: str = "", scope: Scope = None) -> None:
        if scope is not None:
            assert not url, 'Cannot set both "url" and "scope".'
            scheme = scope["scheme"]
            host, port = scope["server"]
            path = scope.get("root_path", "") + scope["path"]
            query_string = scope["query_string"]

            default_port = {
                "http": 80,
                "https": 443,
                "ws": 80,
                "wss": 443
            }[scheme]
            if port == default_port:
                url = "%s://%s%s" % (scheme, host, path)
            else:
                url = "%s://%s:%s%s" % (scheme, host, port, path)

            if query_string:
                url += "?" + unquote(query_string.decode())
        self._url = url
Ejemplo n.º 7
0
    async def __call__(self, scope: Scope, receive: Receive, send: Send):
        self._set_cookies_to_headers()
        if self.streaming:
            self._starlette_resp = StreamingResponse
            self._body = self.streaming
        elif self._json is not None:
            self._starlette_resp = JSONResponse
            self._body = self.json
            self.headers["content-type"] = "application/json"
        elif self._redirect_to is not None:
            url, qs = self._redirect_to
            if qs is None:
                qs = scope.get("query_string", b"").decode("ascii")

            if qs:
                qs = f"?{qs}"

            self._body = url + qs

        app = self._starlette_resp(self._body,
                                   status_code=self.status_code,
                                   headers=self.headers)

        return await app(scope, receive, send)
Ejemplo n.º 8
0
    def prepare_fhir_scopes(
        scope: Scope,
        headers: Headers,
        params: QueryParams,
        errors: typing.List[typing.Any],
    ):
        # Generate Request ID
        scope["FHIR_REQUEST_ID"] = uuid.uuid4()

        # 1. Prepare Accept & FHIR Version
        # --------------------------------
        accept = headers.get("accept", None)
        if accept:
            parts = accept.split(";")
            accept = parts[0].strip()
            if accept in ("application/json", "text/json"):
                accept = "application/fhir+json"
            if accept in ALLOWED_ACCEPTS:
                scope["FHIR_RESPONSE_ACCEPT"] = accept
            else:
                errors.append({
                    "loc": "Header.Accept",
                    "msg": f"Accept mime '{accept}' is not supported.",
                    "original": headers.get("accept"),
                })
            if len(parts) > 1:
                version_str = None
                try:
                    name, version_str = parts[1].strip().split("=")
                    if name == "fhirVersion":
                        version = MIME_FHIR_VERSION_MAP[version_str]
                        scope["FHIR_VERSION"] = version
                        scope["FHIR_VERSION_ORIGINAL"] = version_str
                    else:
                        errors.append({
                            "loc": "Header.Accept",
                            "msg":
                            "Invalid format of FHIR Version is provided in mime",
                            "original": headers.get("accept"),
                        })
                except KeyError:
                    errors.append({
                        "loc": "Header.Accept",
                        "msg":
                        f"Unsupported FHIR Version '{version_str}' is provided in mime",
                        "original": headers.get("accept"),
                    })
                except ValueError:
                    errors.append({
                        "loc": "Header.Accept",
                        "msg":
                        "Invalid format of FHIR Version is provided in mime",
                        "original": headers.get("accept"),
                    })
        else:
            scope["FHIR_RESPONSE_ACCEPT"] = "application/fhir+json"

        if (scope.get("FHIR_VERSION_ORIGINAL", None) is None
                and scope.get("FHIR_VERSION", None) is None):
            scope["FHIR_VERSION"] = MIME_FHIR_VERSION_MAP[DEFAULT_FHIR_VERSION]

        # 2. Check Query String
        # ---------------------
        format_mime = params.get("_format", None)
        if format_mime is not None:
            if format_mime in ALLOWED_ACCEPTS:
                scope["FHIR_RESPONSE_FORMAT"] = format_mime
            else:
                errors.append({
                    "loc": "QueryString._format",
                    "msg": f"Format mime '{format_mime}' is not supported.",
                    "original": format_mime,
                })
        pretty_response = params.get("_pretty", None)
        if pretty_response is not None:
            if pretty_response in ("true", "false"):
                scope["FHIR_RESPONSE_PRETTY"] = pretty_response == "true"

            else:
                errors.append({
                    "loc": "QueryString._pretty",
                    "msg":
                    f"Invalid ``_pretty`` value '{pretty_response}' is provided.",
                    "original": pretty_response,
                })

        # 3. Prepare Conditional Headers
        # ------------------------------
        if headers.get("If-None-Exist"):
            scope["FHIR_CONDITION_NONE_EXIST"] = [
                tuple(
                    map(lambda x: x.strip(),
                        headers.get("If-None-Exist").split("=")))
            ]

        if headers.get("If-Modified-Since"):
            try:
                scope["FHIR_CONDITION_MODIFIED_SINCE"] = parsedate_to_datetime(
                    headers.get("If-Modified-Since"))
            except ValueError:
                errors.append({
                    "loc": "Header.If-Modified-Since",
                    "msg": "Invalid formatted datetime value is provided.",
                    "original": headers.get("If-Modified-Since"),
                })
        if headers.get("If-None-Match"):
            try:
                scope["FHIR_CONDITION_NONE_MATCH"] = literal_eval(
                    headers.get("If-None-Match").replace("W/", ""))
            except (SyntaxError, ValueError):
                errors.append({
                    "loc": "Header.If-None-Match",
                    "msg": "Invalid formatted ETag value is provided.",
                    "original": headers.get("If-None-Match"),
                })

        if headers.get("If-Match"):
            try:
                scope["FHIR_CONDITION_MATCH"] = literal_eval(
                    headers.get("If-Match").replace("W/", ""))
            except (SyntaxError, ValueError):
                errors.append({
                    "loc": "Header.If-Match",
                    "msg": "Invalid formatted ETag value is provided.",
                    "original": headers.get("If-Match"),
                })
Ejemplo n.º 9
0
    async def _dispatch(self, scope: Scope, receive: Receive,
                        send: Send) -> None:
        # check request type
        if scope["type"] == "lifespan":
            while True:
                message = await receive()
                if message["type"] == "lifespan.startup":
                    try:
                        await self._startup()
                    except Exception as e:
                        await send({
                            "type": "lifespan.startup.failed",
                            "message": str(e)
                        })
                    else:
                        await send({"type": "lifespan.startup.complete"})
                elif message["type"] == "lifespan.shutdown":
                    try:
                        await self._shutdown()
                    except Exception as e:
                        await send({
                            "type": "lifespan.shutdown.failed",
                            "message": str(e)
                        })
                    else:
                        await send({"type": "lifespan.shutdown.complete"})
                    return
        # check `/favicon.ico`.
        if self.favicon and scope["path"] == "/favicon.ico":
            scope["path"] = self.favicon
        # check mounted app.
        normalized = normalize_path(scope["path"])
        for prefix, app in self.mounted_app.items():
            if normalized.startswith(prefix):
                scope["path"] = scope["path"].replace(prefix[:-1], "", 1)
                root = scope.get("root_path", "") + prefix
                scope["root_path"] = root.replace("//", "/")
                if scope["type"] == "http":
                    model = {
                        "req": models.http.Request(scope, receive, send),
                        "resp": models.http.Response(),
                    }
                elif scope["type"] == "websocket":
                    model = {
                        "conn":
                        models.websocket.Connection(scope, receive, send)
                    }
                else:
                    raise TypeError(f"`{scope['type']}` is not supported.")

                scope["extensions"] = scope.get("extensions", {})
                scope["extensions"].update({"spangle": dict(**model)})
                await app(scope, receive, send)
                return

        # check spangle views.
        if scope["type"] == "http":
            app = await dispatch_http(scope, receive, send)
            t = asyncio.create_task(app(scope, receive, send))
        elif scope["type"] == "websocket":
            app = await dispatch_websocket(scope, receive, send)
            t = asyncio.create_task(app(scope, receive, send))
        else:
            raise ValueError("Invalid scheme.")
        await t