docs: private long doWrap(ByteBuffer[] userBuffers, int off, int len) throws IOException {
          if(anyAreSet(state, FLAG_CLOSED)) {
              throw new ClosedChannelException();
          }
          if(outstandingTasks > 0) {
              return 0;
          }
          if(anyAreSet(state, FLAG_WRITE_REQUIRES_READ)) {
              doUnwrap(null, 0, 0);
              if(allAreClear(state, FLAG_READ_REQUIRES_WRITE)) { //unless a wrap is immediatly required we just return
                  return 0;
              }
          }
          if(wrappedData != null) {
              int res = sink.write(wrappedData.getBuffer());
              if(res == 0 || wrappedData.getBuffer().hasRemaining()) {
                  return 0;
              }
              wrappedData.getBuffer().clear();
          } else {
              wrappedData = bufferPool.allocate();
          }
          try {
              SSLEngineResult result = null;
              while (result == null || (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_WRAP && result.getStatus() != SSLEngineResult.Status.BUFFER_OVERFLOW)) {
                  if (userBuffers == null) {
                      result = engine.wrap(EMPTY_BUFFER, wrappedData.getBuffer());
                  } else {
                      result = engine.wrap(userBuffers, off, len, wrappedData.getBuffer());
                  }
              }
              wrappedData.getBuffer().flip();
  
              if (result.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
                  throw new IOException("underflow"); //todo: can this happen?
              } else if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
                  if (!wrappedData.getBuffer().hasRemaining()) { //if an earlier wrap suceeded we ignore this
                      throw new IOException("overflow"); //todo: handle properly
                  }
              }
              //attempt to write it out, if we fail we just return
              //we ignore the handshake status, as wrap will get called again
              if (wrappedData.getBuffer().hasRemaining()) {
                  sink.write(wrappedData.getBuffer());
              }
              //if it was not a complete write we just return
              if (wrappedData.getBuffer().hasRemaining()) {
                  return result.bytesConsumed();
              }
  
              if (!handleHandshakeResult(result)) {
                  return 0;
              }
              if (result.getStatus() == SSLEngineResult.Status.CLOSED && userBuffers != null) {
                  notifyWriteClosed();
                  throw new ClosedChannelException();
              }
  
              return result.bytesConsumed();
          } catch (RuntimeException|IOException|Error e) {
              try {
                  close();
              } catch (Throwable ex) {
                  UndertowLogger.REQUEST_LOGGER.debug("Exception closing SSLConduit after exception in doWrap()", ex);
              }
              throw e;
          } finally {
              //this can be cleared if the channel is fully closed
              if(wrappedData != null) {
                  if (!wrappedData.getBuffer().hasRemaining()) {
                      wrappedData.close();
                      wrappedData = null;
                  }
              }
          }
      }