Skip to content

blackbull.middleware

blackbull.middleware

Public middleware exports.

Legacy Middleware-suffixed aliases and compress emit DeprecationWarning; use the unsuffixed classes for new code.

CORS

Cross-Origin Resource Sharing (CORS) middleware.

Handles preflight OPTIONS requests and attaches CORS headers to actual cross-origin responses. Requests without an Origin header pass through unchanged.

Parameters:

Name Type Description Default
allow_origins list[str] | str

Explicit origin strings or ['*'] for wildcard.

'*'
allow_methods list[str] | None

HTTP methods permitted in cross-origin requests. Defaults to ['GET', 'POST', 'HEAD', 'OPTIONS'].

None
allow_headers list[str] | str

Request headers permitted; ['*'] allows all.

'*'
allow_credentials bool

Emit Access-Control-Allow-Credentials: true. Cannot be combined with allow_origins=['*'].

False
expose_headers list[str] | None

Response headers the browser JS may read.

None
max_age int | None

Preflight cache lifetime in seconds. None omits the header.

600

Usage::

app = BlackBull()
app.use(CORS(
    allow_origins=['https://myapp.example.com'],
    allow_credentials=True,
    max_age=3600,
))

__init__(allow_origins='*', allow_methods=None, allow_headers='*', allow_credentials=False, expose_headers=None, max_age=600)

Build the middleware.

allow_credentials=True cannot be combined with allow_origins=['*']: the pair raises ValueError. A wildcard origin with credentials is what the CORS specification forbids, not something this middleware chooses to refuse.

Cache

Per-worker in-memory response cache.

Compression

ASGI middleware: compress the response body using the best codec the client accepts (br > zstd > gzip, in server-preference order).

Bodies smaller than min_size bytes are forwarded uncompressed. Responses with already-compressed Content-Types (image/, video/, etc.) are forwarded uncompressed. brotli and zstandard are optional — if not installed the middleware falls back gracefully to gzip or no compression.

BlackBull middleware convention::

from blackbull.middleware import Compression

@app.route(path='/', middlewares=[Compression()])
async def handler(conn, receive, send): ...

StaticFiles

Serve files from one directory, as route middleware or on its own.

app.static(url_prefix, root_dir) attaches it to a route; called with three arguments instead of four it is a plain ASGI app that can be mounted anywhere. The difference shows up on a miss: with a call_next the request continues down the chain, and without one it is answered 404.

Only GET and HEAD are served. A resolved path outside the root is a 400, checked after symlinks are followed, so a link pointing out of the tree does not escape it. Under BLACKBULL_ENV=production nothing is served at all — a production deployment is expected to have a proxy or CDN in front, and two things serving the same files is the problem being avoided.

What a hit produces depends on the request and on what is on disk. ETag and Last-Modified are emitted unless conditional is off, and a matching If-None-Match / If-Modified-Since gets a 304 without the body being read. A Range request gets a 206, or a 416 when the range is unsatisfiable. A precompressed sibling (app.js.br, .zst, .gz) is served in place of the original when the client accepts that encoding and the request is not a range request; those responses carry Vary: Accept-Encoding. Files larger than four megabytes stream in chunks rather than being read whole, so peak memory does not follow file size.

docs/guide/static-files.md covers the choices this leaves open — the opt-in cache in particular, and why it is off by default.

__init__(directory=None, *, url_prefix='', root_dir=None, cache=False, index=None, conditional=True)

Serve directory or root_dir; at least one must be supplied.

index=None disables directory indexes. cache=True holds small bodies in a bounded per-worker cache; its validation TTL can delay on-disk edits. conditional=False disables validators and 304 responses. See docs/guide/static-files.md for cache, traversal and production-mode limits.

TrustedProxy

Rewrite conn.client and conn.scheme only behind configured trusted peers.

Proxies must append the observed peer or overwrite the chain. Standalone proto/prefix assertions must replace client-supplied values. Forwarded takes precedence over X-Forwarded-For/Proto; walk right to left to the first untrusted IP. Trust defaults to loopback. See the reverse-proxy guide.

as_middleware(target)

Decorate a function or class with (conn, receive, send, call_next) middleware.

Normalize downstream HTTP sends to NativeResponse before the middleware's send wrapper. A first request parameter named scope opts into ASGI scope and event dictionaries at both edges, including WebSocket events. Omit the decorator to handle raw downstream send arguments.