Skip to content

Commit 3ce28a9

Browse files
authored
[WebTransport] Support latest HTTP datagram (#31049)
Until the protocol is formally implemented by aioquic, we need to talk to clients talking with the protocol. Let's add some ad-hoc support.
1 parent 321ba4a commit 3ce28a9

File tree

7 files changed

+106
-6
lines changed

7 files changed

+106
-6
lines changed

tools/webtransport/h3/webtransport_h3_server.py

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from aioquic.buffer import Buffer # type: ignore
1212
from aioquic.asyncio import QuicConnectionProtocol, serve # type: ignore
1313
from aioquic.asyncio.client import connect # type: ignore
14-
from aioquic.h3.connection import H3_ALPN, FrameType, H3Connection, ProtocolError # type: ignore
14+
from aioquic.h3.connection import H3_ALPN, FrameType, H3Connection, ProtocolError, Setting # type: ignore
1515
from aioquic.h3.events import H3Event, HeadersReceived, WebTransportStreamDataReceived, DatagramReceived # type: ignore
1616
from aioquic.quic.configuration import QuicConfiguration # type: ignore
1717
from aioquic.quic.connection import stream_is_unidirectional # type: ignore
@@ -36,11 +36,42 @@
3636
_doc_root: str = ""
3737

3838

39+
class H3ConnectionWithDatagram04(H3Connection):
40+
"""
41+
A H3Connection subclass, to make it work with the latest
42+
HTTP Datagram protocol.
43+
"""
44+
H3_DATAGRAM_04 = 0xffd277
45+
46+
def __init__(self, *args: Any, **kwargs: Any) -> None:
47+
super().__init__(*args, **kwargs)
48+
self._supports_h3_datagram_04 = False
49+
50+
def _validate_settings(self, settings: Dict[int, int]) -> None:
51+
H3_DATAGRAM_04 = H3ConnectionWithDatagram04.H3_DATAGRAM_04
52+
if H3_DATAGRAM_04 in settings and settings[H3_DATAGRAM_04] == 1:
53+
settings[Setting.H3_DATAGRAM] = 1
54+
self._supports_h3_datagram_04 = True
55+
return super()._validate_settings(settings)
56+
57+
def _get_local_settings(self) -> Dict[int, int]:
58+
H3_DATAGRAM_04 = H3ConnectionWithDatagram04.H3_DATAGRAM_04
59+
settings = super()._get_local_settings()
60+
settings[H3_DATAGRAM_04] = 1
61+
return settings
62+
63+
@property
64+
def supports_h3_datagram_04(self) -> bool:
65+
"""
66+
True if the client supports the latest HTTP Datagram protocol.
67+
"""
68+
return self._supports_h3_datagram_04
69+
3970
class WebTransportH3Protocol(QuicConnectionProtocol):
4071
def __init__(self, *args: Any, **kwargs: Any) -> None:
4172
super().__init__(*args, **kwargs)
4273
self._handler: Optional[Any] = None
43-
self._http: Optional[H3Connection] = None
74+
self._http: Optional[H3ConnectionWithDatagram04] = None
4475
self._session_stream_id: Optional[int] = None
4576
self._close_info: Optional[Tuple[int, bytes]] = None
4677
self._capsule_decoder_for_session_stream: H3CapsuleDecoder =\
@@ -49,7 +80,8 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
4980

5081
def quic_event_received(self, event: QuicEvent) -> None:
5182
if isinstance(event, ProtocolNegotiated):
52-
self._http = H3Connection(self._quic, enable_webtransport=True)
83+
self._http = H3ConnectionWithDatagram04(
84+
self._quic, enable_webtransport=True)
5385

5486
if self._http is not None:
5587
for http_event in self._http.handle_event(event):
@@ -80,6 +112,9 @@ def _h3_event_received(self, event: H3Event) -> None:
80112

81113
if isinstance(event, WebTransportStreamDataReceived) and\
82114
self._session_stream_id == event.stream_id:
115+
if self._http and not self._http.supports_h3_datagram_04 and\
116+
len(event.data) > 0:
117+
raise ProtocolError('Unexpected data on the session stream')
83118
self._receive_data_on_session_stream(
84119
event.data, event.stream_ended)
85120
elif self._handler is not None:
@@ -302,7 +337,16 @@ def send_datagram(self, data: bytes) -> None:
302337
303338
:param data: The data to send.
304339
"""
305-
self._http.send_datagram(flow_id=self.session_id, data=data)
340+
flow_id = self.session_id
341+
if self._http.supports_h3_datagram_04:
342+
# The REGISTER_DATAGRAM_NO_CONTEXT capsule was on the session
343+
# stream, so we must have the ID of the stream.
344+
assert self._protocol._session_stream_id is not None
345+
# TODO(yutakahirano): Make sure if this is the correct logic.
346+
# Chrome always use 0 for the initial stream and the initial flow
347+
# ID, we cannot check the correctness with it.
348+
flow_id = self._protocol._session_stream_id // 4
349+
self._http.send_datagram(flow_id=flow_id, data=data)
306350

307351
def stop_stream(self, stream_id: int, code: int) -> None:
308352
"""

webtransport/close.https.any.js.ini

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[close.https.any.html]
2+
disabled: true
3+
4+
[close.https.any.window.html]
5+
disabled: true
6+
7+
[close.https.any.worker.html]
8+
disabled: true
9+
10+
[close.https.any.sharedworker.html]
11+
disabled: true
12+
13+
[close.https.any.serviceworker.html]
14+
disabled: true

webtransport/connect.any.js.ini

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[connect.any.html]
2+
disabled: true
3+
4+
[connect.any.window.html]
5+
disabled: true
6+
7+
[connect.any.worker.html]
8+
disabled: true
9+
10+
[connect.any.sharedworker.html]
11+
disabled: true
12+
13+
[connect.any.serviceworker.html]
14+
disabled: true

webtransport/csp-pass.window.js.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[csp-pass.window.html]
2+
disabled: true
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[datagrams.https.any.html]
2+
disabled: true
3+
4+
[datagrams.https.any.window.html]
5+
disabled: true
6+
7+
[datagrams.https.any.worker.html]
8+
disabled: true
9+
10+
[datagrams.https.any.sharedworker.html]
11+
disabled: true
12+
13+
[datagrams.https.any.serviceworker.html]
14+
disabled: true

webtransport/resources/webtransport-test-helpers.sub.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,4 @@ function check_and_remove_standard_headers(headers) {
5454
delete headers[':protocol'];
5555
assert_equals(headers['origin'], `${get_host_info().ORIGIN}`);
5656
delete headers['origin'];
57-
assert_equals(headers['datagram-flow-id'], '0');
58-
delete headers['datagram-flow-id'];
5957
}

webtransport/streams-echo.any.js.ini

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[streams-echo.any.html]
2+
disabled: true
3+
4+
[streams-echo.any.window.html]
5+
disabled: true
6+
7+
[streams-echo.any.worker.html]
8+
disabled: true
9+
10+
[streams-echo.any.sharedworker.html]
11+
disabled: true
12+
13+
[streams-echo.any.serviceworker.html]
14+
disabled: true

0 commit comments

Comments
 (0)