Connection Layer

hface offers ready-to-use server and client, but the true power of this library is in its connection layer offered by the hface.connections module.

This module should allow others to build new HTTP servers or clients on the top of hface. The idea is to allow tools to implement their logic using the HTTPConnection class. This class abstract differences between HTTP versions and their implementation.

Custom server example

The following example shows a minimal HTTP server:

import asyncio

from hface import ServerTLSConfig
from hface.connections import HTTPConnection, HTTPOverTCPListener
from hface.events import ConnectionTerminated, HeadersReceived
from hface.protocols.http1 import HTTP1ServerFactory


async def handler(connection: HTTPConnection):
    print(f"Connection from {connection.remote_address}")
    while True:
        event = await connection.receive_event()
        if isinstance(event, HeadersReceived):
            response_headers = [(b":status", b"200")]
            await connection.send_headers(event.stream_id, response_headers)
            await connection.send_data(event.stream_id, b"It works!\n", end_stream=True)
        elif isinstance(event, ConnectionTerminated):
            break


async def main():
    tls_config = ServerTLSConfig(
        certfile="certs/cert.pem",
        keyfile="certs/key.pem",
    )
    async with await HTTPOverTCPListener.create(
        HTTP1ServerFactory(),
        local_address=("localhost", 5443),
        tls_config=tls_config,
    ) as listener:
        await listener.serve(handler)


if __name__ == "__main__":
    asyncio.run(main())

(The example should run as it is. It is available in examples/custom_server.py)

HTTP connections are handled in the handler() function. The example function consumes HTTP events: when it receives request headers, it sends an HTTP response (ignoring any request body).

The handler() function can process connection of any HTTP version. From the :status pseudo header, the example may appear like HTTP/2, but it uses HTTP/1. hface translates requests and responses to and from one common format.

You can modify the main() function to support other HTTP versions:

Custom client example

The next example is a simple HTTP client:

import asyncio

from hface import ClientTLSConfig
from hface.connections import HTTPConnection, HTTPOverTCPOpener
from hface.events import ConnectionTerminated, DataReceived, HeadersReceived
from hface.protocols.http1 import HTTP1ClientFactory


async def make_request(connection: HTTPConnection):
    stream_id = connection.get_available_stream_id()
    headers = [
        (b":method", b"GET"),
        (b":scheme", b"https"),
        (b":authority", b"localhost"),
        (b":path", b"/"),
    ]
    await connection.send_headers(stream_id, headers, end_stream=True)
    data = b""
    end = False
    while not end:
        event = await connection.receive_event()
        if isinstance(event, HeadersReceived):
            end = event.end_stream
        elif isinstance(event, DataReceived):
            data += event.data
            end = event.end_stream
        elif isinstance(event, ConnectionTerminated):
            end = True
    return data


async def main():
    tls_config = ClientTLSConfig(
        cafile="certs/cacert.pem",
    )
    open_connection = HTTPOverTCPOpener(
        HTTP1ClientFactory(),
        tls_config=tls_config,
    )
    async with await open_connection(("localhost", 5443), tls=True) as connection:
        data = await make_request(connection)
    print(data.decode())


if __name__ == "__main__":
    asyncio.run(main())

(The example should run as it is. It is available in examples/custom_client.py)

The make_request() function send an HTTP request and waits for HTTP events with response headers and response data.

The function can work with any HTTP version:

Servers advertise HTTP/3 support using an Alt-Svc header, which is not processed by hface yet. Selection between TCP and QUIC connections has to be implemented in a higher layer.

How does it work?

The HTTPConnection class provides an unified interface to HTTP connections, independently on their version.

Each connection combines a sans-IO HTTPProtocol with a AnyIO stream for IO:

Both the sans-io and IO parts are represented by an interface (abstract base classes), so it is possible to plug in own implementations.

HTTP connections cannot be construct directly. To accept server connections, you need an implementation of the HTTPListener interface:

To open client connection, you need an implementation of the HTTPOpener interface:

