Request

HTTP request handling and utilities.

Transport-agnostic request system.

Classes:
BaseRequest (ABC) — transport-agnostic interface

├── HttpRequest — ASGI HTTP (stream-based, body via receive) └── MsgRequest — WSX messages (atomic, WebSocket/NATS)

RequestRegistry — factory and tracking for active requests

Every request has an associated Response (request.response). The handler returns a value; the Dispatcher calls response.set_result() and sends the ASGI response.

Request/Response lifecycle:

RequestRegistry.create(scope, receive, send)
    → factory()        # sync: allocate slots
    → request.init()   # async: read body, parse data
    → register in registry
set_current_request(request)
router.node(path) → handler
result = handler(**request.query)
response.set_result(result)
response(scope, receive, send)
set_current_request(None)
registry.unregister()
Notification pattern (fire-and-forget over HTTP):

Some protocols (e.g., JSON-RPC 2.0) define messages that expect no response payload. The handler sets the status code and returns None; the Dispatcher sends an empty HTTP response:

request = get_current_request()
request.response.status_code = 202
return None  # Dispatcher sends 202 with empty body
class genro_asgi.request.BaseRequest[source]

Bases: ABC

Abstract base for transport-agnostic requests.

Subclasses: HttpRequest (ASGI HTTP), MsgRequest (WSX messages). Handlers see only BaseRequest and work identically across transports.

Every instance carries an associated response (Response object). The handler controls the response via request.response: status code, headers, and — indirectly — body (the Dispatcher calls response.set_result() with whatever the handler returns).

For notification/fire-and-forget over HTTP the handler sets request.response.status_code = 202 and returns None.

Abstract properties (subclasses must implement):

id, method, path, headers, cookies, query, data, transport

Concrete attributes:

response: Associated Response object (created in __init__) auth_tags: Security tags injected by AuthMiddleware via scope env_capabilities: Environment flags injected via scope external_id: Client-provided correlation ID (optional) tytx_mode: True when request uses TYTX serialization tytx_transport: TYTX transport type (‘json’, ‘msgpack’) or None app_name: App handling this request (set after routing, for metrics) created_at: Timestamp (epoch) when request was created age: Seconds since creation (computed)

add_cleanup(callback)[source]

Register a callback to run at end of request (always, even on error).

Parameters:

callback (Any) – Callable to invoke. Called in reverse order of registration.

Return type:

None

add_onerr(callback)[source]

Register a callback to run only when the request ends with an error.

Parameters:

callback (Any) – Callable to invoke with the exception. Called in reverse order.

Return type:

None

property age: float

Seconds since request was created.

property app_name: str | None

Name of the app handling this request (set after routing).

property auth_tags: list[str]

Auth tags (set from scope during init by AuthMiddleware).

abstract property cookies: dict[str, str]

Request cookies.

property created_at: float

Timestamp when request was created.

property ctx: Any

Execution context (DictObj set by Dispatcher in scope[“ctx”]).

abstract property data: Any

Request body/payload.

property env_capabilities: list[str]

Environment capabilities (set from scope during init).

property external_id: str | None

Client-provided ID for correlation (e.g., WSX message id).

abstract property headers: dict[str, str]

Request headers (lowercase keys).

abstract property id: str

Correlation ID for request/response matching.

abstractmethod async init(scope, receive, send=None, **kwargs)[source]

Async initialization — parse transport data from ASGI scope.

Subclasses must override to read body/message, populate internal state (headers, cookies, query, data), and extract auth context.

Parameters:
Return type:

None

abstract property method: str

GET, POST, PUT, DELETE, PATCH.

Type:

HTTP method

abstract property path: str

Request path (e.g., ‘/users/42’).

abstract property query: dict[str, Any]

Query parameters.

response: Response
run_cleanups(error=None)[source]

Run registered callbacks. Called by Dispatcher in finally block.

Parameters:

error (BaseException | None) – The exception if request failed, None on success.

Return type:

None

abstract property transport: str

‘http’, ‘websocket’, ‘nats’.

Type:

Transport type

property tytx_mode: bool

True if request uses TYTX serialization.

property tytx_transport: str | None

TYTX transport type (‘json’, ‘msgpack’) or None.

class genro_asgi.request.HttpRequest[source]

Bases: BaseRequest

HTTP request adapter — wraps ASGI scope, parses body via asgi_data.

Supports both TYTX-encoded requests (type hydration for Decimal, date, datetime, time) and standard HTTP requests with plain JSON bodies.

TYTX mode is detected from the X-TYTX-Transport header. When present, Response uses the same transport to serialize the reply. Body parsing is handled by genro_tytx.asgi_data() in both cases (after genro-tytx 0.8.0, plain JSON is parsed correctly without TYTX markers).

