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:
The example uses
HTTPOverTCPListener
withHTTP1ServerFactory
to listen for HTTP/1 connections.The factory class can be replaced by
HTTP2ServerFactory
for HTTP/2 connections.
HTTPOverQUICListener
together withHTTP3ServerFactory
offer HTTP/3 support.And finally,
HTTPMultiListener
andALPNHTTPFactory
can provide support for multiple HTTP versions at once.
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:
The example uses
HTTPOverTCPOpener
withHTTP1ClientFactory
to open HTTP/1 connections.The factory class can be replaced by
HTTP2ClientFactory
for HTTP/2 connections.
HTTPOverQUICOpener
together withHTTP3ClientFactory
offer HTTP/3 support.
ALPNHTTPFactory
can choose between HTTP/1 and HTTP/2.
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:
For HTTP/1 and HTTP/2:
HTTPOverTCPProtocol
provides sans-IO logicByteStream
can be used to receive and send data.
For HTTP/3:
HTTPOverQUICProtocol
provides sans-IO logicDatagramStream
can be used to receive and send data.
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:
HTTPOverTCPListener
can accept HTTP/1 and HTTP/2 connections. It consists of:HTTPOverTCPFactory
as the sans-IO part, which creates instances ofHTTPOverTCPProtocol
.TCPServerNetworking
to provide IO, which can listen forByteStream
connections.
HTTPOverQUICListener
can accept HTTP/3 connections. It consists of:HTTPOverQUICServerFactory
as the sans-IO part, which creates instances ofHTTPOverQUICProtocol
.QUICServerNetworking
to provide IO, which can listen forQUICStream
connections (QUICStream
extendsDatagramStream
).
To open client connection, you need an implementation of the HTTPOpener
interface:
HTTPOverTCPOpener
can open HTTP/1 and HTTP/2 connections. It consists of:HTTPOverTCPFactory
as the sans-io part, which creates instances ofHTTPOverTCPProtocol
.TCPClientNetworking
, which can openByteStream
connections.
HTTPOverQUICOpener
can open HTTP/3 connections. It consists of:HTTPOverQUICClientFactory
as the sans-io part, which creates instances ofHTTPOverQUICProtocol
.UDPClientNetworking
, which can openDatagramStream
connections.
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
orHTTPOpener
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
- 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
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
- 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
http_factory (HTTPOverQUICServerFactory) – a factory that will construct a Sans-IO protocol for each accepted connection.
networking (Optional[QUICServerNetworking]) – networking implementation to use. Defaults to
SystemNetworking
local_address (Tuple[str, int]) – an IP address and a port number to listen on.
tls_config (ServerTLSConfig) – TLS configuration
- Return type
- 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
- class hface.connections.HTTPOverTCPOpener(http_factory, networking=None, *, tls_config=None)¶
Opens a new HTTP connections over a TPC.
Implements
HTTPOpener
.- Parameters
http_factory (HTTPOverTCPFactory) – a factory that will construct a Sans-IO protocol for each opened connection.
networking (TCPClientNetworking | None) – networking implementation to use. Defaults to
SystemNetworking
tls_config (ClientTLSConfig | None) – TLS configuration for secure (https) connections
- class hface.connections.HTTPOverQUICOpener(http_factory, networking=None, *, tls_config=None)¶
Opens a new HTTP connections over a QUIC.
Implements
HTTPOpener
.- Parameters
http_factory (HTTPOverQUICClientFactory) – a factory that will construct a Sans-IO protocol for each opened connection.
networking (UDPClientNetworking | None) – networking implementation to use. Defaults to
SystemNetworking
tls_config (ClientTLSConfig | None) – TLS configuration for secure (https) connections
Networking API¶
Networking backends¶
- class hface.networking.SystemNetworking¶
Default networking implementation that uses system sockets.
Implements
ServerNetworking
andClientNetworking
- 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
- 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
- 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
- class hface.networking.ServerNetworking¶
Interface for classes that provide networking for servers.
Combines
TCPServerNetworking
andQUICServerNetworking
.
- 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
andUDPClientNetworking
.
- 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
- 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
- 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
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