# genro-asgi Architecture Overview **Version**: 2.0.0 **Status**: SOURCE OF TRUTH **Last Updated**: 2026-06-28 --- ## What is genro-asgi genro-asgi is a minimal ASGI framework that provides: - **AsgiServer**: a demultiplexer that mounts one application per first-level URL segment and relays the ASGI call to it - **Configuration**: a Python `config.py` builder recipe rendered onto the server - **Request System**: transport-agnostic request handling (HTTP, WebSocket/WSX) - **Response System**: a single `Response` class with auto content-type detection - **Executors**: thread/process pools for blocking and CPU-bound work - **WSX Protocol**: RPC message format over WebSocket --- ## Core Principles ### 1. Instance isolation, no globals The server is an instance with its own state. Every child object holds a semantic reference to its parent (`self.server`, `self.application`, ...). There are no module-level singletons; `del server` collects everything. ### 2. The server demultiplexes, apps route `AsgiServer` does no routing of its own. The `Dispatcher` picks the app from the first path segment and relays the whole ASGI call; each app does its own routing in `handle_request`. ### 3. Config-driven The server boots from a `config.py` whose `ServerConfiguration` (a subclass of `AsgiConfigBuilder`) is rendered onto it. The builder recipe is the single source of the server's topology (apps, middleware, auth, databases, openapi). ### 4. Transport agnostic Handlers receive a `BaseRequest`; the same handler works over HTTP or WebSocket (WSX), which uses `MsgRequest(BaseRequest)`. --- ## Module Structure ``` src/genro_asgi/ ├── __init__.py # Public exports (52 symbols) ├── __main__.py # CLI entry point ├── types.py # ASGI type definitions ├── exceptions.py # HTTPException, WebSocket exceptions, Redirect ├── request.py # BaseRequest, HttpRequest, MsgRequest, RequestRegistry ├── response.py # Response (set_result / set_error), make_cookie ├── websocket.py # WebSocket connection wrapper ├── lifespan.py # Lifespan, ServerLifespan ├── loader.py # AppLoader (isolated module loading) ├── resources.py # ResourceLoader (hierarchical fallback) ├── storage.py # LocalStorage, StorageNode ├── db.py # AsgiDbHandlerBase ├── config/ # config.py builder model (the live configuration) ├── server/ # server.py, dispatcher.py, auth_mixin.py, worker.py, server_app/ ├── applications/ # AsgiApplication, OpenApiApplication, McpApplication ├── authentication/ # Backward-compatible shim (logic in server/auth_mixin.py) ├── datastructures/ # Headers, URL, QueryParams, State, Address ├── executors/ # BaseExecutor, LocalExecutor, ThreadExecutor, ExecutorRegistry ├── middleware/ # Auto-discovery + 7 middleware classes ├── routers/ # StaticRouter ├── session/ # Session, Avatar, MemorySessionStore ├── sys_applications/ # genro_api, login_page, plugin_config, swagger ├── utils/ # split_and_strip and small helpers └── wsx/ # WSX protocol (handler, protocol, registry) ``` --- ## Architecture Documents | Document | Description | |----------|-------------| | [01-server.md](01-server.md) | AsgiServer, config.py boot, mount, dispatch | | [02-request-system.md](02-request-system.md) | BaseRequest, HttpRequest, MsgRequest, RequestRegistry | | [03-response-system.md](03-response-system.md) | The single Response class | | [04-executors.md](04-executors.md) | Blocking/CPU task execution | | [05-lifespan.md](05-lifespan.md) | Startup/shutdown lifecycle | | [06-wsx-protocol.md](06-wsx-protocol.md) | WSX message format | | [07-streaming.md](07-streaming.md) | Streaming and protections | | [08-routing.md](08-routing.md) | Demultiplex + per-app routing | | [09-auth-context.md](09-auth-context.md) | Authentication and context | --- ## Quick Start The server boots from a `config.py`. You do not subclass `AsgiServer`; you write a `ServerConfiguration` recipe and point the server at it. ```python # config.py from genro_asgi.config import AsgiConfigBuilder from my_app import Application as MyApp # an AsgiApplication subclass class ServerConfiguration(AsgiConfigBuilder): def main(self, root): root.server(host="127.0.0.1", port=8000) root.middleware(cors=True) apps = root.applications(default="main") apps.application(code="main", app_class=MyApp) ``` ```python from genro_asgi import AsgiServer server = AsgiServer("config.py") # the config.py directory is the server dir server.run() # uvicorn on the configured host/port ``` --- ## Key Design Decisions ### No envelope abstraction Requests are the trackable unit; there is no `RequestEnvelope`/`ResponseEnvelope` wrapper. `RequestRegistry` tracks the in-flight request. ### Single `request.py` and single `Response` All request classes live in `request.py`; there is a single `Response` class in `response.py` (no `JSONResponse`/`HTMLResponse`/... hierarchy). `set_result()` picks the content type from the result. ### WSX is the message protocol; handling lives in request.py `wsx/` holds the WSX message format (`protocol.py`), the connection handler (`handler.py`) and the connection registry (`registry.py`). WSX request handling uses `MsgRequest(BaseRequest)` from `request.py`. --- **Copyright**: Softwell S.r.l. (2025-2026) **License**: Apache License 2.0