The sans-IO classes are documented at the Sans-IO Protocols page.

Default networking is SystemNetworking, which implements both ServerNetworking and ClientNetworking.

Connection API

The Connection class

class hface.connections.HTTPConnection(transport)

An HTTP connection

This class unifies access to all HTTP connections. Internally, it combines Sans-IO HTTPProtocol with a network stream. The former allows to swap HTTP versions and implementations, the latter allows to proxy traffic or use alternative IO.

This class should not be initialized directly (at least for now). It is returned from HTTPListener or HTTPOpener implementations.

Parameters

transport (Transport) – not a part of public API

property http_version: str

An HTTP version as a string.

property multiplexed: bool

Whether this connection supports multiple parallel streams.

Returns True for HTTP/2 and HTTP/3 connections.

property local_address: Tuple[str, int]

Local network address

The address is intended for logging and/or troubleshooting.

property remote_address: Tuple[str, int]

Remote network address

The address is intended for logging and/or troubleshooting.

property error_codes: HTTPErrorCodes

Error codes suitable for this HTTP connection.

The error codes can be used send_stream_reset()

property extra_attributes: Mapping[Any, Callable[[], Any]]

Implements anyio.TypedAttributeProvider

This method returns attributes from an underlying network stream.

await __aenter__()

Calls open() when this class is used as an async context manager.

Returns

self

Return type

HTTPConnection

await __aexit__(exc_type, exc_val, exc_tb)

Calls aclose() when this class is used as an async context manager.

await open()

Sends protocol preamble if needed.

await aclose()

Close this connection.

is_available()

Return whether this connection is capable to open new streams.

Return type

bool

get_available_stream_id()

Return an ID that can be used to create a new stream.

Use the returned ID with send_headers() to create the stream. This method may or may not return one value until that method is called.

Returns

stream ID

Return type

int

await send_headers(stream_id, headers, end_stream=False)

Send a frame with HTTP headers.

If this is a client connection, this method starts an HTTP request. If this is a server connection, it starts an HTTP response.

Parameters
  • stream_id (int) – stream ID

  • headers (Sequence[Tuple[bytes, bytes]]) – HTTP headers

  • end_stream (bool) – whether to close the stream for sending

await send_data(stream_id, data, end_stream=False)

Send a frame with HTTP data.

Parameters
  • stream_id (int) – stream ID

  • data (bytes) – payload

  • end_stream (bool) – whether to close the stream for sending

await send_stream_reset(stream_id, error_code=0)

Immediately terminate a stream.

Stream reset is used to request cancellation of a stream or to indicate that an error condition has occurred.

Use error_codes to obtain error codes for common problems.

Parameters
  • stream_id (int) – stream ID

  • error_code (int) – indicates why the stream is being terminated

await receive_event()

Receive next HTTP event.

Returns

an event instance

Return type

Event

Listeners

class hface.connections.HTTPListener

Interface for listeners that accept HTTP connections

abstractmethod await serve(handler, task_group=None)

Accept incoming connections as they come in and start tasks to handle them.

Parameters
  • handler (Callable[[HTTPConnection], Any]) – a callable that will be used to handle each accepted connection

  • task_group (Optional[TaskGroup]) – AnyIO task group that will be used to start tasks for accepted connection (if omitted, an ad-hoc task group will be created)

abstractmethod await aclose()

Close the resource.

class hface.connections.HTTPOverTCPListener(http_factory, network_listener)

Accepts HTTP connections from a TCP socket.

Implements HTTPListener.

Parameters
  • http_factory (HTTPOverTCPFactory) – a factory that will construct a Sans-IO protocol for each accepted connection.

  • network_listener (Listener[ByteStream]) – AnyIO listener that will accept TCP connections.

classmethod await create(http_factory, networking=None, *, local_address, tls_config)

Create a new listener for the given local address.

