aioquic - Man Page
Name
aioquic — aioquic LicenseVersionPython versionsTestsCoverage
aioquic is a library for the QUIC network protocol in Python. It features several APIs:
- a Quic API following the "bring your own I/O" pattern, suitable for embedding in any framework,
- an HTTP/3 API which also follows the "bring your own I/O" pattern,
- a QUIC convenience API built on top of asyncio, Python's standard asynchronous I/O framework.
Design
Sans-IO APIs
Both the QUIC and the HTTP/3 APIs follow the sans I/O pattern, leaving actual I/O operations to the API user. This approach has a number of advantages including making the code testable and allowing integration with different concurrency models.
TLS and encryption
TLS 1.3
aioquic features a minimal TLS 1.3 implementation built upon the cryptography library. This is because QUIC requires some APIs which are currently unavailable in mainstream TLS implementations such as OpenSSL:
- the ability to extract traffic secrets
- the ability to operate directly on TLS messages, without using the TLS record layer
Header protection and payload encryption
QUIC makes extensive use of cryptographic operations to protect QUIC packet headers and encrypt packet payloads. These operations occur for every single packet and are a determining factor for performance. For this reason, they are implemented as a C extension linked to OpenSSL.
Quic API
The QUIC API performs no I/O on its own, leaving this to the API user. This allows you to integrate QUIC in any Python application, regardless of the concurrency model you are using.
Connection
- class aioquic.quic.connection.QuicConnection(*, configuration, original_destination_connection_id=None, retry_source_connection_id=None, session_ticket_fetcher=None, session_ticket_handler=None, token_handler=None)
A QUIC connection.
The state machine is driven by three kinds of sources:
- the API user requesting data to be send out (see connect(), reset_stream(), send_ping(), send_datagram_frame() and send_stream_data())
- data being received from the network (see receive_datagram())
- a timer firing (see handle_timer())
- Parameters
configuration (QuicConfiguration) -- The QUIC configuration to use.
- change_connection_id()
Switch to the next available connection ID and retire the previous one.
- NOTE:
After calling this method you need to call the QUIC connection datagrams_to_send() method to retrieve data which needs to be sent over the network. If you are using the asyncio API, calling the transmit() method will do it for you.
- Return type
- close(error_code=QuicErrorCode.NO_ERROR, frame_type=None, reason_phrase='')
Close the connection.
- NOTE:
After calling this method you need to call the QUIC connection datagrams_to_send() method to retrieve data which needs to be sent over the network. If you are using the asyncio API, calling the transmit() method will do it for you.
- Parameters
- Return type
- connect(addr, now)
Initiate the TLS handshake.
This method can only be called for clients and a single time.
- NOTE:
After calling this method you need to call the QUIC connection datagrams_to_send() method to retrieve data which needs to be sent over the network. If you are using the asyncio API, calling the transmit() method will do it for you.
- Parameters
- Return type
- datagrams_to_send(now)
Return a list of (data, addr) tuples of datagrams which need to be sent, and the network address to which they need to be sent.
After calling this method call get_timer() to know when the next timer needs to be set.
- get_next_available_stream_id(is_unidirectional=False)
Return the stream ID for the next stream created by this endpoint.
- Return type
- get_timer()
Return the time at which the timer should fire or None if no timer is needed.
- handle_timer(now)
Handle the timer.
- next_event()
Retrieve the next event from the event buffer.
Returns None if there are no buffered events.
- Return type
Optional[QuicEvent]
- receive_datagram(data, addr, now)
Handle an incoming datagram.
- NOTE:
After calling this method you need to call the QUIC connection datagrams_to_send() method to retrieve data which needs to be sent over the network. If you are using the asyncio API, calling the transmit() method will do it for you.
- Parameters
- Return type
- request_key_update()
Request an update of the encryption keys.
- NOTE:
After calling this method you need to call the QUIC connection datagrams_to_send() method to retrieve data which needs to be sent over the network. If you are using the asyncio API, calling the transmit() method will do it for you.
- Return type
- reset_stream(stream_id, error_code)
Abruptly terminate the sending part of a stream.
- NOTE:
After calling this method you need to call the QUIC connection datagrams_to_send() method to retrieve data which needs to be sent over the network. If you are using the asyncio API, calling the transmit() method will do it for you.
- Parameters
- Return type
- send_datagram_frame(data)
Send a DATAGRAM frame.
- send_ping(uid)
Send a PING frame to the peer.
- NOTE:
After calling this method you need to call the QUIC connection datagrams_to_send() method to retrieve data which needs to be sent over the network. If you are using the asyncio API, calling the transmit() method will do it for you.
- Parameters
uid (int) -- A unique ID for this PING.
- Return type
- send_stream_data(stream_id, data, end_stream=False)
Send data on the specific stream.
- NOTE:
After calling this method you need to call the QUIC connection datagrams_to_send() method to retrieve data which needs to be sent over the network. If you are using the asyncio API, calling the transmit() method will do it for you.
- Parameters
- Return type
- stop_stream(stream_id, error_code)
Request termination of the receiving part of a stream.
- NOTE:
After calling this method you need to call the QUIC connection datagrams_to_send() method to retrieve data which needs to be sent over the network. If you are using the asyncio API, calling the transmit() method will do it for you.
- Parameters
- Return type
Configuration
- class aioquic.quic.configuration.QuicConfiguration(alpn_protocols=None, congestion_control_algorithm='reno', connection_id_length=8, idle_timeout=60.0, is_client=True, max_data=1048576, max_datagram_size=1200, max_stream_data=1048576, quic_logger=None, secrets_log_file=None, server_name=None, session_ticket=None, token=b'', cadata=None, cafile=None, capath=None, certificate=None, certificate_chain=<factory>, cipher_suites=None, initial_rtt=0.1, max_datagram_frame_size=None, original_version=None, private_key=None, quantum_readiness_test=False, supported_versions=<factory>, verify_mode=None)
A QUIC configuration.
- alpn_protocols: Optional[List[str]] = None
A list of supported ALPN protocols.
- congestion_control_algorithm: str = 'reno'
The name of the congestion control algorithm to use.
Currently supported algorithms: "reno", `"cubic".
- connection_id_length: int = 8
The length in bytes of local connection IDs.
- idle_timeout: float = 60.0
The idle timeout in seconds.
The connection is terminated if nothing is received for the given duration.
- is_client: bool = True
Whether this is the client side of the QUIC connection.
- load_cert_chain(certfile, keyfile=None, password=None)
Load a private key and the corresponding certificate.
- Return type
- load_verify_locations(cafile=None, capath=None, cadata=None)
Load a set of "certification authority" (CA) certificates used to validate other peers' certificates.
- Return type
- max_data: int = 1048576
Connection-wide flow control limit.
- max_datagram_size: int = 1200
The maximum QUIC payload size in bytes to send, excluding UDP or IP overhead.
- max_stream_data: int = 1048576
Per-stream flow control limit.
- quic_logger: Optional[QuicLogger] = None
The QuicLogger instance to log events to.
- secrets_log_file: TextIO = None
A file-like object in which to log traffic secrets.
This is useful to analyze traffic captures with Wireshark.
- server_name: Optional[str] = None
The server name to use when verifying the server's TLS certificate, which can either be a DNS name or an IP address.
If it is a DNS name, it is also sent during the TLS handshake in the Server Name Indication (SNI) extension.
NOTE:
This is only used by clients.
- session_ticket: Optional[SessionTicket] = None
The TLS session ticket which should be used for session resumption.
- token: bytes = b''
The address validation token that can be used to validate future connections.
NOTE:
This is only used by clients.
- class aioquic.quic.logger.QuicLogger
A QUIC event logger which stores traces in memory.
Events
- class aioquic.quic.events.QuicEvent
Base class for QUIC events.
- class aioquic.quic.events.ConnectionTerminated(error_code, frame_type, reason_phrase)
The ConnectionTerminated event is fired when the QUIC connection is terminated.
- error_code: int
The error code which was specified when closing the connection.
- frame_type: Optional[int]
The frame type which caused the connection to be closed, or None.
- reason_phrase: str
The human-readable reason for which the connection was closed.
- class aioquic.quic.events.HandshakeCompleted(alpn_protocol, early_data_accepted, session_resumed)
The HandshakeCompleted event is fired when the TLS handshake completes.
- alpn_protocol: Optional[str]
The protocol which was negotiated using ALPN, or None.
- early_data_accepted: bool
Whether early (0-RTT) data was accepted by the remote peer.
- session_resumed: bool
Whether a TLS session was resumed.
- class aioquic.quic.events.PingAcknowledged(uid)
The PingAcknowledged event is fired when a PING frame is acknowledged.
- uid: int
The unique ID of the PING.
- class aioquic.quic.events.StopSendingReceived(error_code, stream_id)
The StopSendingReceived event is fired when the remote peer requests stopping data transmission on a stream.
- error_code: int
The error code that was sent from the peer.
- stream_id: int
The ID of the stream that the peer requested stopping data transmission.
- class aioquic.quic.events.StreamDataReceived(data, end_stream, stream_id)
The StreamDataReceived event is fired whenever data is received on a stream.
- data: bytes
The data which was received.
- end_stream: bool
Whether the STREAM frame had the FIN bit set.
- stream_id: int
The ID of the stream the data was received for.
- class aioquic.quic.events.StreamReset(error_code, stream_id)
The StreamReset event is fired when the remote peer resets a stream.
- error_code: int
The error code that triggered the reset.
- stream_id: int
The ID of the stream that was reset.
HTTP/3 API
The HTTP/3 API performs no I/O on its own, leaving this to the API user. This allows you to integrate HTTP/3 in any Python application, regardless of the concurrency model you are using.
Connection
- class aioquic.h3.connection.H3Connection(quic, enable_webtransport=False)
A low-level HTTP/3 connection object.
- Parameters
quic (QuicConnection) -- A QuicConnection instance.
- create_webtransport_stream(session_id, is_unidirectional=False)
Create a WebTransport stream and return the stream ID.
- NOTE:
After calling this method you need to call the QUIC connection datagrams_to_send() method to retrieve data which needs to be sent over the network. If you are using the asyncio API, calling the transmit() method will do it for you.
- Parameters
- Return type
- handle_event(event)
Handle a QUIC event and return a list of HTTP events.
- Parameters
event (QuicEvent) -- The QUIC event to handle.
- Return type
List[H3Event]
- property received_settings: Dict[int, int] | None
Return the received SETTINGS frame, or None.
- send_data(stream_id, data, end_stream)
Send data on the given stream.
- NOTE:
After calling this method you need to call the QUIC connection datagrams_to_send() method to retrieve data which needs to be sent over the network. If you are using the asyncio API, calling the transmit() method will do it for you.
- Parameters
- Return type
- send_datagram(stream_id, data)
Send a datagram for the specified stream.
If the stream ID is not a client-initiated bidirectional stream, an InvalidStreamTypeError exception is raised.
- NOTE:
After calling this method you need to call the QUIC connection datagrams_to_send() method to retrieve data which needs to be sent over the network. If you are using the asyncio API, calling the transmit() method will do it for you.
- Parameters
- Return type
- send_headers(stream_id, headers, end_stream=False)
Send headers on the given stream.
- NOTE:
After calling this method you need to call the QUIC connection datagrams_to_send() method to retrieve data which needs to be sent over the network. If you are using the asyncio API, calling the transmit() method will do it for you.
- Parameters
- Return type
- send_push_promise(stream_id, headers)
Send a push promise related to the specified stream.
Returns the stream ID on which headers and data can be sent.
If the stream ID is not a client-initiated bidirectional stream, an InvalidStreamTypeError exception is raised.
If there are not available push IDs, an NoAvailablePushIDError exception is raised.
- NOTE:
After calling this method you need to call the QUIC connection datagrams_to_send() method to retrieve data which needs to be sent over the network. If you are using the asyncio API, calling the transmit() method will do it for you.
- Parameters
- Return type
- property sent_settings: Dict[int, int] | None
Return the sent SETTINGS frame, or None.
Events
- class aioquic.h3.events.H3Event
Base class for HTTP/3 events.
- class aioquic.h3.events.DatagramReceived(data, stream_id)
The DatagramReceived is fired whenever a datagram is received from the the remote peer.
- data: bytes
The data which was received.
- stream_id: int
The ID of the stream the data was received for.
- class aioquic.h3.events.DataReceived(data, stream_id, stream_ended, push_id=None)
The DataReceived event is fired whenever data is received on a stream from the remote peer.
- data: bytes
The data which was received.
- push_id: Optional[int] = None
The Push ID or None if this is not a push.
- stream_ended: bool
Whether the STREAM frame had the FIN bit set.
- stream_id: int
The ID of the stream the data was received for.
- class aioquic.h3.events.HeadersReceived(headers, stream_id, stream_ended, push_id=None)
The HeadersReceived event is fired whenever headers are received.
- headers: List[Tuple[bytes, bytes]]
The headers.
- push_id: Optional[int] = None
The Push ID or None if this is not a push.
- stream_ended: bool
Whether the STREAM frame had the FIN bit set.
- stream_id: int
The ID of the stream the headers were received for.
- class aioquic.h3.events.PushPromiseReceived(headers, push_id, stream_id)
The PushedStreamReceived event is fired whenever a pushed stream has been received from the remote peer.
- headers: List[Tuple[bytes, bytes]]
The request headers.
- push_id: int
The Push ID of the push promise.
- stream_id: int
The Stream ID of the stream that the push is related to.
- class aioquic.h3.events.WebTransportStreamDataReceived(data, stream_id, stream_ended, session_id)
The WebTransportStreamDataReceived is fired whenever data is received for a WebTransport stream.
- data: bytes
The data which was received.
- session_id: int
The ID of the session the data was received for.
- stream_ended: bool
Whether the STREAM frame had the FIN bit set.
- stream_id: int
The ID of the stream the data was received for.
Exceptions
- class aioquic.h3.exceptions.H3Error
Base class for HTTP/3 exceptions.
- class aioquic.h3.exceptions.InvalidStreamTypeError
An action was attempted on an invalid stream type.
- class aioquic.h3.exceptions.NoAvailablePushIDError
There are no available push IDs left, or push is not supported by the remote party.
Asyncio API
The asyncio API provides a high-level Quic API built on top of asyncio, Python's standard asynchronous I/O framework.
aioquic comes with a selection of examples, including:
- an HTTP/3 client
- an HTTP/3 server
The examples can be browsed on GitHub:
https://github.com/aiortc/aioquic/tree/main/examples
Client
- async with aioquic.asyncio.connect(host, port, *, configuration=None, create_protocol=<class 'aioquic.asyncio.protocol.QuicConnectionProtocol'>, session_ticket_handler=None, stream_handler=None, token_handler=None, wait_connected=True, local_port=0)
Connect to a QUIC server at the given host and port.
connect() returns an awaitable. Awaiting it yields a QuicConnectionProtocol which can be used to create streams.
connect() also accepts the following optional arguments: :rtype: AsyncGenerator[QuicConnectionProtocol, None]
- configuration is a QuicConfiguration configuration object.
- create_protocol allows customizing the Protocol that manages the connection. It should be a callable or class accepting the same arguments as QuicConnectionProtocol and returning an instance of QuicConnectionProtocol or a subclass.
- session_ticket_handler is a callback which is invoked by the TLS engine when a new session ticket is received.
- stream_handler is a callback which is invoked whenever a stream is created. It must accept two arguments: a asyncio.StreamReader and a asyncio.StreamWriter.
- wait_connected indicates whether the context manager should wait for the connection to be established before yielding the QuicConnectionProtocol. By default this is True but you can set it to False if you want to immediately start sending data using 0-RTT.
- local_port is the UDP port number that this client wants to bind.
Server
- await aioquic.asyncio.serve(host, port, *, configuration, create_protocol=<class 'aioquic.asyncio.protocol.QuicConnectionProtocol'>, session_ticket_fetcher=None, session_ticket_handler=None, retry=False, stream_handler=None)
Start a QUIC server at the given host and port.
serve() requires a QuicConfiguration containing TLS certificate and private key as the configuration argument.
serve() also accepts the following optional arguments: :rtype: QuicServer
- create_protocol allows customizing the Protocol that manages the connection. It should be a callable or class accepting the same arguments as QuicConnectionProtocol and returning an instance of QuicConnectionProtocol or a subclass.
- session_ticket_fetcher is a callback which is invoked by the TLS engine when a session ticket is presented by the peer. It should return the session ticket with the specified ID or None if it is not found.
- session_ticket_handler is a callback which is invoked by the TLS engine when a new session ticket is issued. It should store the session ticket for future lookup.
- retry specifies whether client addresses should be validated prior to the cryptographic handshake using a retry packet.
- stream_handler is a callback which is invoked whenever a stream is created. It must accept two arguments: a asyncio.StreamReader and a asyncio.StreamWriter.
Common
- class aioquic.asyncio.QuicConnectionProtocol(quic, stream_handler=None)
- change_connection_id()
Change the connection ID used to communicate with the peer.
The previous connection ID will be retired.
- Return type
- close(error_code=QuicErrorCode.NO_ERROR, reason_phrase='')
Close the connection.
- connect(addr, transmit=True)
Initiate the TLS handshake.
This method can only be called for clients and a single time.
- Return type
- await create_stream(is_unidirectional=False)
Create a QUIC stream and return a pair of (reader, writer) objects.
The returned reader and writer objects are instances of asyncio.StreamReader and asyncio.StreamWriter classes.
- Return type
Tuple[StreamReader, StreamWriter]
- await ping()
Ping the peer and wait for the response.
- Return type
- quic_event_received(event)
Called when a QUIC event is received.
Reimplement this in your subclass to handle the events.
- Return type
- request_key_update()
Request an update of the encryption keys.
- Return type
- transmit()
Send pending datagrams to the peer and arm the timer if needed.
This method is called automatically when data is received from the peer or when a timer goes off. If you interact directly with the underlying QuicConnection, make sure you call this method whenever data needs to be sent out to the network.
- Return type
- await wait_closed()
Wait for the connection to be closed.
- Return type
- await wait_connected()
Wait for the TLS handshake to complete.
- Return type
Changelog
1.2.0
- Add support for compatible version handling as defined in RFC 9368.
- Add support for QUIC Version 2, as defined in RFC 9369.
- Drop support for draft QUIC versions which were obsoleted by RFC 9000.
- Improve datagram padding to allow better packet coalescing and reduce the number of roundtrips during connection establishement.
- Fix server anti-amplification checks during address validation to take into account invalid packets, such as datagram-level padding.
- Allow asyncio clients to make efficient use of 0-RTT by passing wait_connected=False to connect().
- Add command-line arguments to the http3_client example for client certificates and negotiating QUIC Version 2.
1.1.0
1.0.0
- Ensure no data is sent after a stream reset.
- Make H3Connection's send_datagram() and send_push_promise() methods raise an InvalidStreamTypeError exception if an invalid stream ID is specified.
- Improve the documentation for QuicConnectionProtocol's transmit() method.
- Fix utcnow() deprecation warning on Python 3.12 by using cryptography 42.0 and timezone-aware datetime instances when validating TLS certificates.
- Build binary wheels against OpenSSL 3.2.0.
- Ignore any non-ASCII ALPN values received.
- Perform more extensive HTTP/3 header validation in H3Connection.
- Fix exceptions when draining stream writers in the asyncio API.
- Set the QuicConnection idle timer according to RFC 9000 section 10.1.
- Implement fairer stream scheduling in QuicConnection to avoid head-of-line blocking.
- Only load certifi root certificates if none was specified in the QuicConfiguration.
- Improve padding of UDP datagrams containing Initial packets to comply with RFC 9000 section 14.1.
- Limit the number of pending connection IDs marked for retirement to prevent a possible DoS attack.
License
Copyright (c) Jeremy Lainé. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of aioquic nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Author
Jeremy Lainé
Copyright
Jeremy Lainé