Parsing flow (in init()):
  1. Headers decoded from ASGI scope (latin-1)

  2. TYTX mode detected from X-TYTX-Transport header

  3. asgi_data(scope, receive) parses headers, query, cookies, body

  4. Request ID extracted from x-request-id header or generated (UUID)

  5. Auth context read from scope (injected by AuthMiddleware)

Known limitation:

Raw body bytes are not preserved after parsing. asgi_data consumes the ASGI receive callable internally; self.body returns b"".

Extra properties (beyond BaseRequest):

scope, body, scheme, url, headers_obj, query_params, client, state, content_type

property body: bytes

Raw body bytes.

property client: Address | None

Client address (host, port) if available.

property content_type: str | None

Content-Type header value.

property cookies: dict[str, str]

Request cookies.

property data: Any

Request body/payload.

property db: Any

Database connection, lazy-loaded from scope["ctx"]._db.

On first access, reads ctx._db (set by Dispatcher) and registers db.closeConnection as a request cleanup callback.

Returns:

The database connection, or None if ctx is absent or has no _db.

property headers: dict[str, str]

Request headers (lowercase keys).

property headers_obj: Headers

Request headers as Headers object (case-insensitive).

property id: str

Correlation ID for request/response matching.

async init(scope, receive, send=None, **kwargs)[source]

Async init — parse headers, body, query, cookies via asgi_data.

Delegates to genro_tytx.asgi_data(scope, receive) which handles TYTX-encoded and plain JSON bodies transparently (since genro-tytx 0.8.0). After this method:

  • self._data: parsed body (dict for JSON, None if empty/unparseable)

  • self._headers, self._cookies, self._query: populated

  • self._tytx_mode: True if X-TYTX-Transport header was present

  • self._auth_tags, self._env_capabilities: from scope

  • self._body: b”” (raw bytes not preserved — see class docstring)

Return type:

None

property method: str

GET, POST, PUT, DELETE, PATCH.

Type:

HTTP method

property path: str

Request path (e.g., ‘/users/42’).

property query: dict[str, Any]

Query parameters.

property query_params: QueryParams

Query string parameters as QueryParams object.

property scheme: str

http or https.

Type:

URL scheme

property scope: MutableMapping[str, Any]

Raw ASGI scope dict.

property server: Any

Parent AsgiServer instance.

property session: Any

Session object (set by SessionMiddleware via scope).

property state: State

Request-scoped state container.

property transport: str

‘http’, ‘websocket’, ‘nats’.

Type:

Transport type

property url: URL

Full request URL.

class genro_asgi.request.MsgRequest[source]

Bases: BaseRequest

Message-based request adapter (WSX over WebSocket, NATS, etc.).

Parses WSX:// formatted messages into BaseRequest interface. Transport-agnostic: works with any message-based protocol.

property client: tuple[str, int] | None

Client address as raw (host, port) tuple from WebSocket scope.

Unlike HttpRequest.client (which wraps in Address), WSX messages return the raw tuple because WebSocket connections are long-lived and the overhead of wrapping each message is unnecessary.

property cookies: dict[str, str]

Request cookies.

property data: Any

Request body/payload.

property headers: dict[str, str]

Request headers (lowercase keys).

property id: str

Correlation ID for request/response matching.

async init(scope, receive, send=None, **kwargs)[source]

Async initialization - parses WSX message.

Return type:

None

property method: str

GET, POST, PUT, DELETE, PATCH.

Type:

HTTP method

property path: str

Request path (e.g., ‘/users/42’).

property query: dict[str, Any]

Query parameters.

property scope: MutableMapping[str, Any]

Access to raw ASGI scope.

property transport: str

‘http’, ‘websocket’, ‘nats’.

Type:

Transport type

property websocket: WebSocket | None

Access to underlying WebSocket connection (if available).

class genro_asgi.request.RequestRegistry(factories=None)[source]

Bases: object

Registry for creating and tracking active requests.

Responsibilities: - Creates appropriate request based on scope[“type”] using factories dict - Calls async init() on the created request - Tracks active requests for monitoring and metrics - Provides iteration and lookup by request ID

Example

registry = RequestRegistry() request = await registry.create(scope, receive, send) print(f”Active: {len(registry)}”) registry.unregister()

count_by_app(app_name)[source]

Count active requests for a specific app.

Return type:

int

async create(scope, receive, send=None, **kwargs)[source]

Create and register a request from ASGI scope.

Return type:

BaseRequest

property current: BaseRequest | None

Current request from ContextVar.

factories
get(request_id)[source]

Get a request by id.

Return type:

BaseRequest | None

register_factory(scope_type, factory)[source]

Register a factory for a scope type.

Return type:

None

unregister()[source]

Unregister current request.

Return type:

BaseRequest | None

genro_asgi.request.get_current_request()[source]

Get the current request from context. Returns None if not in request context.

Return type:

BaseRequest | None

genro_asgi.request.set_current_request(request)[source]

Set the current request in context. Returns token for reset.

Return type:

Any