Skip to content

Server entrypoints

serve

serve(app: ASGIApp, config: Config | None = None) -> None

Start the server. This is the primary programmatic entrypoint and matches the behavior of the h2corn CLI.

On Unix, the call goes through the multi-worker supervisor, even for workers=1, so signal handling, inherited listeners, and worker lifecycle stay identical to the CLI. On Windows, a single in-process worker is used.

Blocks until the server is asked to shut down via one of:

SignalEffect
SIGINT / SIGTERMGraceful shutdown
SIGHUPRolling worker reload
SIGTTINScale workers up by one
SIGTTOUScale workers down by one

Parameters:

NameTypeDescriptionDefault
appASGIApp

An ASGI 3 application callable.

required
configConfig | None

Server configuration. Defaults to Config() (one worker, bound to 127.0.0.1:8000).

None

Example:

from h2corn import Config, serve
from myapp import app

serve(app, Config(bind=('127.0.0.1:8000',), workers=4))

Server

Server(app: ASGIApp, config: Config | None = None)

In-process, single-worker ASGI server.

Use this when you want to embed h2corn inside an existing event loop — for example, inside a test harness, a custom CLI, or an application that manages its own process model. For ordinary deployments, prefer serve, which goes through the multi-worker supervisor and matches the h2corn CLI.

The configured bind listeners are opened, lifespan startup runs, then the server processes requests until shutdown() is called or the surrounding task is cancelled.

Example:

import asyncio
from h2corn import Config, Server

async def main():
    server = Server(app, Config(bind=('127.0.0.1:8000',)))
    await server.serve()

asyncio.run(main())

addressesproperty

addresses: tuple[str, ...]

Resolved listener addresses, in Config.bind string form.

Empty until serve() has bound the listeners. Unlike Config.bind, these carry the port the kernel actually assigned — bind to port 0 and read the address back from here:

server = Server(app, Config(bind=('127.0.0.1:0',)))
task = asyncio.create_task(server.serve())
while not server.addresses:
    await asyncio.sleep(0)
host, _, port = server.addresses[0].rpartition(':')

shutdown

shutdown(kind: Literal['stop', 'restart'] = 'stop') -> None

Initiate a graceful shutdown of an in-flight serve() call.

Safe to call from any thread or coroutine. The currently in-flight requests are given up to Config.timeout_graceful_shutdown seconds to complete before the server returns.

restart

restart() -> None

Equivalent to shutdown('restart').

Used by the supervisor to distinguish a graceful reload from a terminal stop. Outside the supervisor, this behaves the same as shutdown().

serveasync

serve() -> None

Run the server until shutdown.

Binds the configured listeners, runs ASGI lifespan startup, processes requests, then runs lifespan shutdown when the loop exits.

Raises NotImplementedError if Config.workers is not 1; multi-worker deployments must go through serve.