Send data on a stream. Args: stream_id: The stream ID data: Body data bytes end_stream: Whether this ends the stream Returns: bool: True if data sent, False if stream was already closed
(self, stream_id, data, end_stream=False)
| 491 | return result |
| 492 | |
| 493 | def send_data(self, stream_id, data, end_stream=False): |
| 494 | """Send data on a stream. |
| 495 | |
| 496 | Args: |
| 497 | stream_id: The stream ID |
| 498 | data: Body data bytes |
| 499 | end_stream: Whether this ends the stream |
| 500 | |
| 501 | Returns: |
| 502 | bool: True if data sent, False if stream was already closed |
| 503 | """ |
| 504 | stream = self.streams.get(stream_id) |
| 505 | if stream is None: |
| 506 | return False |
| 507 | |
| 508 | data_to_send = data |
| 509 | try: |
| 510 | while data_to_send: |
| 511 | available = self.h2_conn.local_flow_control_window(stream_id) |
| 512 | chunk_size = min(available, self.max_frame_size, len(data_to_send)) |
| 513 | |
| 514 | if chunk_size <= 0: |
| 515 | # Wait for WINDOW_UPDATE per RFC 7540 Section 6.9.2 |
| 516 | self._send_pending_data() |
| 517 | available = self._wait_for_flow_control_window(stream_id) |
| 518 | if available <= 0: |
| 519 | return False |
| 520 | chunk_size = min(available, self.max_frame_size, len(data_to_send)) |
| 521 | |
| 522 | chunk = data_to_send[:chunk_size] |
| 523 | data_to_send = data_to_send[chunk_size:] |
| 524 | is_final = end_stream and len(data_to_send) == 0 |
| 525 | |
| 526 | self.h2_conn.send_data(stream_id, chunk, end_stream=is_final) |
| 527 | self._send_pending_data() |
| 528 | |
| 529 | stream.send_data(data, end_stream=end_stream) |
| 530 | return True |
| 531 | except (_h2_exceptions.StreamClosedError, _h2_exceptions.FlowControlError): |
| 532 | # Stream was reset by client or flow control error - clean up gracefully |
| 533 | stream.close() |
| 534 | self.cleanup_stream(stream_id) |
| 535 | return False |
| 536 | |
| 537 | def send_trailers(self, stream_id, trailers): |
| 538 | """Send trailing headers on a stream. |