code: async def send_data(
          self,
          stream_id: int,
          data: bytes,
          end_stream: bool = False,
      ):
          
          if self.closed:
              raise ConnectionClosedError
          stream = self._get_stream(stream_id)
          if stream.closed:
              raise StreamClosedError(stream_id)
  
          remaining = data
          while len(remaining) > 0:
              await asyncio.gather(
                  self._writable.wait(),
                  self._window_open(stream.id),
              )
  
              remaining_size = len(remaining)
              window_size = self._h2.local_flow_control_window(stream.id)
              max_frame_size = self._h2.max_outbound_frame_size
  
              send_size = min(remaining_size, window_size, max_frame_size)
              if send_size == 0:
                  continue
  
              logger.debug(
                  f'[{stream.id}] Sending {send_size} of {remaining_size} '
                  f'bytes (window {window_size}, frame max {max_frame_size})'
              )
  
              to_send = remaining[:send_size]
              remaining = remaining[send_size:]
              end = (end_stream is True and len(remaining) == 0)
  
              self._h2.send_data(stream.id, to_send, end_stream=end)
              self._flush()
  
              if self._h2.local_flow_control_window(stream.id) == 0:
                  stream.window_open.clear()
docs: Send data, respecting the receiver's flow control instructions. If
          the provided data is larger than the connection's maximum outbound
          frame size, it will be broken into several frames as appropriate.