{"id":2742,"library":"qh3","title":"qh3: QUIC and HTTP/3","description":"qh3 is a lightweight and fast Python library providing a QUIC and HTTP/3 implementation. It is a actively maintained fork of `aioquic`, designed to be embedded into other Python client and server libraries. It focuses on client-side functionality, features a minimal TLS 1.3 implementation, and does not rely on external C/OpenSSL dependencies, instead using a Rust-based cryptography backend. The library maintains a regular release cadence with updates often several times a month.","status":"active","version":"1.7.1","language":"en","source_language":"en","source_url":"https://github.com/jawah/qh3","tags":["networking","quic","http3","http/3","asyncio","sans-io"],"install":[{"cmd":"pip install qh3","lang":"bash","label":"Install stable version"}],"dependencies":[],"imports":[{"symbol":"QuicConfiguration","correct":"from qh3.quic.configuration import QuicConfiguration"},{"symbol":"connect","correct":"from qh3.asyncio.client import connect"},{"symbol":"H3Connection","correct":"from qh3.h3.connection import H3Connection"},{"symbol":"H3Event","correct":"from qh3.h3.events import H3Event"}],"quickstart":{"code":"import asyncio\nimport ssl\nfrom qh3.quic.configuration import QuicConfiguration\nfrom qh3.asyncio.client import connect\nfrom qh3.h3.connection import H3Connection\nfrom qh3.h3.events import DataReceived, HeadersReceived\n\n\nasync def fetch_http3_url(url: str):\n    # Create a QUIC configuration\n    configuration = QuicConfiguration(\n        is_client=True,\n        alpn_protocols=[\"h3-29\"],\n        max_datagram_frame_size=65536,\n        # Set up a basic TLS context. For production, use proper certificate validation.\n        # In this example, we disable it for simplicity, NOT recommended for production.\n        verify_mode=ssl.CERT_NONE, \n        # You can add root CAs if needed: ciphers=[...], root_cert=b\"...\"\n    )\n\n    print(f\"Connecting to {url}...\")\n    async with connect(url, configuration=configuration) as client:\n        print(f\"Connected to {url}. Client: {client}\")\n        h3_connection = H3Connection(client.quic.configuration)\n\n        # Send a GET request\n        stream_id = client.quic.get_next_available_stream_id()\n        headers = [\n            (b\":method\", b\"GET\"),\n            (b\":scheme\", b\"https\"),\n            (b\":authority\", client.host.encode()),\n            (b\":path\", b\"/\"),\n            (b\"user-agent\", b\"qh3-client\"),\n        ]\n        h3_connection.send_headers(stream_id, headers)\n\n        await client.transmit()\n\n        # Receive response\n        data = b''\n        while True:\n            events = h3_connection.handle_quic_events(client.quic.receive())\n            for event in events:\n                if isinstance(event, HeadersReceived):\n                    print(\"Received headers:\")\n                    for k, v in event.headers:\n                        print(f\"  {k.decode()}: {v.decode()}\")\n                elif isinstance(event, DataReceived):\n                    data += event.data\n            if client.quic.is_idle: # Check if the QUIC connection is idle (all data received/sent)\n                break\n            await asyncio.sleep(0.01) # Yield to event loop\n            \n        print(\"\\n--- Response Body ---\")\n        print(data.decode(errors='ignore')) # Ignore decoding errors for simplicity\n\n# Example Usage:\n# You need a running HTTP/3 server to test this.\n# For local testing, you might use `mkcert` to generate a trusted cert for 'localhost'\n# and run a server like `aioquic/examples/http3_server.py` adapted for qh3.\n# This example targets a public HTTP/3 enabled endpoint for demonstration, \n# but actual connectivity depends on network and server configuration.\n\n# Replace with a known HTTP/3 server if available\n# Note: Public HTTP/3 test servers are not always stable or available.\n# For best results, run a local qh3/aioquic compatible server.\n# Example for aioquic server: `python -m aioquic.examples.http3_server --host 127.0.0.1 --port 4433 cert.pem key.pem`\n# and then use url = \"https://127.0.0.1:4433/\"\n\n# A real-world example would involve a public URL known to support HTTP/3.\n# For this example, we'll try a common one, but it might require a local setup.\n\nurl = os.environ.get('QH3_TEST_URL', 'https://quic.tech:8443/')\n\nasync def main():\n    await fetch_http3_url(url)\n\nif __name__ == '__main__':\n    import os\n    import logging\n    # Set basic logging for better visibility\n    logging.basicConfig(level=logging.INFO)\n    asyncio.run(main())\n","lang":"python","description":"This quickstart demonstrates how to establish an HTTP/3 client connection, send a GET request, and receive the response body. It leverages `asyncio` for asynchronous operations. Users should configure `QuicConfiguration` for certificate validation appropriate for their environment."},"warnings":[{"fix":"Revert changes related to `caextra`. Manage intermediate CAs by including them directly in the main CA bundle if necessary, or by rebuilding the chain discretely as required by the application's TLS context.","message":"The `caextra` configuration option, which allowed passing extra intermediate CAs, was reverted and removed in v1.5.3 after being briefly introduced in v1.5.2. Users should adapt their certificate chain handling if they relied on this feature.","severity":"breaking","affected_versions":">=1.5.3"},{"fix":"Review and update code paths related to cryptographic configurations and TLS context creation. `qh3`'s `QuicConfiguration` should now be used directly, without relying on `cryptography` internals.","message":"For users migrating from `aioquic` or older `qh3` versions (pre-1.0.0), `qh3` no longer depends on the `cryptography` package. This change was part of a major refactor to use a Rust-based cryptography backend (`aws-lc-rs` via `PyO3`), leading to API incompatibilities in cryptographic operations for direct users of these internals.","severity":"breaking","affected_versions":">=1.0.0"},{"fix":"Consult the `qh3` documentation and changelog, comparing against `aioquic` usage, and adapt code for `qh3`'s specific API, especially for QUIC and HTTP/3 connection management and event handling.","message":"`qh3` is a fork of `aioquic` but is not a drop-in replacement, especially since its first major version. While aiming for compatibility, significant internal changes and differing development goals mean direct migration requires careful review of API changes, particularly for low-level QUIC and HTTP/3 interactions.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Ensure ECH functionality is only invoked or expected on the client side. Server implementations should not rely on or attempt to configure ECH through `qh3`.","message":"Encrypted Client Hello (ECH) support, introduced in v1.7.0, is currently only intended for client-side usage and is not supported for server-side implementations.","severity":"gotcha","affected_versions":">=1.7.0"},{"fix":"Users on Windows ARM64 experiencing installation issues may need to build `qh3` from source or await a future release with the corrected wheel. Check the latest GitHub releases or PyPI for updated wheels.","message":"As of v1.7.1, there was a known issue regarding a missing ARM64 pre-built wheel for Windows non-freethreaded builds, potentially affecting users on this specific platform during installation.","severity":"gotcha","affected_versions":"1.7.1"}],"env_vars":null,"last_verified":"2026-04-10T00:00:00.000Z","next_check":"2026-07-09T00:00:00.000Z"}