docs: @SuppressWarnings("unchecked")
      public VirtualConnection read(long numBytes, WsByteBuffer[] requestBuffers) {
          if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
              Tr.debug(tc, "read entry: stream " + myID + " request: " + requestBuffers + " num bytes requested: " + numBytes);
          }
  
          long streamByteCount = streamReadSize; // number of bytes available on this stream
          long requestByteCount = bytesRemaining(requestBuffers); // total capacity of the caller byte array
          int reqArrayIndex = 0; // keep track of where we are in the caller's array
  
          // if at least numBytes are ready to be processed as part of the HTTP Request/Response, then load it up
          if (numBytes > streamReadSize || requestBuffers == null) {
              if (this.headersCompleted) {
                  return h2HttpInboundLinkWrap.getVirtualConnection();
              } else {
                  if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                      Tr.debug(tc, "read exit: stream " + myID + " more bytes requested than available for read: " + numBytes + " > " + streamReadSize);
                  }
                  return null;
              }
          }
  
          if (streamByteCount < requestByteCount) {
              // the byte count requested exceeds the caller array capacity
              actualReadCount = streamByteCount;
          } else {
              actualReadCount = requestByteCount;
          }
  
          // copy bytes from this stream into the caller arrays
          for (int bytesRead = 0; bytesRead < actualReadCount; bytesRead++) {
  
              // find the first buffer from the caller that has remaining capacity
              while ((requestBuffers[reqArrayIndex].position() == requestBuffers[reqArrayIndex].limit())) {
                  reqArrayIndex++;
              }
  
              // find the next stream buffer that has data
              while (!streamReadReady.isEmpty() && !streamReadReady.get(0).hasRemaining()) {
                  streamReadReady.get(0).release();
                  streamReadReady.remove(0);
              }
              requestBuffers[reqArrayIndex].put(streamReadReady.get(0).get());
          }
  
          // put stream array back in shape
          streamReadSize = 0;
          readLatch = new CountDownLatch(1);
          for (WsByteBuffer buffer : ((ArrayList<WsByteBuffer>) streamReadReady.clone())) {
              streamReadReady.clear();
              if (buffer.hasRemaining()) {
                  moveDataIntoReadBufferArray(buffer.slice());
              }
          }
  
          if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
              Tr.debug(tc, "read exit: " + streamId());
          }
          // return the vc since this was a successful read
          return h2HttpInboundLinkWrap.getVirtualConnection();
      }