code: public void writeData(int streamId, boolean outFinished, Buffer buffer, long byteCount)
        throws IOException {
      if (byteCount == 0) { // Empty data frames are not flow-controlled.
        writer.data(outFinished, streamId, buffer, 0);
        return;
      }
  
      while (byteCount > 0) {
        int toWrite;
        synchronized (Http2Connection.this) {
          try {
            while (bytesLeftInWriteWindow <= 0) {
              // Before blocking, confirm that the stream we're writing is still open. It's possible
              // that the stream has since been closed (such as if this write timed out.)
              if (!streams.containsKey(streamId)) {
                throw new IOException("stream closed");
              }
              Http2Connection.this.wait(); // Wait until we receive a WINDOW_UPDATE.
            }
          } catch (InterruptedException e) {
            Thread.currentThread().interrupt(); // Retain interrupted status.
            throw new InterruptedIOException();
          }
  
          toWrite = (int) Math.min(byteCount, bytesLeftInWriteWindow);
          toWrite = Math.min(toWrite, writer.maxDataLength());
          bytesLeftInWriteWindow -= toWrite;
        }
  
        byteCount -= toWrite;
        writer.data(outFinished && byteCount == 0, streamId, buffer, toWrite);
      }
    }
docs: Callers of this method are not thread safe, and sometimes on application threads. Most often,
  this method will be called to send a buffer worth of data to the peer.
  
  <p>Writes are subject to the write window of the stream and the connection. Until there is a
  window sufficient to send {@code byteCount}, the caller will block. For example, a user of
  {@code HttpURLConnection} who flushes more bytes to the output stream than the connection's
  write window will block.
  
  <p>Zero {@code byteCount} writes are not subject to flow control and will not block. The only
  use case for zero {@code byteCount} is closing a flushed output stream.