Parameters
  • http_factory (HTTPOverTCPFactory) – a factory that will construct a Sans-IO protocol for each accepted connection.

  • networking (Optional[TCPServerNetworking]) – networking implementation to use. Defaults to SystemNetworking

  • local_address (Tuple[str, int]) – an IP address and a port number to listen on.

  • tls_config (hface._configuration.ServerTLSConfig | None) – TLS configuration for secure (https) connections. None for insecure (http) connections.

Return type

HTTPOverTCPListener

class hface.connections.HTTPOverQUICListener(http_factory, network_listener, *, tls_config)

Accepts HTTP connections over QUIC (at a UDP socket).

Implements HTTPListener.

Parameters
  • http_factory (HTTPOverQUICServerFactory) – a factory that will construct a Sans-IO protocol for each accepted connection.

  • network_listener (Listener[QUICStream]) – listener that will accept QUIC connections.

  • tls_config (ServerTLSConfig) – TLS configuration

classmethod await create(http_factory, networking=None, *, local_address, tls_config)

Create a new listener for the given local address.

Parameters
Return type

HTTPOverQUICListener

class hface.connections.HTTPMultiListener(listeners)

Combines multiple HTTP listeners into one.

Implements HTTPListener.

Parameters

listeners (Sequence[HTTPListener]) – listeners to serve

Openers

class hface.connections.HTTPOpener

Opens HTTP connections.

abstractmethod await __call__(remote_address, tls=False, *, server_name=None)

Open connection to the given origin.

Param

an IP address and a port number to connect to

Parameters
  • tls (bool) – whether to secure the connection using TLS

  • server_name (Optional[str]) – override server name sent in TLS SNI

Returns

a new HTTP connection

Return type

HTTPConnection

class hface.connections.HTTPOverTCPOpener(http_factory, networking=None, *, tls_config=None)

Opens a new HTTP connections over a TPC.

Implements HTTPOpener.

Parameters
class hface.connections.HTTPOverQUICOpener(http_factory, networking=None, *, tls_config=None)

Opens a new HTTP connections over a QUIC.

Implements HTTPOpener.

Parameters

Networking API

Networking backends

class hface.networking.SystemNetworking

Default networking implementation that uses system sockets.

Implements ServerNetworking and ClientNetworking

await listen_tcp(local_address)

Listen for insecure TCP connections at the given local address.

Parameters

local_address (Tuple[str, int]) – an IP address and a port number to listen on

Returns

a new listener instance

Return type

Listener[ByteStream]

await listen_tcp_tls(local_address, *, tls_config, alpn_protocols=None)

Listen for TCP connections secured using TLS at the given local address.

Parameters
  • local_address (Tuple[str, int]) – an IP address and a port number to listen on

  • tls_config (ServerTLSConfig) – TLS configuration

  • alpn_protocols (Optional[Sequence[str]]) – ALPN protocols to offer in a TLS handshake

Returns

a new listener instance

Return type

Listener[ByteStream]

await listen_quic(local_address, *, quic_connection_id_length, quic_supported_versions)

Listen for secure QUIC connections at the given local address.

Parameters

local_address (Tuple[str, int]) – an IP address and a port number to listen on

Returns

a new listener instance

Return type

Listener[QUICStream]

await connect_tcp(remote_address)

Create an insecure TCP connection to the given address.

Parameters

remote_address (Tuple[str, int]) – an IP address and a port number to connect to

Returns

a new byte stream

Return type

ByteStream

await connect_tcp_tls(remote_address, *, tls_config=None, server_name=None, alpn_protocols=None)

Create a TCP connection to the given address secured using TLS.

Parameters
  • remote_address (Tuple[str, int]) – an IP address and a port number to connect to

  • tls_config (Optional[ClientTLSConfig]) – TLS configuration

  • server_name (Optional[str]) – override server name sent in TLS SNI

  • alpn_protocols (Optional[Sequence[str]]) – ALPN protocols to offer in a TLS handshake

Returns

a new byte stream

Return type

ByteStream

await connect_udp(remote_address)

Create a UDP socket that can send packets to the given address.

