docs: @Override
      public VirtualConnection getRequestBodyBuffers(InterChannelCallback callback, boolean bForce) throws BodyCompleteException {
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) {
              Tr.entry(tc, "getRequestBodyBuffers(async)");
          }
          if (!headersParsed()) {
              // request message must have the headers parsed prior to attempting
              // to read a body (this is a completely invalid state in the channel
              // above)
              IOException ioe = new IOException("Request not read yet");
              FFDCFilter.processException(ioe, CLASS_NAME + ".getRequestBodyBuffers", "1355");
              if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                  Tr.debug(tc, "Attempt to read a body without headers");
              }
              callback.error(getVC(), ioe);
              if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) {
                  Tr.exit(tc, "getRequestBodyBuffers(async): no hdrs yet");
              }
              return null;
          }
  
          // check to see if a read is even necessary
          if (!isIncomingBodyValid() || incomingBuffersReady()) {
              if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) {
                  Tr.exit(tc, "getRequestBodyBuffers(async): read not needed");
              }
              if (bForce) {
                  callback.complete(getVC());
                  return null;
              }
              return getVC();
          }
  
          if (isBodyComplete()) {
              // throw new BodyCompleteException("No more body to read");
              // instead of throwing an exception, just return the VC as though
              // data is immediately ready and the caller will switch to their
              // sync block and then get a null buffer back
              if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) {
                  Tr.exit(tc, "getRequestBodyBuffers(async): body complete");
              }
              if (bForce) {
                  callback.complete(getVC());
                  return null;
              }
              return getVC();
          }
  
          setAppReadCallback(callback);
          setForceAsync(bForce);
          setMultiRead(true);
          try {
              if (!readBodyBuffers(getRequestImpl(), true)) {
                  if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) {
                      Tr.exit(tc, "getRequestBodyBuffers(async): read finished");
                  }
                  if (bForce) {
                      callback.complete(getVC());
                      return null;
                  }
                  return getVC();
              }
          } catch (IOException ioe) {
              // no FFDC required
              if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) {
                  Tr.exit(tc, "getRequestBodyBuffers(async): exception:" + ioe);
              }
              callback.error(getVC(), ioe);
              return null;
          }
  
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) {
              Tr.exit(tc, "getRequestBodyBuffers(async): null");
          }
          return null;
      }