11
11
from aioquic .buffer import Buffer # type: ignore
12
12
from aioquic .asyncio import QuicConnectionProtocol , serve # type: ignore
13
13
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
15
15
from aioquic .h3 .events import H3Event , HeadersReceived , WebTransportStreamDataReceived , DatagramReceived # type: ignore
16
16
from aioquic .quic .configuration import QuicConfiguration # type: ignore
17
17
from aioquic .quic .connection import stream_is_unidirectional # type: ignore
36
36
_doc_root : str = ""
37
37
38
38
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
+
39
70
class WebTransportH3Protocol (QuicConnectionProtocol ):
40
71
def __init__ (self , * args : Any , ** kwargs : Any ) -> None :
41
72
super ().__init__ (* args , ** kwargs )
42
73
self ._handler : Optional [Any ] = None
43
- self ._http : Optional [H3Connection ] = None
74
+ self ._http : Optional [H3ConnectionWithDatagram04 ] = None
44
75
self ._session_stream_id : Optional [int ] = None
45
76
self ._close_info : Optional [Tuple [int , bytes ]] = None
46
77
self ._capsule_decoder_for_session_stream : H3CapsuleDecoder = \
@@ -49,7 +80,8 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
49
80
50
81
def quic_event_received (self , event : QuicEvent ) -> None :
51
82
if isinstance (event , ProtocolNegotiated ):
52
- self ._http = H3Connection (self ._quic , enable_webtransport = True )
83
+ self ._http = H3ConnectionWithDatagram04 (
84
+ self ._quic , enable_webtransport = True )
53
85
54
86
if self ._http is not None :
55
87
for http_event in self ._http .handle_event (event ):
@@ -80,6 +112,9 @@ def _h3_event_received(self, event: H3Event) -> None:
80
112
81
113
if isinstance (event , WebTransportStreamDataReceived ) and \
82
114
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' )
83
118
self ._receive_data_on_session_stream (
84
119
event .data , event .stream_ended )
85
120
elif self ._handler is not None :
@@ -302,7 +337,16 @@ def send_datagram(self, data: bytes) -> None:
302
337
303
338
:param data: The data to send.
304
339
"""
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 )
306
350
307
351
def stop_stream (self , stream_id : int , code : int ) -> None :
308
352
"""
0 commit comments