Skip to content

blackbull.server

blackbull.server

BlackBull's server. Construct Server/ASGIServer directly to embed it in an existing event loop or bind before forking.

Server

An asyncio socket server that dispatches each connection through the app's ProtocolRegistry.

The shared HTTP listener detects HTTP/1.1 vs HTTP/2 (and upgrades to WebSocket); port-bound non-ASGI protocols registered via BlackBull.raw_handler get their own listening socket. When ssl_context or certfile is set, the HTTP listener runs as HTTPS.

ASGIServer is an alias of this class.

certfile property writable

The TLS certificate path, or None.

Assigning a path that is not a file raises FileNotFoundError there and then, rather than at handshake time.

keyfile property writable

The TLS private-key path, or None.

Assigning a path that is not a file raises FileNotFoundError there and then, rather than at handshake time.

ssl_context property writable

The context a listener built from certfile/keyfile uses.

Assigning it re-points every bound listener that was carrying the previous one — which is what makes configuring mTLS after open_socket() work, and what keeps bound_listeners honest about what is actually being served. A listener the caller stated carries its own context and is left alone, because identity is the difference between "the server's" and "its own".

configure_mtls(ca_cert)

Require a client certificate signed by ca_cert.

TLS must already be configured; on a plaintext server this raises RuntimeError rather than silently serving without mTLS.

connection_protocol_factory(bound_binding=None)

One buffered protocol per accept; _AcceptGate serves it.

run(port=80) async

Run an asyncio socket server with the setting in this object.

shutdown() async

Drive the ASGI lifespan shutdown handshake.

Raises RuntimeError if startup() was never called. After a failed startup() it cleans up quietly, so try/finally is safe.

startup() async

Drive the ASGI lifespan startup handshake.

Launches the app's lifespan task, delivers 'lifespan.startup', and waits for 'lifespan.startup.complete'. Raises RuntimeError on 'lifespan.startup.failed'. Stores the context manager so that shutdown() can deliver 'lifespan.shutdown' to the same task.

stop(drain_timeout=8.0) async

Stop accepting, then let the connections already being served finish.

Nothing in flight is cancelled until drain_timeout less min(0.5, drain_timeout / 2); then every connection left is aborted, its unsent output discarded, and its task cancelled, within the deadline. Keep the budget inside MultiWorkerServer.shutdown_timeout so the drain ends here and not in a SIGKILL.