Send trailing headers on a stream. Trailers are headers sent after the response body, commonly used for gRPC status codes, checksums, and timing information. Args: stream_id: The stream ID trailers: List of (name, value) trailer tuples Raise
(self, stream_id, trailers)
| 481 | return False |
| 482 | |
| 483 | async def send_trailers(self, stream_id, trailers): |
| 484 | """Send trailing headers on a stream. |
| 485 | |
| 486 | Trailers are headers sent after the response body, commonly used |
| 487 | for gRPC status codes, checksums, and timing information. |
| 488 | |
| 489 | Args: |
| 490 | stream_id: The stream ID |
| 491 | trailers: List of (name, value) trailer tuples |
| 492 | |
| 493 | Raises: |
| 494 | HTTP2Error: If stream not found, headers not sent, or pseudo-headers used |
| 495 | |
| 496 | Returns: |
| 497 | bool: True if trailers sent, False if stream was already closed |
| 498 | """ |
| 499 | stream = self.streams.get(stream_id) |
| 500 | if stream is None: |
| 501 | # Stream was already cleaned up (reset/closed) - return gracefully |
| 502 | return False |
| 503 | if not stream.response_headers_sent: |
| 504 | # Can't send trailers without headers - return False |
| 505 | return False |
| 506 | |
| 507 | # Validate and normalize trailer headers |
| 508 | trailer_headers = [] |
| 509 | for name, value in trailers: |
| 510 | lname = name.lower() |
| 511 | if lname.startswith(':'): |
| 512 | raise HTTP2Error(f"Pseudo-header '{name}' not allowed in trailers") |
| 513 | trailer_headers.append((lname, str(value))) |
| 514 | |
| 515 | try: |
| 516 | # Send trailers with end_stream=True |
| 517 | self.h2_conn.send_headers(stream_id, trailer_headers, end_stream=True) |
| 518 | stream.send_trailers(trailer_headers) |
| 519 | await self._send_pending_data() |
| 520 | return True |
| 521 | except _h2_exceptions.StreamClosedError: |
| 522 | # Stream was reset by client - clean up gracefully |
| 523 | stream.close() |
| 524 | self.cleanup_stream(stream_id) |
| 525 | return False |
| 526 | |
| 527 | async def send_error(self, stream_id, status_code, message=None): |
| 528 | """Send an error response on a stream.""" |