channels-webtransport
Experimental WebTransport support for Django Channels, backed by PyWebTransport.
This is deliberately a thin package: it translates a PyWebTransport session into a small, versioned ASGI extension and provides a Channels-style async consumer. It is suitable for experimentation and internal projects while the WebTransport ASGI protocol is still being designed. It is not yet a stable public protocol.
Install
The package requires Python 3.12 or newer.
uv add channels-webtransport
For a local checkout:
uv add --editable ../channels-webtransport
Configure Django
Add the package to INSTALLED_APPS so Django discovers the management command:
INSTALLED_APPS = [
# ...
"channels",
"channels_webtransport",
]
ASGI_APPLICATION = "project.asgi.application"
WEBTRANSPORT = {
"HOST": "127.0.0.1",
"PORT": 4433,
"CERTFILE": BASE_DIR / "certs" / "localhost.crt",
"KEYFILE": BASE_DIR / "certs" / "localhost.key",
# Any remaining PyWebTransport ServerConfig keyword arguments:
"OPTIONS": {},
}
The same root ASGI application can route HTTP, WebSocket, and WebTransport scopes:
# project/asgi.py
import os
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from django.urls import path
from app.consumers import EchoWebTransportConsumer
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings")
application = ProtocolTypeRouter(
{
"http": get_asgi_application(),
"webtransport": URLRouter([path("wt/echo/", EchoWebTransportConsumer.as_asgi())]),
}
)
Create a consumer in the usual Channels style:
# app/consumers.py
from channels_webtransport import AsyncWebTransportConsumer
class EchoWebTransportConsumer(AsyncWebTransportConsumer):
async def receive_datagram(self, data: bytes) -> None:
await self.send_datagram(data)
async def receive_stream(self, stream: int, data: bytes, end_stream: bool) -> None:
await self.send_stream(stream, data, end_stream=end_stream)
Then run the WebTransport listener alongside the normal HTTP/ASGI server:
uv run python manage.py runwebtransport
runwebtransport owns a separate HTTP/3-over-UDP listener. Your normal Daphne/Uvicorn
process continues to serve HTTP and WebSocket traffic. Command-line flags override settings:
uv run python manage.py runwebtransport \
--host 127.0.0.1 --port 4433 \
--certificate certs/localhost.crt --private-key certs/localhost.key
Local TLS and browser testing
WebTransport requires HTTPS and HTTP/3. Generate a short-lived development certificate with PyWebTransport:
mkdir -p certs
uv run python -c \
'from pywebtransport.utils import generate_self_signed_cert; generate_self_signed_cert(hostname="localhost", output_dir="certs", validity_days=13)'
For a Python client, pass ssl.CERT_NONE only in local tests. Browsers require the SHA-256
certificate hash in serverCertificateHashes for a self-signed certificate, and the certificate
must be valid for no more than 14 days. Chromium-based browsers have the broadest development
support. See examples/browser_echo.html for a client.
Serve the example from localhost, paste the certificate hash into it, and open
http://localhost:8000/browser_echo.html:
uv run python -m http.server --directory examples 8000
Consumer test helper
Most consumer logic can be tested without opening a UDP socket:
from channels_webtransport.testing import WebTransportCommunicator
async def test_echo():
communicator = WebTransportCommunicator(EchoWebTransportConsumer.as_asgi(), "/wt/echo/")
connected, _ = await communicator.connect()
assert connected
await communicator.send_datagram(b"hello")
assert await communicator.receive_datagram() == b"hello"
await communicator.disconnect()
The package test suite also starts a real loopback QUIC server and client:
uv run pytest
Experimental ASGI contract
The scope has type="webtransport", ordinary HTTP request fields, and
scope["webtransport"]["spec_version"] == "0.1".
Messages received by the application:
webtransport.connectwebtransport.datagram.receivewithdatawebtransport.stream.openwithstream,direction, andinitiated_bywebtransport.stream.receivewithstream,data, andend_streamwebtransport.stream.resetandwebtransport.stream.stop_sendingwebtransport.disconnectwithcodeandreason
Messages sent by the application:
webtransport.accept, optionally withsubprotocolwebtransport.close, with sessioncode/reason, or pre-accept HTTPstatuswebtransport.datagram.sendwithdatawebtransport.stream.sendwithstream,data, andend_streamwebtransport.stream.resetandwebtransport.stream.stop_receiving
Version 0.1 supports datagrams and client-created bidirectional and unidirectional streams. Server-created streams, a synchronous consumer, and a standardized ASGI extension are outside this first release. HTTP middleware that expects an HTTP scope will not automatically apply; authenticate the CONNECT request using the WebTransport scope headers or a short-lived ticket.
Development
uv sync --all-groups
uv run ruff check .
uv run mypy
uv run pytest
uv build
Licensed under the MIT License.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file channels_webtransport-0.0.1.tar.gz.
File metadata
- Download URL: channels_webtransport-0.0.1.tar.gz
- Upload date:
- Size: 10.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
34cdf7a0cf7fea468abd9bac065e9de586f5d39afcd5119d3e9a970bd217df76
|
|
| MD5 |
d85274989a7aa594476c88615abc1a2b
|
|
| BLAKE2b-256 |
c8a04e3361c7b9d09c5d089089e82043413d207703de08ec49a11c9182323579
|
Provenance
The following attestation bundles were made for channels_webtransport-0.0.1.tar.gz:
Publisher:
release.yml on Dank-del/channels-webtransport
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
channels_webtransport-0.0.1.tar.gz -
Subject digest:
34cdf7a0cf7fea468abd9bac065e9de586f5d39afcd5119d3e9a970bd217df76 - Sigstore transparency entry: 2477219046
- Sigstore integration time:
-
Permalink:
Dank-del/channels-webtransport@69369e904305996680002bbb4c21c4506f1d02fe -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/Dank-del
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@69369e904305996680002bbb4c21c4506f1d02fe -
Trigger Event:
release
-
Statement type:
File details
Details for the file channels_webtransport-0.0.1-py3-none-any.whl.
File metadata
- Download URL: channels_webtransport-0.0.1-py3-none-any.whl
- Upload date:
- Size: 13.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8cb828fcb65c691906bb00eff353e3d4eacda42c1f47515216d8adee709fbcff
|
|
| MD5 |
e96ab8ebcefa8c67931200a434202540
|
|
| BLAKE2b-256 |
092dd2474ce6b7e90dac6549b5eda6e7966ad3433df23da63e6fbee52ca24126
|
Provenance
The following attestation bundles were made for channels_webtransport-0.0.1-py3-none-any.whl:
Publisher:
release.yml on Dank-del/channels-webtransport
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
channels_webtransport-0.0.1-py3-none-any.whl -
Subject digest:
8cb828fcb65c691906bb00eff353e3d4eacda42c1f47515216d8adee709fbcff - Sigstore transparency entry: 2477220209
- Sigstore integration time:
-
Permalink:
Dank-del/channels-webtransport@69369e904305996680002bbb4c21c4506f1d02fe -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/Dank-del
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@69369e904305996680002bbb4c21c4506f1d02fe -
Trigger Event:
release
-
Statement type: