Skip to content

blackbull.grpc.asgi

blackbull.grpc.asgi

ASGI bridge that serves gRPC calls over BlackBull's HTTP/2 layer.

gRPC is HTTP/2 with a fixed request shape (POST /package.Service/Method, content-type: application/grpc) and a Length-Prefixed-Message body, where the call result is reported in grpc-status / grpc-message trailers. BlackBull's HTTP/2 sender already emits trailers via the http.response.trailers ASGI event, and HTTP2Recipient already delivers request DATA as incremental http.request events, so all four RPC kinds — unary, server-, client-, and bidirectional-streaming — map cleanly onto the existing (scope, receive, send) bridge; no new protocol Actor is needed (see .claude/planning/designs/grpc-streaming-request-design.md).

serve_grpc is dispatched from :meth:BlackBull._dispatch when the request content-type is application/grpc and a registry was installed via app.enable_grpc(...).

GrpcContext

Per-call context handed to a gRPC handler.

Exposes request metadata (the HTTP/2 headers), the call deadline and peer, and lets the handler set the outgoing status, a human-readable message, leading/trailing metadata, or abort the call outright — the subset of grpcio's ServicerContext that a raw-bytes transport can honour.

The response-start machinery (_send_started) is bound by :func:serve_grpc just before the handler runs; handlers touch it only via :meth:send_initial_metadata.

abort(status, details='')

Raise :class:GrpcError to end the call with a non-OK status.

invocation_metadata()

Return all request metadata (HTTP/2 headers) as (name, value) pairs — grpcio's ServicerContext.invocation_metadata. Pseudo- headers (:method, :path, …) are excluded; they are call routing, not application metadata.

metadata(name, default=b'')

Return a request header (call metadata) value, or default.

peer()

Return the client address as grpcio formats it (ipv4:host:port / ipv6:[host]:port), or '' when the transport did not supply one.

send_initial_metadata(metadata) async

Send response leading metadata now (the initial HTTP/2 HEADERS), before the first response message — grpcio's ServicerContext.send_initial_metadata.

Optional: if never called, the response HEADERS are still emitted lazily (just before the first message, or with the trailers for an empty response). Calling it flushes them early with metadata attached — used to hand the client leading metadata (auth challenges, stream ids) up front. Raises :class:ValueError once the HEADERS have already gone out (grpcio's "initial metadata no longer allowed").

time_remaining()

Seconds left until the call deadline (never negative), or None when the client set no grpc-timeout — grpcio's ServicerContext.time_remaining. Lets a handler shed work it cannot finish in time.

trailing_metadata()

The trailing metadata set so far (a copy) — lets helpers compose with, rather than clobber, what the handler already set (e.g. blackbull-protobuf's abort_with_details appending grpc-status-details-bin).

serve_grpc(registry, conn, receive, send) async

Serve a single gRPC call (unary or server-streaming) through the ASGI (scope, receive, send) bridge. Never raises for handler/protocol errors: every failure path is reported as a gRPC status.