def _h3_event_received(self, event: H3Event) -> None: if isinstance(event, HeadersReceived): # Convert from List[Tuple[bytes, bytes]] to Dict[bytes, bytes]. # Only the last header will be kept when there are duplicate # headers. headers = {} for header, value in event.headers: headers[header] = value method = headers.get(b":method") protocol = headers.get(b":protocol") if method == b"CONNECT" and protocol == b"webtransport": self._session_stream_id = event.stream_id self._handshake_webtransport(event, headers) else: self._send_error_response(event.stream_id, 400) if isinstance(event, DataReceived) and\ self._session_stream_id == event.stream_id: if self._http and not self._http.supports_h3_datagram_04 and\ len(event.data) > 0: raise ProtocolError('Unexpected data on the session stream') self._receive_data_on_session_stream( event.data, event.stream_ended) elif self._handler is not None: if isinstance(event, WebTransportStreamDataReceived): self._handler.stream_data_received( stream_id=event.stream_id, data=event.data, stream_ended=event.stream_ended) elif isinstance(event, DatagramReceived): if self._allow_datagrams: self._handler.datagram_received(data=event.data)
def _receive_data_on_session_stream(self, data: bytes, fin: bool) -> None: self._capsule_decoder_for_session_stream.append(data) if fin: self._capsule_decoder_for_session_stream.final() for capsule in self._capsule_decoder_for_session_stream: if capsule.type in { CapsuleType.DATAGRAM, CapsuleType.REGISTER_DATAGRAM_CONTEXT, CapsuleType.CLOSE_DATAGRAM_CONTEXT }: raise ProtocolError( f"Unimplemented capsule type: {capsule.type}") if capsule.type in { CapsuleType.REGISTER_DATAGRAM_NO_CONTEXT, CapsuleType.CLOSE_WEBTRANSPORT_SESSION }: # We'll handle this case below. pass else: # We should ignore unknown capsules. continue if self._close_info is not None: raise ProtocolError( ("Receiving a capsule with type = {} after receiving " + "CLOSE_WEBTRANSPORT_SESSION").format(capsule.type)) if capsule.type == CapsuleType.REGISTER_DATAGRAM_NO_CONTEXT: buffer = Buffer(data=capsule.data) format_type = buffer.pull_uint_var() # https://ietf-wg-webtrans.github.io/draft-ietf-webtrans-http3/draft-ietf-webtrans-http3.html#name-datagram-format-type WEBTRANPORT_FORMAT_TYPE = 0xff7c00 if format_type != WEBTRANPORT_FORMAT_TYPE: raise ProtocolError( "Unexpected datagram format type: {}".format( format_type)) self._allow_datagrams = True elif capsule.type == CapsuleType.CLOSE_WEBTRANSPORT_SESSION: buffer = Buffer(data=capsule.data) code = buffer.pull_uint32() # 4 bytes for the uint32. reason = buffer.pull_bytes(len(capsule.data) - 4) # TODO(yutakahirano): Make sure `reason` is a UTF-8 text. self._close_info = (code, reason) if fin: self._call_session_closed(self._close_info, abruptly=False)