The socket is not connected (it can be used to send packets to any address), but this will likely change in the future.

Parameters

remote_address (Tuple[str, int]) – Remote address. Used to choose between IPv4 and IPv6

Returns

a new datagram stream

Return type

DatagramStream

class hface.networking.ServerNetworking

Interface for classes that provide networking for servers.

Combines TCPServerNetworking and QUICServerNetworking.

class hface.networking.TCPServerNetworking

Interface for classes that provide networking for TCP servers.

Allows to start listening for TCP connections, optionally secured using TLS.

abstractmethod await listen_tcp(local_address)

Listen for insecure TCP connections at the given local address.

Parameters

local_address (Tuple[str, int]) – an IP address and a port number to listen on

Returns

a new listener instance

Return type

Listener[ByteStream]

await listen_tcp_tls(local_address, *, tls_config, alpn_protocols=None)

Listen for TCP connections secured using TLS at the given local address.

Parameters
  • local_address (Tuple[str, int]) – an IP address and a port number to listen on

  • tls_config (ServerTLSConfig) – TLS configuration

  • alpn_protocols (Optional[Sequence[str]]) – ALPN protocols to offer in a TLS handshake

Returns

a new listener instance

Return type

Listener[ByteStream]

class hface.networking.QUICServerNetworking

Interface for classes that provide networking for QUIC servers.

Allows to start listening for QUIC connections.

abstractmethod await listen_quic(local_address, *, quic_connection_id_length, quic_supported_versions)

Listen for secure QUIC connections at the given local address.

Parameters

local_address (Tuple[str, int]) – an IP address and a port number to listen on

Returns

a new listener instance

Return type

Listener[QUICStream]

class hface.networking.ClientNetworking

Networking for HTTP clients

Combines TCPClientNetworking and UDPClientNetworking.

class hface.networking.TCPClientNetworking

Interface for classes that provide networking for TCP clients.

Allows to open TCP connections, optionally secured using TLS.

abstractmethod await connect_tcp(remote_address)

Create an insecure TCP connection to the given address.

Parameters

remote_address (Tuple[str, int]) – an IP address and a port number to connect to

Returns

a new byte stream

Return type

ByteStream

await connect_tcp_tls(remote_address, *, tls_config=None, server_name=None, alpn_protocols=None)

Create a TCP connection to the given address secured using TLS.

Parameters
  • remote_address (Tuple[str, int]) – an IP address and a port number to connect to

  • tls_config (Optional[ClientTLSConfig]) – TLS configuration

  • server_name (Optional[str]) – override server name sent in TLS SNI

  • alpn_protocols (Optional[Sequence[str]]) – ALPN protocols to offer in a TLS handshake

Returns

a new byte stream

Return type

ByteStream

class hface.networking.UDPClientNetworking

Interface for classes that provide networking for UDP clients.

Allows to bind UDP ports.

abstractmethod await connect_udp(remote_address)

Create a UDP socket that can send packets to the given address.

The socket is not connected (it can be used to send packets to any address), but this will likely change in the future.

Parameters

remote_address (Tuple[str, int]) – Remote address. Used to choose between IPv4 and IPv6

Returns

a new datagram stream

Return type

DatagramStream

Streams

class hface.networking.ByteStream

Alias of anyio.abc.ByteStream

class hface.networking.DatagramStream

Alias of anyio.abc.UnreliableObjectStream[hface.DatagramType]

class hface.networking.QUICStream

Datagram stream aware of QUIC connection IDs.

HTTP/3 abstraction used by hface is designed as HTTP-over-UDP, hiding the QUIC in the middle. So QUIC streams extend hface.networking.DatagramStream.

This abstraction is not sufficient at one place - HTTP/3 servers need to share one UDP socket between multiple connections. In order to make that work, users of QUIC server sockets have to call the update_connection_ids() method.

abstractmethod update_connection_ids(connection_ids)

Update QUIC connection IDs received by this stream.

Parameters

connection_ids (Sequence[bytes]) – connection IDs receive by this stream