%PDF- %PDF-
Direktori : /backups/router/usr/local/lib/python3.11/site-packages/aioquic/quic/ |
Current File : //backups/router/usr/local/lib/python3.11/site-packages/aioquic/quic/stream.py |
from typing import Optional from . import events from .packet import ( QuicErrorCode, QuicResetStreamFrame, QuicStopSendingFrame, QuicStreamFrame, ) from .packet_builder import QuicDeliveryState from .rangeset import RangeSet class FinalSizeError(Exception): pass class StreamFinishedError(Exception): pass class QuicStreamReceiver: """ The receive part of a QUIC stream. It finishes: - immediately for a send-only stream - upon reception of a STREAM_RESET frame - upon reception of a data frame with the FIN bit set """ def __init__(self, stream_id: Optional[int], readable: bool) -> None: self.highest_offset = 0 # the highest offset ever seen self.is_finished = False self.stop_pending = False self._buffer = bytearray() self._buffer_start = 0 # the offset for the start of the buffer self._final_size: Optional[int] = None self._ranges = RangeSet() self._stream_id = stream_id self._stop_error_code: Optional[int] = None def get_stop_frame(self) -> QuicStopSendingFrame: self.stop_pending = False return QuicStopSendingFrame( error_code=self._stop_error_code, stream_id=self._stream_id, ) def starting_offset(self) -> int: return self._buffer_start def handle_frame( self, frame: QuicStreamFrame ) -> Optional[events.StreamDataReceived]: """ Handle a frame of received data. """ pos = frame.offset - self._buffer_start count = len(frame.data) frame_end = frame.offset + count # we should receive no more data beyond FIN! if self._final_size is not None: if frame_end > self._final_size: raise FinalSizeError("Data received beyond final size") elif frame.fin and frame_end != self._final_size: raise FinalSizeError("Cannot change final size") if frame.fin: self._final_size = frame_end if frame_end > self.highest_offset: self.highest_offset = frame_end # fast path: new in-order chunk if pos == 0 and count and not self._buffer: self._buffer_start += count if frame.fin: # all data up to the FIN has been received, we're done receiving self.is_finished = True return events.StreamDataReceived( data=frame.data, end_stream=frame.fin, stream_id=self._stream_id ) # discard duplicate data if pos < 0: frame.data = frame.data[-pos:] frame.offset -= pos pos = 0 count = len(frame.data) # marked received range if frame_end > frame.offset: self._ranges.add(frame.offset, frame_end) # add new data gap = pos - len(self._buffer) if gap > 0: self._buffer += bytearray(gap) self._buffer[pos : pos + count] = frame.data # return data from the front of the buffer data = self._pull_data() end_stream = self._buffer_start == self._final_size if end_stream: # all data up to the FIN has been received, we're done receiving self.is_finished = True if data or end_stream: return events.StreamDataReceived( data=data, end_stream=end_stream, stream_id=self._stream_id ) else: return None def handle_reset( self, *, final_size: int, error_code: int = QuicErrorCode.NO_ERROR ) -> Optional[events.StreamReset]: """ Handle an abrupt termination of the receiving part of the QUIC stream. """ if self._final_size is not None and final_size != self._final_size: raise FinalSizeError("Cannot change final size") # we are done receiving self._final_size = final_size self.is_finished = True return events.StreamReset(error_code=error_code, stream_id=self._stream_id) def on_stop_sending_delivery(self, delivery: QuicDeliveryState) -> None: """ Callback when a STOP_SENDING is ACK'd. """ if delivery != QuicDeliveryState.ACKED: self.stop_pending = True def stop(self, error_code: int = QuicErrorCode.NO_ERROR) -> None: """ Request the peer stop sending data on the QUIC stream. """ self._stop_error_code = error_code self.stop_pending = True def _pull_data(self) -> bytes: """ Remove data from the front of the buffer. """ try: has_data_to_read = self._ranges[0].start == self._buffer_start except IndexError: has_data_to_read = False if not has_data_to_read: return b"" r = self._ranges.shift() pos = r.stop - r.start data = bytes(self._buffer[:pos]) del self._buffer[:pos] self._buffer_start = r.stop return data class QuicStreamSender: """ The send part of a QUIC stream. It finishes: - immediately for a receive-only stream - upon acknowledgement of a STREAM_RESET frame - upon acknowledgement of a data frame with the FIN bit set """ def __init__(self, stream_id: Optional[int], writable: bool) -> None: self.buffer_is_empty = True self.highest_offset = 0 self.is_finished = not writable self.reset_pending = False self._acked = RangeSet() self._acked_fin = False self._buffer = bytearray() self._buffer_fin: Optional[int] = None self._buffer_start = 0 # the offset for the start of the buffer self._buffer_stop = 0 # the offset for the stop of the buffer self._pending = RangeSet() self._pending_eof = False self._reset_error_code: Optional[int] = None self._stream_id = stream_id @property def next_offset(self) -> int: """ The offset for the next frame to send. This is used to determine the space needed for the frame's `offset` field. """ try: return self._pending[0].start except IndexError: return self._buffer_stop def get_frame( self, max_size: int, max_offset: Optional[int] = None ) -> Optional[QuicStreamFrame]: """ Get a frame of data to send. """ assert self._reset_error_code is None, "cannot call get_frame() after reset()" # get the first pending data range try: r = self._pending[0] except IndexError: if self._pending_eof: # FIN only self._pending_eof = False return QuicStreamFrame(fin=True, offset=self._buffer_fin) self.buffer_is_empty = True return None # apply flow control start = r.start stop = min(r.stop, start + max_size) if max_offset is not None and stop > max_offset: stop = max_offset if stop <= start: return None # create frame frame = QuicStreamFrame( data=bytes( self._buffer[start - self._buffer_start : stop - self._buffer_start] ), offset=start, ) self._pending.subtract(start, stop) # track the highest offset ever sent if stop > self.highest_offset: self.highest_offset = stop # if the buffer is empty and EOF was written, set the FIN bit if self._buffer_fin == stop: frame.fin = True self._pending_eof = False return frame def get_reset_frame(self) -> QuicResetStreamFrame: self.reset_pending = False return QuicResetStreamFrame( error_code=self._reset_error_code, final_size=self.highest_offset, stream_id=self._stream_id, ) def on_data_delivery( self, delivery: QuicDeliveryState, start: int, stop: int, fin: bool ) -> None: """ Callback when sent data is ACK'd. """ # If the frame had the FIN bit set, its end MUST match otherwise # we have a programming error. assert ( not fin or stop == self._buffer_fin ), "on_data_delivered() was called with inconsistent fin / stop" # If a reset has been requested, stop processing data delivery. # The transition to the finished state only depends on the reset # being acknowledged. if self._reset_error_code is not None: return if delivery == QuicDeliveryState.ACKED: if stop > start: # Some data has been ACK'd, discard it. self._acked.add(start, stop) first_range = self._acked[0] if first_range.start == self._buffer_start: size = first_range.stop - first_range.start self._acked.shift() self._buffer_start += size del self._buffer[:size] if fin: # The FIN has been ACK'd. self._acked_fin = True if self._buffer_start == self._buffer_fin and self._acked_fin: # All data and the FIN have been ACK'd, we're done sending. self.is_finished = True else: if stop > start: # Some data has been lost, reschedule it. self.buffer_is_empty = False self._pending.add(start, stop) if fin: # The FIN has been lost, reschedule it. self.buffer_is_empty = False self._pending_eof = True def on_reset_delivery(self, delivery: QuicDeliveryState) -> None: """ Callback when a reset is ACK'd. """ if delivery == QuicDeliveryState.ACKED: # The reset has been ACK'd, we're done sending. self.is_finished = True else: # The reset has been lost, reschedule it. self.reset_pending = True def reset(self, error_code: int) -> None: """ Abruptly terminate the sending part of the QUIC stream. """ assert self._reset_error_code is None, "cannot call reset() more than once" self._reset_error_code = error_code self.reset_pending = True # Prevent any more data from being sent or re-sent. self.buffer_is_empty = True def write(self, data: bytes, end_stream: bool = False) -> None: """ Write some data bytes to the QUIC stream. """ assert self._buffer_fin is None, "cannot call write() after FIN" assert self._reset_error_code is None, "cannot call write() after reset()" size = len(data) if size: self.buffer_is_empty = False self._pending.add(self._buffer_stop, self._buffer_stop + size) self._buffer += data self._buffer_stop += size if end_stream: self.buffer_is_empty = False self._buffer_fin = self._buffer_stop self._pending_eof = True class QuicStream: def __init__( self, stream_id: Optional[int] = None, max_stream_data_local: int = 0, max_stream_data_remote: int = 0, readable: bool = True, writable: bool = True, ) -> None: self.is_blocked = False self.max_stream_data_local = max_stream_data_local self.max_stream_data_local_sent = max_stream_data_local self.max_stream_data_remote = max_stream_data_remote self.receiver = QuicStreamReceiver(stream_id=stream_id, readable=readable) self.sender = QuicStreamSender(stream_id=stream_id, writable=writable) self.stream_id = stream_id @property def is_finished(self) -> bool: return self.receiver.is_finished and self.sender.is_finished