dynamicforms-fastapi-viewsets 0.11.2__tar.gz → 0.11.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/PKG-INFO +3 -3
- dynamicforms_fastapi_viewsets-0.11.3/fastapi_viewsets/__init__.py +1 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/mux_ws/__init__.py +4 -10
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/mux_ws/server.py +1 -4
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/pyproject.toml +2 -2
- dynamicforms_fastapi_viewsets-0.11.2/fastapi_viewsets/__init__.py +0 -1
- dynamicforms_fastapi_viewsets-0.11.2/fastapi_viewsets/mux_ws/origin.py +0 -103
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/.gitignore +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/LICENSE +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/README.md +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/action_configuration.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/backends/__init__.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/backends/django_orm.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/collection_viewset.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/conf.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/context/__init__.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/context/auth/__init__.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/context/auth/django.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/context/auth/jwt.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/context/auth/static.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/cursor.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/decorators/__init__.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/decorators/build_schema.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/decorators/celery_viewset/__init__.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/decorators/celery_viewset/client.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/decorators/celery_viewset/payload.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/decorators/celery_viewset/result_reader.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/decorators/celery_viewset/server.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/decorators/conftest.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/decorators/lifecycle_runner.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/decorators/primary_key_model_helper.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/decorators/route_viewset.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/endpoint_docs.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/events/__init__.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/events/authorizers.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/events/changes.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/events/conftest.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/events/delivery.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/events/event.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/events/lifecycle.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/events/redis.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/events/scopes.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/events/sources.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/events/store.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/events/topics.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/events/viewset.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/exceptions.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/filters/__init__.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/filters/base.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/filters/declaration.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/filters/operators.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/filters/registry.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/list_query.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/list_shapes.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/middleware/__init__.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/middleware/auth/__init__.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/middleware/auth/authorization.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/middleware/idempotency.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/middleware/rate_limiter.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/mixins.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/mux_ws/asgi.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/mux_ws/protocol.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/mux_ws/registry.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/mux_ws/transports.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/py.typed +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/response_classes.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/routing.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/save_state/__init__.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/save_state/save_state.py +0 -0
- {dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/fastapi_viewsets/save_state/serialize_state.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: dynamicforms-fastapi-viewsets
|
|
3
|
-
Version: 0.11.
|
|
3
|
+
Version: 0.11.3
|
|
4
4
|
Summary: Django REST Framework-style viewsets for FastAPI, with optional Celery-backed async execution and a Vue/TypeScript client counterpart.
|
|
5
5
|
Project-URL: Homepage, https://github.com/dynamicforms/fastapi-viewsets
|
|
6
6
|
Project-URL: Repository, https://github.com/dynamicforms/fastapi-viewsets
|
|
@@ -35,7 +35,7 @@ Requires-Dist: coverage; extra == 'dev'
|
|
|
35
35
|
Requires-Dist: django; extra == 'dev'
|
|
36
36
|
Requires-Dist: gevent; extra == 'dev'
|
|
37
37
|
Requires-Dist: httpx2; extra == 'dev'
|
|
38
|
-
Requires-Dist: muxws[starlette,websockets]>=0.4.
|
|
38
|
+
Requires-Dist: muxws[starlette,websockets]>=0.4.2; extra == 'dev'
|
|
39
39
|
Requires-Dist: pyjwt; extra == 'dev'
|
|
40
40
|
Requires-Dist: pytest; extra == 'dev'
|
|
41
41
|
Requires-Dist: pytest-asyncio; extra == 'dev'
|
|
@@ -49,7 +49,7 @@ Requires-Dist: django>=4.2; extra == 'django'
|
|
|
49
49
|
Provides-Extra: jwt
|
|
50
50
|
Requires-Dist: pyjwt>=2.8; extra == 'jwt'
|
|
51
51
|
Provides-Extra: muxws
|
|
52
|
-
Requires-Dist: muxws[starlette]>=0.4.
|
|
52
|
+
Requires-Dist: muxws[starlette]>=0.4.2; extra == 'muxws'
|
|
53
53
|
Description-Content-Type: text/markdown
|
|
54
54
|
|
|
55
55
|
# DynamicForms FastAPI Viewsets
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.11.3"
|
|
@@ -16,18 +16,14 @@ Server side:
|
|
|
16
16
|
from fastapi import FastAPI, WebSocket
|
|
17
17
|
from muxws import accept
|
|
18
18
|
|
|
19
|
-
from fastapi_viewsets.mux_ws import
|
|
19
|
+
from fastapi_viewsets.mux_ws import process_command
|
|
20
20
|
|
|
21
21
|
@app.websocket("/ws")
|
|
22
22
|
async def muxws_endpoint(websocket: WebSocket) -> None:
|
|
23
|
-
# A cookie-authenticated WebSocket has no CSRF protection: refuse a foreign Origin before
|
|
24
|
-
# accept(), which the server answers with HTTP 403.
|
|
25
|
-
if not origin_allowed(websocket):
|
|
26
|
-
await websocket.close(code=1008)
|
|
27
|
-
return
|
|
28
23
|
# Do not call websocket.accept() yourself - muxws performs the upgrade, because only it
|
|
29
|
-
# knows which muxws.v1.<codec> subprotocol to select.
|
|
30
|
-
|
|
24
|
+
# knows which muxws.v1.<codec> subprotocol to select. A cookie-authenticated WebSocket has
|
|
25
|
+
# no CSRF protection: allowed_origins refuses a foreign Origin with HTTP 403.
|
|
26
|
+
peer = await accept(websocket, allowed_origins=["https://app.example"])
|
|
31
27
|
peer.on_stream(
|
|
32
28
|
lambda payload, stream: process_command(payload, stream, connection=websocket, peer=peer)
|
|
33
29
|
)
|
|
@@ -38,7 +34,6 @@ Which viewsets get registered is decided at three levels, each able to defer to
|
|
|
38
34
|
`settings.viewsets_register_muxws` globally (default True).
|
|
39
35
|
"""
|
|
40
36
|
|
|
41
|
-
from .origin import origin_allowed
|
|
42
37
|
from .protocol import METHOD_KEY, PATH_KEY, QUERY_KEY, STATUS_KEY
|
|
43
38
|
from .registry import (
|
|
44
39
|
is_registered,
|
|
@@ -58,7 +53,6 @@ __all__ = [
|
|
|
58
53
|
"QUERY_KEY",
|
|
59
54
|
"STATUS_KEY",
|
|
60
55
|
"is_registered",
|
|
61
|
-
"origin_allowed",
|
|
62
56
|
"process_command",
|
|
63
57
|
"register_viewset",
|
|
64
58
|
"registered_viewsets",
|
|
@@ -76,10 +76,7 @@ async def process_command(
|
|
|
76
76
|
|
|
77
77
|
@app.websocket("/ws")
|
|
78
78
|
async def endpoint(websocket: WebSocket) -> None:
|
|
79
|
-
|
|
80
|
-
await websocket.close(code=1008) # before accept(): the handshake is answered with HTTP 403
|
|
81
|
-
return
|
|
82
|
-
peer = await accept(websocket)
|
|
79
|
+
peer = await accept(websocket, allowed_origins=["https://app.example"])
|
|
83
80
|
|
|
84
81
|
async def on_stream(payload, stream):
|
|
85
82
|
if await process_command(payload, stream, connection=websocket, peer=peer):
|
{dynamicforms_fastapi_viewsets-0.11.2 → dynamicforms_fastapi_viewsets-0.11.3}/pyproject.toml
RENAMED
|
@@ -34,7 +34,7 @@ dependencies = [
|
|
|
34
34
|
|
|
35
35
|
[project.optional-dependencies]
|
|
36
36
|
celery = ["celery>=5.3", "redis>=5.0"]
|
|
37
|
-
muxws = ["muxws[starlette]>=0.4.
|
|
37
|
+
muxws = ["muxws[starlette]>=0.4.2"]
|
|
38
38
|
django = ["django>=4.2", "asgiref>=3.6"]
|
|
39
39
|
jwt = ["PyJWT>=2.8"]
|
|
40
40
|
dev = [
|
|
@@ -49,7 +49,7 @@ dev = [
|
|
|
49
49
|
"django",
|
|
50
50
|
"asgiref",
|
|
51
51
|
"PyJWT",
|
|
52
|
-
"muxws[starlette,websockets]>=0.4.
|
|
52
|
+
"muxws[starlette,websockets]>=0.4.2",
|
|
53
53
|
"ruff",
|
|
54
54
|
"uvicorn",
|
|
55
55
|
]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.11.2"
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
`origin_allowed`, the `Origin` check of a WebSocket endpoint.
|
|
3
|
-
|
|
4
|
-
A browser sends the cookies of the endpoint's site with a WebSocket handshake whatever page opened
|
|
5
|
-
it, and a WebSocket has no CSRF protection of its own: an endpoint whose session is a cookie answers
|
|
6
|
-
a handshake from another site's page with that session unless it checks `Origin` before `accept()`.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
from collections.abc import Collection
|
|
10
|
-
from typing import TYPE_CHECKING
|
|
11
|
-
from urllib.parse import urlsplit
|
|
12
|
-
|
|
13
|
-
if TYPE_CHECKING:
|
|
14
|
-
from starlette.requests import HTTPConnection
|
|
15
|
-
|
|
16
|
-
_DEFAULT_PORTS = {"http": 80, "https": 443}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def _parse_origin(value: str) -> tuple[str, str, int] | None:
|
|
20
|
-
"""`(scheme, host, port)` of a serialized origin, `scheme://host[:port]` with an optional
|
|
21
|
-
trailing `/`, scheme and host lower-cased and the port explicit; None for anything else."""
|
|
22
|
-
text = value.strip()
|
|
23
|
-
if text.endswith("/"):
|
|
24
|
-
text = text[:-1]
|
|
25
|
-
try:
|
|
26
|
-
parts = urlsplit(text)
|
|
27
|
-
port = parts.port
|
|
28
|
-
except ValueError:
|
|
29
|
-
return None
|
|
30
|
-
scheme = parts.scheme.lower()
|
|
31
|
-
if scheme not in _DEFAULT_PORTS or not parts.hostname:
|
|
32
|
-
return None
|
|
33
|
-
if parts.path or parts.query or parts.fragment or parts.username is not None or parts.password is not None:
|
|
34
|
-
return None
|
|
35
|
-
if "@" in parts.netloc:
|
|
36
|
-
return None
|
|
37
|
-
return scheme, parts.hostname.lower(), port if port is not None else _DEFAULT_PORTS[scheme]
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def _parse_host(value: str, default_port: int) -> tuple[str, int] | None:
|
|
41
|
-
"""`(host, port)` of a `Host` header value, the port `default_port` when it names none."""
|
|
42
|
-
try:
|
|
43
|
-
parts = urlsplit(f"//{value.strip()}")
|
|
44
|
-
port = parts.port
|
|
45
|
-
except ValueError:
|
|
46
|
-
return None
|
|
47
|
-
if not parts.hostname or parts.path or parts.query or parts.fragment or "@" in parts.netloc:
|
|
48
|
-
return None
|
|
49
|
-
return parts.hostname.lower(), port if port is not None else default_port
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def origin_allowed(connection: "HTTPConnection", allowed_origins: Collection[str] | None = None) -> bool:
|
|
53
|
-
"""
|
|
54
|
-
Whether the `Origin` of `connection` (a `WebSocket` or a `Request`) may open the endpoint.
|
|
55
|
-
|
|
56
|
-
- No `Origin` header (a client that is not a browser): True. More than one: False.
|
|
57
|
-
- `Origin: null`, and an origin that is not `scheme://host[:port]` with the scheme `http` or
|
|
58
|
-
`https` (a path, a query, userinfo, another scheme): False.
|
|
59
|
-
- Origins compare as `(scheme, host, port)`, scheme and host lower-cased, the port the scheme's
|
|
60
|
-
default (80, 443) when the origin names none, a trailing `/` removed.
|
|
61
|
-
- With `allowed_origins`: True when the origin equals one of its entries. An entry that is not
|
|
62
|
-
an origin raises `ValueError`.
|
|
63
|
-
- Without it: True when the origin's `(host, port)` equals that of the request's host, the first
|
|
64
|
-
item of `X-Forwarded-Host` when the request carries one, else `Host`; a host without a port
|
|
65
|
-
takes the default port of the origin's scheme. The scheme is not compared, so a TLS proxy in
|
|
66
|
-
front of the application passes. No host at all: False. A proxy that rewrites `Host` without
|
|
67
|
-
setting `X-Forwarded-Host` needs `allowed_origins`.
|
|
68
|
-
|
|
69
|
-
The endpoint calls it before `accept()` and closes a refused handshake, which the server answers
|
|
70
|
-
with HTTP 403:
|
|
71
|
-
|
|
72
|
-
if not origin_allowed(websocket):
|
|
73
|
-
await websocket.close(code=1008)
|
|
74
|
-
return
|
|
75
|
-
peer = await accept(websocket)
|
|
76
|
-
"""
|
|
77
|
-
allowed = None
|
|
78
|
-
if allowed_origins is not None:
|
|
79
|
-
allowed = set()
|
|
80
|
-
for entry in allowed_origins:
|
|
81
|
-
parsed = _parse_origin(entry)
|
|
82
|
-
if parsed is None:
|
|
83
|
-
raise ValueError(f"{entry!r} in allowed_origins is not an origin: scheme://host[:port]")
|
|
84
|
-
allowed.add(parsed)
|
|
85
|
-
|
|
86
|
-
values = connection.headers.getlist("origin")
|
|
87
|
-
if not values:
|
|
88
|
-
return True
|
|
89
|
-
if len(values) > 1:
|
|
90
|
-
return False
|
|
91
|
-
origin = _parse_origin(values[0])
|
|
92
|
-
if origin is None:
|
|
93
|
-
return False
|
|
94
|
-
if allowed is not None:
|
|
95
|
-
return origin in allowed
|
|
96
|
-
|
|
97
|
-
scheme, host, port = origin
|
|
98
|
-
forwarded = connection.headers.get("x-forwarded-host")
|
|
99
|
-
request_host = forwarded.split(",")[0] if forwarded else connection.headers.get("host")
|
|
100
|
-
if not request_host or not request_host.strip():
|
|
101
|
-
return False
|
|
102
|
-
parsed_host = _parse_host(request_host, _DEFAULT_PORTS[scheme])
|
|
103
|
-
return parsed_host == (host, port)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|