
管理员
站长
- UID
- 1
- 积分
- 224400
- 余额
- 13 R
- Moe币
- 84881
- 在线时间
- 279 小时
- 注册时间
- 2025-12-28
- 最后登录
- 2026-11-9
|
m_event(request, stream_id)
if isinstance(event, h2.events.DataReceived):
assert event.flow_controlled_length is not None
assert event.data is not None
amount = event.flow_controlled_length
self._h2_state.acknowledge_received_data(amount, stream_id)
await self._write_outgoing_data(request)
yield event.data
elif isinstance(event, h2.events.StreamEnded):
break
async def _receive_stream_event(
self, request: Request, stream_id: int
) -> h2.events.ResponseReceived | h2.events.DataReceived | h2.events.StreamEnded:
"""
Return the next available event for a given stream ID.
Will read more data from the network if required.
"""
while not self._events.get(stream_id):
await self._receive_events(request, stream_id)
event = self._events[stream_id].pop(0)
if isinstance(event, h2.events.StreamReset):
raise RemoteProtocolError(event)
return event
async def _receive_events(
self, request: Request, stream_id: int | None = None
) -> None:
"""
Read some data from the network until we see one or more events
for a given stream ID.
"""
async with self._read_lock:
if self._connection_terminated is not None:
last_stream_id = self._connection_terminated.last_stream_id
if stream_id and last_stream_id and stream_id > last_stream_id:
self._request_count -= 1
raise ConnectionNotAvailable()
raise RemoteProtocolError(self._connection_terminated)
# This conditional is a bit icky. We don't want to block reading if we've
# actually got an event to return for a given stream. We need to do that
# check *within* the atomic read lock. Though it also need to be optional,
# because when we call it from `_wait_for_outgoing_flow` we *do* want to
# block until we've available flow control, event when we have events
# pending for the stream ID we're attempting to send on.
if stream_id is None or not self._events.get(stream_id):
events = await self._read_incoming_data(request)
for event in events:
if isinstance(event, h2.events.RemoteSettingsChanged):
async with Trace(
"receive_remote_settings", logger, request
) as trace:
await self._receive_remote_settings_change(event)
trace.return_value = event
elif isinstance(
event,
(
h2.events.ResponseReceived,
h2.events.DataReceived,
h2.events.StreamEnded,
h2.events.StreamReset,
),
):
if event.stream_id in self._events:
self._events[event.stream_id].append(event)
elif isinstance(event, h2.events.ConnectionTerminated):
self._connection_terminated = event
await self._write_outgoing_data(request)
async def _receive_remote_settings_change(
self, event: h2.events.RemoteSettingsChanged
) -> None:
max_concurrent_streams = event.changed_settings.get(
h2.settings.SettingCodes.MAX_CONCURRENT_STREAMS
)
if max_concurrent_streams:
new_max_streams = min(
max_concurrent_streams.new_value,
self._h2_state.local_settings.max_concurrent_streams,
)
if new_max_streams and new_max_streams != self._max_streams:
while new_max_streams > self._max_streams:
await self._max_streams_semaphore.release()
self._max_streams += 1
while new_max_streams < self._max_streams:
await self._max_streams_semaphore.acquire()
self._max_streams -= 1
async def _response_closed(self, stream_id: int) -> None:
await self._max_streams_semaphore.release()
del self._events[stream_id]
async with self._state_lock:
if self._connection_terminated and not self._events:
await self.aclose()
elif self._state == HTTPConnectionState.ACTIVE and not self._events:
self._state = HTTPConnectionState.IDLE
if self._keepalive_expiry is not None:
now = time.monotonic()
self._expire_at = now + self._keepalive_expiry
if self._used_all_stream_ids: # pragma: nocover
await self.aclose()
async def aclose(self) -> None:
# Note that this method unilaterally closes the connection, and does
# not have any kind of locking in place around it.
self._h2_state.close_connection()
self._state = HTTPConnectionState.CLOSED
await self._network_stream.aclose()
# Wrappers around network read/write operations...
async def _read_incoming_data(self, request: Request) -> list[h2.events.Event]:
timeouts = request.extensions.get("timeout", {})
timeout = timeouts.get("read", None)
if self._read_exception is not None:
raise self._read_exception # pragma: nocover
try:
data = await self._network_stream.read(self.READ_NUM_BYTES, timeout)
if data == b"":
raise RemoteProtocolError("Server disconnected")
except Exception as exc:
# If we get a network error we should:
#
# 1. Save the exception and just raise it immediately on any future reads.
# (For example, this means that a single read timeout or disconnect will
# immediately close all pending streams. Without requiring multiple
# sequential timeouts.)
# 2. Mark the connection as errored, so that we don't accept any other
# incoming requests.
self._read_exception = exc
self._connection_error = True
raise exc
events: list[h2.events.Event] = self._h2_state.receive_data(data)
return events
async def _write_outgoing_data(self, request: Request) -> None:
timeouts = request.extensions.get("timeout", {})
timeout = timeouts.get("write", None)
async with self._write_lock:
data_to_send = self._h2_state.data_to_send()
if self._write_exception is not None:
raise self._write_exception # pragma: nocover
try:
await self._network_stream.write(data_to_send, timeout)
except Exception as exc: # pragma: nocover
# If we get a network error we should:
#
# 1. Save the exception and just raise it immediately on any future write.
# (For example, this means that a single write timeout or disconnect will
# immediately close all pending streams. Without requiring multiple
# sequential timeouts.)
# 2. Mark the connection as errored, so that we don't accept any other
# incoming requests.
self._write_exception = exc
self._connection_error = True
raise exc
# Flow control...
async def _wait_for_outgoing_flow(self, request: Request, stream_id: int) -> int:
"""
Returns the maximum allowable outgoing flow for a given stream.
If the allowable flow is zero, then waits on the network until
WindowUpdated frames have increased the flow rate.
https://tools.ietf.org/html/rfc7540#section-6.9
"""
local_flow: int = self._h2_state.local_flow_control_window(stream_id)
max_frame_size: int = self._h2_state.max_outbound_frame_size
flow = min(local_flow, max_frame_size)
while flow == 0:
await self._receive_events( |
|