Send a response on a stream. Args: stream_id: The stream ID to respond on status: HTTP status code (int) headers: List of (name, value) header tuples body: Optional response body bytes Returns: bool: True if response sent,
(self, stream_id, status, headers, body=None)
| 358 | await self._send_pending_data() |
| 359 | |
| 360 | async def send_response(self, stream_id, status, headers, body=None): |
| 361 | """Send a response on a stream. |
| 362 | |
| 363 | Args: |
| 364 | stream_id: The stream ID to respond on |
| 365 | status: HTTP status code (int) |
| 366 | headers: List of (name, value) header tuples |
| 367 | body: Optional response body bytes |
| 368 | |
| 369 | Returns: |
| 370 | bool: True if response sent, False if stream was already closed |
| 371 | """ |
| 372 | stream = self.streams.get(stream_id) |
| 373 | if stream is None: |
| 374 | # Stream was already cleaned up (reset/closed) - return gracefully |
| 375 | return False |
| 376 | |
| 377 | # Build response headers with :status pseudo-header |
| 378 | response_headers = [(':status', str(status))] |
| 379 | for name, value in headers: |
| 380 | response_headers.append((name.lower(), str(value))) |
| 381 | |
| 382 | end_stream = body is None or len(body) == 0 |
| 383 | |
| 384 | try: |
| 385 | # Send headers |
| 386 | self.h2_conn.send_headers(stream_id, response_headers, end_stream=end_stream) |
| 387 | stream.send_headers(response_headers, end_stream=end_stream) |
| 388 | await self._send_pending_data() |
| 389 | |
| 390 | # Send body if present |
| 391 | if body and len(body) > 0: |
| 392 | await self.send_data(stream_id, body, end_stream=True) |
| 393 | return True |
| 394 | except _h2_exceptions.StreamClosedError: |
| 395 | # Stream was reset by client - clean up gracefully |
| 396 | stream.close() |
| 397 | self.cleanup_stream(stream_id) |
| 398 | return False |
| 399 | |
| 400 | async def _wait_for_flow_control_window(self, stream_id): |
| 401 | """Wait for flow control window to become positive. |