litestar-vite 0.1.1__py3-none-any.whl → 0.15.0__py3-none-any.whl
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.
- litestar_vite/__init__.py +54 -4
- litestar_vite/__metadata__.py +12 -7
- litestar_vite/cli.py +1048 -10
- litestar_vite/codegen/__init__.py +48 -0
- litestar_vite/codegen/_export.py +229 -0
- litestar_vite/codegen/_inertia.py +619 -0
- litestar_vite/codegen/_openapi.py +280 -0
- litestar_vite/codegen/_routes.py +720 -0
- litestar_vite/codegen/_ts.py +235 -0
- litestar_vite/codegen/_utils.py +141 -0
- litestar_vite/commands.py +73 -0
- litestar_vite/config/__init__.py +997 -0
- litestar_vite/config/_constants.py +97 -0
- litestar_vite/config/_deploy.py +70 -0
- litestar_vite/config/_inertia.py +241 -0
- litestar_vite/config/_paths.py +63 -0
- litestar_vite/config/_runtime.py +235 -0
- litestar_vite/config/_spa.py +93 -0
- litestar_vite/config/_types.py +94 -0
- litestar_vite/deploy.py +366 -0
- litestar_vite/doctor.py +1181 -0
- litestar_vite/exceptions.py +78 -0
- litestar_vite/executor.py +360 -0
- litestar_vite/handler/__init__.py +9 -0
- litestar_vite/handler/_app.py +612 -0
- litestar_vite/handler/_routing.py +130 -0
- litestar_vite/html_transform.py +569 -0
- litestar_vite/inertia/__init__.py +77 -0
- litestar_vite/inertia/_utils.py +119 -0
- litestar_vite/inertia/exception_handler.py +178 -0
- litestar_vite/inertia/helpers.py +1571 -0
- litestar_vite/inertia/middleware.py +54 -0
- litestar_vite/inertia/plugin.py +199 -0
- litestar_vite/inertia/precognition.py +274 -0
- litestar_vite/inertia/request.py +334 -0
- litestar_vite/inertia/response.py +802 -0
- litestar_vite/inertia/types.py +335 -0
- litestar_vite/loader.py +464 -123
- litestar_vite/plugin/__init__.py +687 -0
- litestar_vite/plugin/_process.py +185 -0
- litestar_vite/plugin/_proxy.py +689 -0
- litestar_vite/plugin/_proxy_headers.py +244 -0
- litestar_vite/plugin/_static.py +37 -0
- litestar_vite/plugin/_utils.py +489 -0
- litestar_vite/py.typed +0 -0
- litestar_vite/scaffolding/__init__.py +20 -0
- litestar_vite/scaffolding/generator.py +270 -0
- litestar_vite/scaffolding/templates.py +437 -0
- litestar_vite/templates/__init__.py +0 -0
- litestar_vite/templates/addons/tailwindcss/tailwind.css.j2 +1 -0
- litestar_vite/templates/angular/index.html.j2 +12 -0
- litestar_vite/templates/angular/openapi-ts.config.ts.j2 +18 -0
- litestar_vite/templates/angular/package.json.j2 +36 -0
- litestar_vite/templates/angular/src/app/app.component.css.j2 +3 -0
- litestar_vite/templates/angular/src/app/app.component.html.j2 +1 -0
- litestar_vite/templates/angular/src/app/app.component.ts.j2 +9 -0
- litestar_vite/templates/angular/src/app/app.config.ts.j2 +5 -0
- litestar_vite/templates/angular/src/main.ts.j2 +9 -0
- litestar_vite/templates/angular/src/styles.css.j2 +9 -0
- litestar_vite/templates/angular/tsconfig.app.json.j2 +34 -0
- litestar_vite/templates/angular/tsconfig.json.j2 +20 -0
- litestar_vite/templates/angular/vite.config.ts.j2 +21 -0
- litestar_vite/templates/angular-cli/.postcssrc.json.j2 +5 -0
- litestar_vite/templates/angular-cli/angular.json.j2 +36 -0
- litestar_vite/templates/angular-cli/openapi-ts.config.ts.j2 +18 -0
- litestar_vite/templates/angular-cli/package.json.j2 +28 -0
- litestar_vite/templates/angular-cli/proxy.conf.json.j2 +18 -0
- litestar_vite/templates/angular-cli/src/app/app.component.css.j2 +3 -0
- litestar_vite/templates/angular-cli/src/app/app.component.html.j2 +1 -0
- litestar_vite/templates/angular-cli/src/app/app.component.ts.j2 +9 -0
- litestar_vite/templates/angular-cli/src/app/app.config.ts.j2 +5 -0
- litestar_vite/templates/angular-cli/src/index.html.j2 +13 -0
- litestar_vite/templates/angular-cli/src/main.ts.j2 +6 -0
- litestar_vite/templates/angular-cli/src/styles.css.j2 +10 -0
- litestar_vite/templates/angular-cli/tailwind.config.js.j2 +4 -0
- litestar_vite/templates/angular-cli/tsconfig.app.json.j2 +16 -0
- litestar_vite/templates/angular-cli/tsconfig.json.j2 +26 -0
- litestar_vite/templates/angular-cli/tsconfig.spec.json.j2 +9 -0
- litestar_vite/templates/astro/astro.config.mjs.j2 +28 -0
- litestar_vite/templates/astro/openapi-ts.config.ts.j2 +15 -0
- litestar_vite/templates/astro/src/layouts/Layout.astro.j2 +63 -0
- litestar_vite/templates/astro/src/pages/index.astro.j2 +36 -0
- litestar_vite/templates/astro/src/styles/global.css.j2 +1 -0
- litestar_vite/templates/base/.gitignore.j2 +42 -0
- litestar_vite/templates/base/openapi-ts.config.ts.j2 +15 -0
- litestar_vite/templates/base/package.json.j2 +39 -0
- litestar_vite/templates/base/resources/vite-env.d.ts.j2 +1 -0
- litestar_vite/templates/base/tsconfig.json.j2 +37 -0
- litestar_vite/templates/htmx/src/main.js.j2 +8 -0
- litestar_vite/templates/htmx/templates/base.html.j2.j2 +56 -0
- litestar_vite/templates/htmx/templates/index.html.j2.j2 +13 -0
- litestar_vite/templates/htmx/vite.config.ts.j2 +40 -0
- litestar_vite/templates/nuxt/app.vue.j2 +29 -0
- litestar_vite/templates/nuxt/composables/useApi.ts.j2 +33 -0
- litestar_vite/templates/nuxt/nuxt.config.ts.j2 +31 -0
- litestar_vite/templates/nuxt/openapi-ts.config.ts.j2 +15 -0
- litestar_vite/templates/nuxt/pages/index.vue.j2 +54 -0
- litestar_vite/templates/react/index.html.j2 +13 -0
- litestar_vite/templates/react/src/App.css.j2 +56 -0
- litestar_vite/templates/react/src/App.tsx.j2 +19 -0
- litestar_vite/templates/react/src/main.tsx.j2 +10 -0
- litestar_vite/templates/react/vite.config.ts.j2 +39 -0
- litestar_vite/templates/react-inertia/index.html.j2 +14 -0
- litestar_vite/templates/react-inertia/package.json.j2 +47 -0
- litestar_vite/templates/react-inertia/resources/App.css.j2 +68 -0
- litestar_vite/templates/react-inertia/resources/main.tsx.j2 +17 -0
- litestar_vite/templates/react-inertia/resources/pages/Home.tsx.j2 +18 -0
- litestar_vite/templates/react-inertia/resources/ssr.tsx.j2 +19 -0
- litestar_vite/templates/react-inertia/vite.config.ts.j2 +59 -0
- litestar_vite/templates/react-router/index.html.j2 +12 -0
- litestar_vite/templates/react-router/src/App.css.j2 +17 -0
- litestar_vite/templates/react-router/src/App.tsx.j2 +7 -0
- litestar_vite/templates/react-router/src/main.tsx.j2 +10 -0
- litestar_vite/templates/react-router/vite.config.ts.j2 +39 -0
- litestar_vite/templates/react-tanstack/index.html.j2 +12 -0
- litestar_vite/templates/react-tanstack/openapi-ts.config.ts.j2 +18 -0
- litestar_vite/templates/react-tanstack/src/App.css.j2 +17 -0
- litestar_vite/templates/react-tanstack/src/main.tsx.j2 +21 -0
- litestar_vite/templates/react-tanstack/src/routeTree.gen.ts.j2 +7 -0
- litestar_vite/templates/react-tanstack/src/routes/__root.tsx.j2 +9 -0
- litestar_vite/templates/react-tanstack/src/routes/books.tsx.j2 +9 -0
- litestar_vite/templates/react-tanstack/src/routes/index.tsx.j2 +9 -0
- litestar_vite/templates/react-tanstack/vite.config.ts.j2 +39 -0
- litestar_vite/templates/svelte/index.html.j2 +13 -0
- litestar_vite/templates/svelte/src/App.svelte.j2 +30 -0
- litestar_vite/templates/svelte/src/app.css.j2 +45 -0
- litestar_vite/templates/svelte/src/main.ts.j2 +8 -0
- litestar_vite/templates/svelte/src/vite-env.d.ts.j2 +2 -0
- litestar_vite/templates/svelte/svelte.config.js.j2 +5 -0
- litestar_vite/templates/svelte/vite.config.ts.j2 +39 -0
- litestar_vite/templates/svelte-inertia/index.html.j2 +14 -0
- litestar_vite/templates/svelte-inertia/resources/app.css.j2 +21 -0
- litestar_vite/templates/svelte-inertia/resources/main.ts.j2 +11 -0
- litestar_vite/templates/svelte-inertia/resources/pages/Home.svelte.j2 +43 -0
- litestar_vite/templates/svelte-inertia/resources/vite-env.d.ts.j2 +2 -0
- litestar_vite/templates/svelte-inertia/svelte.config.js.j2 +5 -0
- litestar_vite/templates/svelte-inertia/vite.config.ts.j2 +37 -0
- litestar_vite/templates/sveltekit/openapi-ts.config.ts.j2 +15 -0
- litestar_vite/templates/sveltekit/src/app.css.j2 +40 -0
- litestar_vite/templates/sveltekit/src/app.html.j2 +12 -0
- litestar_vite/templates/sveltekit/src/hooks.server.ts.j2 +55 -0
- litestar_vite/templates/sveltekit/src/routes/+layout.svelte.j2 +12 -0
- litestar_vite/templates/sveltekit/src/routes/+page.svelte.j2 +34 -0
- litestar_vite/templates/sveltekit/svelte.config.js.j2 +12 -0
- litestar_vite/templates/sveltekit/tsconfig.json.j2 +14 -0
- litestar_vite/templates/sveltekit/vite.config.ts.j2 +31 -0
- litestar_vite/templates/vue/env.d.ts.j2 +7 -0
- litestar_vite/templates/vue/index.html.j2 +13 -0
- litestar_vite/templates/vue/src/App.vue.j2 +28 -0
- litestar_vite/templates/vue/src/main.ts.j2 +5 -0
- litestar_vite/templates/vue/src/style.css.j2 +45 -0
- litestar_vite/templates/vue/vite.config.ts.j2 +39 -0
- litestar_vite/templates/vue-inertia/env.d.ts.j2 +7 -0
- litestar_vite/templates/vue-inertia/index.html.j2 +14 -0
- litestar_vite/templates/vue-inertia/package.json.j2 +50 -0
- litestar_vite/templates/vue-inertia/resources/main.ts.j2 +18 -0
- litestar_vite/templates/vue-inertia/resources/pages/Home.vue.j2 +22 -0
- litestar_vite/templates/vue-inertia/resources/ssr.ts.j2 +21 -0
- litestar_vite/templates/vue-inertia/resources/style.css.j2 +21 -0
- litestar_vite/templates/vue-inertia/vite.config.ts.j2 +59 -0
- litestar_vite-0.15.0.dist-info/METADATA +230 -0
- litestar_vite-0.15.0.dist-info/RECORD +164 -0
- {litestar_vite-0.1.1.dist-info → litestar_vite-0.15.0.dist-info}/WHEEL +1 -1
- litestar_vite/config.py +0 -100
- litestar_vite/plugin.py +0 -45
- litestar_vite/template_engine.py +0 -103
- litestar_vite-0.1.1.dist-info/METADATA +0 -68
- litestar_vite-0.1.1.dist-info/RECORD +0 -11
- {litestar_vite-0.1.1.dist-info → litestar_vite-0.15.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"""Proxy headers middleware for handling X-Forwarded-* headers securely.
|
|
2
|
+
|
|
3
|
+
This module provides middleware to handle X-Forwarded-* headers from reverse proxies
|
|
4
|
+
like Railway, Heroku, AWS ALB, nginx, etc.
|
|
5
|
+
|
|
6
|
+
Security: Headers are only trusted when the direct caller IP is in the configured
|
|
7
|
+
trusted_proxies list. This prevents header spoofing attacks.
|
|
8
|
+
|
|
9
|
+
Related: https://github.com/litestar-org/litestar-vite/issues/167
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import ipaddress
|
|
13
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
14
|
+
|
|
15
|
+
from litestar.enums import ScopeType
|
|
16
|
+
from litestar.middleware import AbstractMiddleware
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from litestar.types import ASGIApp, Receive, Scope, Send
|
|
20
|
+
|
|
21
|
+
__all__ = ("ProxyHeadersMiddleware", "TrustedHosts")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class TrustedHosts:
|
|
25
|
+
"""Container for trusted proxy hosts and networks.
|
|
26
|
+
|
|
27
|
+
Provides efficient lookup for IP addresses and CIDR networks.
|
|
28
|
+
Following Uvicorn's security model for proxy header validation.
|
|
29
|
+
|
|
30
|
+
Supports:
|
|
31
|
+
- Wildcard "*" to trust all hosts (for controlled environments)
|
|
32
|
+
- IPv4 addresses: "192.168.1.1"
|
|
33
|
+
- IPv6 addresses: "::1"
|
|
34
|
+
- CIDR notation: "10.0.0.0/8", "fd00::/8"
|
|
35
|
+
- Literals for non-IP hosts (e.g., Unix socket paths)
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
__slots__ = ("always_trust", "trusted_hosts", "trusted_literals", "trusted_networks")
|
|
39
|
+
|
|
40
|
+
def __init__(self, trusted_hosts: "list[str] | str") -> None:
|
|
41
|
+
"""Initialize trusted hosts container.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
trusted_hosts: A single host, comma-separated string, or list of hosts.
|
|
45
|
+
Use "*" to trust all hosts (only in controlled environments).
|
|
46
|
+
"""
|
|
47
|
+
self.always_trust: bool = trusted_hosts in ("*", ["*"])
|
|
48
|
+
self.trusted_literals: set[str] = set()
|
|
49
|
+
self.trusted_hosts: set[ipaddress.IPv4Address | ipaddress.IPv6Address] = set()
|
|
50
|
+
self.trusted_networks: set[ipaddress.IPv4Network | ipaddress.IPv6Network] = set()
|
|
51
|
+
|
|
52
|
+
if not self.always_trust:
|
|
53
|
+
hosts_list: list[str]
|
|
54
|
+
if isinstance(trusted_hosts, str):
|
|
55
|
+
hosts_list = [h.strip() for h in trusted_hosts.split(",") if h.strip()]
|
|
56
|
+
else:
|
|
57
|
+
hosts_list = trusted_hosts
|
|
58
|
+
|
|
59
|
+
for host in hosts_list:
|
|
60
|
+
if "/" in host:
|
|
61
|
+
# CIDR notation
|
|
62
|
+
try:
|
|
63
|
+
self.trusted_networks.add(ipaddress.ip_network(host, strict=False))
|
|
64
|
+
except ValueError:
|
|
65
|
+
# Not a valid network, treat as literal
|
|
66
|
+
self.trusted_literals.add(host)
|
|
67
|
+
else:
|
|
68
|
+
try:
|
|
69
|
+
self.trusted_hosts.add(ipaddress.ip_address(host))
|
|
70
|
+
except ValueError:
|
|
71
|
+
# Not a valid IP, treat as literal (e.g., Unix socket path)
|
|
72
|
+
self.trusted_literals.add(host)
|
|
73
|
+
|
|
74
|
+
def __contains__(self, host: "str | None") -> bool:
|
|
75
|
+
"""Check if a host is trusted.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
host: The host to check. Can be an IP address or literal.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
True if the host is trusted, False otherwise.
|
|
82
|
+
"""
|
|
83
|
+
# None and empty string are never trusted
|
|
84
|
+
if not host:
|
|
85
|
+
return False
|
|
86
|
+
if self.always_trust:
|
|
87
|
+
return True
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
ip = ipaddress.ip_address(host)
|
|
91
|
+
if ip in self.trusted_hosts:
|
|
92
|
+
return True
|
|
93
|
+
return any(ip in net for net in self.trusted_networks)
|
|
94
|
+
except ValueError:
|
|
95
|
+
return host in self.trusted_literals
|
|
96
|
+
|
|
97
|
+
def get_trusted_client_host(self, x_forwarded_for: str) -> str:
|
|
98
|
+
"""Extract the real client IP from X-Forwarded-For header.
|
|
99
|
+
|
|
100
|
+
The X-Forwarded-For header contains a comma-separated list of IPs.
|
|
101
|
+
Each proxy appends the client IP to the list. We find the first
|
|
102
|
+
untrusted host (reading from right to left) which is the real client.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
x_forwarded_for: The X-Forwarded-For header value.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
The first untrusted host in the chain, or the original client
|
|
109
|
+
if all hosts are trusted.
|
|
110
|
+
"""
|
|
111
|
+
hosts = [h.strip() for h in x_forwarded_for.split(",") if h.strip()]
|
|
112
|
+
|
|
113
|
+
if not hosts:
|
|
114
|
+
return ""
|
|
115
|
+
|
|
116
|
+
if self.always_trust:
|
|
117
|
+
# When trusting all, return the leftmost (original client)
|
|
118
|
+
return hosts[0]
|
|
119
|
+
|
|
120
|
+
# Each proxy appends to the list, so check in reverse
|
|
121
|
+
# Find the first untrusted host from the right
|
|
122
|
+
for host in reversed(hosts):
|
|
123
|
+
if host not in self:
|
|
124
|
+
return host
|
|
125
|
+
|
|
126
|
+
# All hosts are trusted - return the original client
|
|
127
|
+
return hosts[0]
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class ProxyHeadersMiddleware(AbstractMiddleware):
|
|
131
|
+
"""ASGI middleware for secure proxy header handling.
|
|
132
|
+
|
|
133
|
+
Only processes X-Forwarded-* headers when the direct caller (scope["client"])
|
|
134
|
+
is in the trusted hosts list. This prevents header spoofing attacks.
|
|
135
|
+
|
|
136
|
+
Handles:
|
|
137
|
+
- X-Forwarded-Proto: Sets scope["scheme"] (http/https/ws/wss)
|
|
138
|
+
- X-Forwarded-For: Sets scope["client"] to the real client IP
|
|
139
|
+
- X-Forwarded-Host: Optionally sets the Host header
|
|
140
|
+
|
|
141
|
+
Security:
|
|
142
|
+
Never blindly trusts headers from any client. Validates caller IP
|
|
143
|
+
against trusted hosts before reading headers. Validates scheme values
|
|
144
|
+
to only allow http/https/ws/wss.
|
|
145
|
+
|
|
146
|
+
Example::
|
|
147
|
+
|
|
148
|
+
from litestar_vite import VitePlugin, ViteConfig
|
|
149
|
+
from litestar_vite.config import RuntimeConfig
|
|
150
|
+
|
|
151
|
+
# Trust all proxies (Railway, Heroku, container environments)
|
|
152
|
+
app = Litestar(
|
|
153
|
+
plugins=[VitePlugin(config=ViteConfig(
|
|
154
|
+
runtime=RuntimeConfig(trusted_proxies="*")
|
|
155
|
+
))]
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# Trust specific proxy IPs
|
|
159
|
+
app = Litestar(
|
|
160
|
+
plugins=[VitePlugin(config=ViteConfig(
|
|
161
|
+
runtime=RuntimeConfig(trusted_proxies=["10.0.0.0/8", "172.16.0.0/12"])
|
|
162
|
+
))]
|
|
163
|
+
)
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
scopes = {ScopeType.HTTP, ScopeType.WEBSOCKET}
|
|
167
|
+
|
|
168
|
+
def __init__(
|
|
169
|
+
self, app: "ASGIApp", trusted_hosts: "list[str] | str" = "127.0.0.1", handle_forwarded_host: bool = True
|
|
170
|
+
) -> None:
|
|
171
|
+
"""Initialize the proxy headers middleware.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
app: The ASGI application to wrap.
|
|
175
|
+
trusted_hosts: Hosts to trust for X-Forwarded-* headers.
|
|
176
|
+
Defaults to "127.0.0.1" (localhost only).
|
|
177
|
+
handle_forwarded_host: Whether to handle X-Forwarded-Host header
|
|
178
|
+
for Host header rewriting. Defaults to True.
|
|
179
|
+
"""
|
|
180
|
+
super().__init__(app)
|
|
181
|
+
self.trusted_hosts = TrustedHosts(trusted_hosts)
|
|
182
|
+
self.handle_forwarded_host = handle_forwarded_host
|
|
183
|
+
|
|
184
|
+
async def __call__(self, scope: "Scope", receive: "Receive", send: "Send") -> None:
|
|
185
|
+
"""Process the request and apply proxy headers if trusted.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
scope: The ASGI scope.
|
|
189
|
+
receive: The receive callable.
|
|
190
|
+
send: The send callable.
|
|
191
|
+
"""
|
|
192
|
+
client_addr = scope.get("client") # pyright: ignore[reportUnknownMemberType]
|
|
193
|
+
client_host = client_addr[0] if client_addr else None
|
|
194
|
+
|
|
195
|
+
if client_host in self.trusted_hosts:
|
|
196
|
+
# Build a dict of headers for efficient lookup
|
|
197
|
+
headers: dict[bytes, bytes] = {}
|
|
198
|
+
for key, value in scope.get("headers", []): # pyright: ignore[reportUnknownMemberType]
|
|
199
|
+
# Use first occurrence only (as per HTTP spec)
|
|
200
|
+
if key not in headers:
|
|
201
|
+
headers[key] = value
|
|
202
|
+
|
|
203
|
+
scope_dict = cast("dict[str, Any]", scope)
|
|
204
|
+
|
|
205
|
+
# X-Forwarded-Proto -> scope["scheme"]
|
|
206
|
+
if b"x-forwarded-proto" in headers:
|
|
207
|
+
proto = headers[b"x-forwarded-proto"].decode("latin-1").strip().lower()
|
|
208
|
+
if proto in {"http", "https", "ws", "wss"}:
|
|
209
|
+
# For WebSocket, ensure ws/wss scheme
|
|
210
|
+
if scope["type"] == "websocket":
|
|
211
|
+
if proto == "https":
|
|
212
|
+
scope_dict["scheme"] = "wss"
|
|
213
|
+
elif proto == "http":
|
|
214
|
+
scope_dict["scheme"] = "ws"
|
|
215
|
+
else:
|
|
216
|
+
scope_dict["scheme"] = proto
|
|
217
|
+
else:
|
|
218
|
+
scope_dict["scheme"] = proto
|
|
219
|
+
|
|
220
|
+
# X-Forwarded-For -> scope["client"]
|
|
221
|
+
if b"x-forwarded-for" in headers:
|
|
222
|
+
x_forwarded_for = headers[b"x-forwarded-for"].decode("latin-1")
|
|
223
|
+
real_client = self.trusted_hosts.get_trusted_client_host(x_forwarded_for)
|
|
224
|
+
if real_client:
|
|
225
|
+
scope_dict["client"] = (real_client, 0)
|
|
226
|
+
|
|
227
|
+
# X-Forwarded-Host -> replace Host header
|
|
228
|
+
if self.handle_forwarded_host and b"x-forwarded-host" in headers:
|
|
229
|
+
forwarded_host = headers[b"x-forwarded-host"]
|
|
230
|
+
# Rebuild headers list with replaced Host
|
|
231
|
+
new_headers: list[tuple[bytes, bytes]] = []
|
|
232
|
+
host_replaced = False
|
|
233
|
+
for key, value in scope.get("headers", []): # pyright: ignore[reportUnknownMemberType]
|
|
234
|
+
if key == b"host" and not host_replaced:
|
|
235
|
+
new_headers.append((b"host", forwarded_host))
|
|
236
|
+
host_replaced = True
|
|
237
|
+
else:
|
|
238
|
+
new_headers.append((key, value))
|
|
239
|
+
# If no Host header existed, add it
|
|
240
|
+
if not host_replaced:
|
|
241
|
+
new_headers.append((b"host", forwarded_host))
|
|
242
|
+
scope_dict["headers"] = new_headers
|
|
243
|
+
|
|
244
|
+
await self.app(scope, receive, send)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Static files configuration dataclass."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import TYPE_CHECKING, Any
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from collections.abc import Sequence
|
|
8
|
+
|
|
9
|
+
from litestar.datastructures import CacheControlHeader
|
|
10
|
+
from litestar.openapi.spec import SecurityRequirement
|
|
11
|
+
from litestar.types import (
|
|
12
|
+
AfterRequestHookHandler, # pyright: ignore[reportUnknownVariableType]
|
|
13
|
+
AfterResponseHookHandler, # pyright: ignore[reportUnknownVariableType]
|
|
14
|
+
BeforeRequestHookHandler, # pyright: ignore[reportUnknownVariableType]
|
|
15
|
+
ExceptionHandlersMap,
|
|
16
|
+
Guard, # pyright: ignore[reportUnknownVariableType]
|
|
17
|
+
Middleware,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class StaticFilesConfig:
|
|
23
|
+
"""Configuration for static file serving.
|
|
24
|
+
|
|
25
|
+
This configuration is passed to Litestar's static files router.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
after_request: "AfterRequestHookHandler | None" = None
|
|
29
|
+
after_response: "AfterResponseHookHandler | None" = None
|
|
30
|
+
before_request: "BeforeRequestHookHandler | None" = None
|
|
31
|
+
cache_control: "CacheControlHeader | None" = None
|
|
32
|
+
exception_handlers: "ExceptionHandlersMap | None" = None
|
|
33
|
+
guards: "list[Guard] | None" = None # pyright: ignore[reportUnknownVariableType]
|
|
34
|
+
middleware: "Sequence[Middleware] | None" = None
|
|
35
|
+
opt: "dict[str, Any] | None" = None
|
|
36
|
+
security: "Sequence[SecurityRequirement] | None" = None
|
|
37
|
+
tags: "Sequence[str] | None" = None
|