1111from aioquic .buffer import Buffer # type: ignore
1212from aioquic .asyncio import QuicConnectionProtocol , serve # type: ignore
1313from 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
1515from aioquic .h3 .events import H3Event , HeadersReceived , WebTransportStreamDataReceived , DatagramReceived # type: ignore
1616from aioquic .quic .configuration import QuicConfiguration # type: ignore
1717from aioquic .quic .connection import stream_is_unidirectional # type: ignore
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+
3970class 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 """
0 commit comments