korvex.middleware¶
Ready-made hooks. See Ready-made middleware and Sessions and CSRF.
Ready-made hooks (CORS, request IDs, rate limiting, compression, security headers, sessions, CSRF).
Guide: https://3d3n-pyc.github.io/korvex/middleware/
RequestIdFilter ¶
Logging filter that adds request_id to records (- outside a request).
Guide: https://3d3n-pyc.github.io/korvex/middleware/#request-ids-request_idapp
RateLimitStore ¶
Where rate limit state lives: hit decides for one request, sync or async.
Guide: https://3d3n-pyc.github.io/korvex/middleware/#your-own-store
Decision
dataclass
¶
Decision(allowed: bool, remaining: int, retry_after: float, reset: float)
A rate limit store's answer for one request.
allowed: whether the request may go ahead.remaining: requests left before the limit.retry_after: seconds until the next request would be allowed (0 if allowed).reset: seconds until the key's whole allowance is back.
MemoryStore ¶
MemoryStore(*, clock: Callable[[], float] = monotonic)
Rate limit state in this process's memory, one store per worker.
Algorithm: GCRA, safe with several threads.
Guide: https://3d3n-pyc.github.io/korvex/middleware/#where-the-counts-live
RedisStore ¶
RedisStore(client: Any = 'redis://localhost:6379/0', *, prefix: str = 'korvex:ratelimit:', fail_open: bool = True)
Rate limit state in Redis, shared by every worker and machine.
Needs pip install korvex[redis].
client: a URL, or aredis.asyncio.Redisyou made. Connections open on first use; from a URL, each event loop gets its own client.prefix: put before each key.fail_open: if Redis is unreachable, allow requests (default) or refuse them with a 429; the error is logged either way.
Same algorithm as MemoryStore, run atomically on Redis's clock.
Guide: https://3d3n-pyc.github.io/korvex/middleware/#where-the-counts-live
Session ¶
Session(data: Optional[dict[str, Any]] = None)
The data kept for one browser between requests, saved in a signed cookie.
Use it like a dict. Values must be JSON-serialisable.
Setting, deleting or clearing a key sets modified; a change inside a nested
value is not seen, so set session.modified = True yourself after it.
Guide: https://3d3n-pyc.github.io/korvex/sessions/
Starts with data, not modified.
cors ¶
cors(app: App, *, allow_origins: Sequence[str] | str, allow_methods: Sequence[str] = ALL_METHODS, allow_headers: Sequence[str] | str = (), allow_credentials: bool = False, expose_headers: Sequence[str] = (), max_age: int = 600) -> None
Lets browsers call the app from other origins (CORS).
allow_origins: origins like"https://example.com", or"*".allow_methods: methods a cross-origin request may use.allow_headers: request headers allowed beyond the basic ones, or"*".allow_credentials: whether cookies may be sent. With"*"origins, the request's origin is sent back instead of*, as browsers require.expose_headers: response headers the page's code may read.max_age: seconds a preflight answer may be cached.
Guide: https://3d3n-pyc.github.io/korvex/middleware/#cors-cors-app-allow-origins
request_id ¶
request_id(app: App, *, header: str = 'X-Request-ID', trust_incoming: bool = True) -> None
Gives every request an ID, sent back in the response and put in the logs.
header: the header the ID is read from and sent back in.trust_incoming: reuse a safe incoming ID, else make a new one.
The built-in server's access logs show it too. Guide: https://3d3n-pyc.github.io/korvex/middleware/#request-ids-request_idapp
current_request_id ¶
current_request_id() -> str | None
The ID of the request being handled, if request_id() is set up.
rate_limit ¶
rate_limit(app: App, *, requests: int, per: float, key: Callable[[Connection], Optional[Hashable]] = _client_address, store: RateLimitStore | None = None, headers: bool = True) -> None
Allows each key requests requests per per seconds; beyond, a 429 with Retry-After.
key(request): who the limit applies to, the client's address by default (unknown addresses share one limit);Noneexempts a request.store: where the state lives, aMemoryStoreby default.headers: send RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset.
Runs as a before hook: hooks declared before it (like cors()) run first.
Guide: https://3d3n-pyc.github.io/korvex/middleware/#rate-limiting-rate_limitapp-requests100-per60
compress ¶
compress(app: App, *, minimum_size: int = 500, encodings: Sequence[str] | None = None, levels: dict[str, int] | None = None, content_types: Sequence[str] = COMPRESSIBLE) -> None
Compresses responses for clients that accept it.
encodings: in order of preference;("zstd", "br", "gzip")by default (bris left out of the default on free-threaded Python, but can be asked for). Unavailable ones are skipped.minimum_size: smaller bodies are sent as they are; streams are always compressed.levels: per encoding, like{"gzip": 9}.content_types: prefixes liketext/or suffixes like+json.
Call it first: after hooks run last-declared first. Compressed responses
get Vary: Accept-Encoding. Don't compress pages mixing a secret with
client input (BREACH), over HTTPS too.
Guide: https://3d3n-pyc.github.io/korvex/middleware/#compression-compressapp
security_headers ¶
security_headers(app: App, *, hsts: int | None = 31536000, hsts_subdomains: bool = False, hsts_preload: bool = False, content_security_policy: str | None = None, frame_options: str | None = 'DENY', content_type_options: bool = True, referrer_policy: str | None = 'strict-origin-when-cross-origin', cross_origin_opener_policy: str | None = 'same-origin', permissions_policy: str | None = None) -> None
Adds the usual security headers to every response; one a response sets keeps its own.
hsts:Strict-Transport-Securitymax-age in seconds,Noneto leave it out; sent only whenrequest.schemeishttps(set by a trusted proxy); raises ValueError if negative.hsts_subdomains,hsts_preload: add those directives.content_security_policy: off by default, like"default-src 'self'".frame_options:X-Frame-Options.content_type_options: sendX-Content-Type-Options: nosniff.referrer_policy,cross_origin_opener_policy,permissions_policy: those headers;Noneleaves one out.
Guide: https://3d3n-pyc.github.io/korvex/middleware/#security-headers-security_headersapp
sessions ¶
sessions(app: App, *, secret_key: Union[str, bytes, Sequence[Union[str, bytes]]], max_age: int = 14 * 24 * 3600, cookie: str = 'session', secure: bool = True, samesite: str = 'lax', path: str = '/', domain: Optional[str] = None) -> None
Keeps a Session per browser in a signed, HttpOnly cookie; read it with current_session().
secret_key: at least 32 bytes; a list rotates keys (the first signs, all verify).max_age: seconds a session stays valid.cookie,secure,samesite,path,domain: the cookie's name and attributes.
Only a session that was read and changed is sent; the cookie is limited to 4096 bytes. Guide: https://3d3n-pyc.github.io/korvex/sessions/
current_session ¶
current_session() -> Session
The session of the request being handled, read from its cookie on first use.
A missing, expired or tampered cookie gives an empty session.
Raises RuntimeError outside a request or without sessions(app, ...).
In a WebSocket it reads the handshake's cookie; changes cannot be saved.
Guide: https://3d3n-pyc.github.io/korvex/sessions/
flash ¶
flash(message: str, category: str = 'info') -> None
Keeps a message in the session to show on the next page.
message: the text.category: a label such as "info" or "error".
Guide: https://3d3n-pyc.github.io/korvex/sessions/
get_flashed_messages ¶
get_flashed_messages() -> list[tuple[str, str]]
Returns the (category, message) pairs kept by flash() and forgets them.
Guide: https://3d3n-pyc.github.io/korvex/sessions/
csrf ¶
csrf(app: App, *, trusted_origins: Iterable[str] = (), exempt: Iterable[str] = ()) -> None
Refuses cross-site requests that change things, without tokens (CSRF).
It trusts what the browser sends (Sec-Fetch-Site, else Origin against Host).
GET, HEAD and OPTIONS pass; WebSocket handshakes are always checked. Requests with neither header pass.
trusted_origins: other origins allowed, like"https://admin.example.com".exempt: paths to skip: exact ("/hook") or ending in/for a prefix ("/webhooks/").
Guide: https://3d3n-pyc.github.io/korvex/sessions/