public StreamConnection performUpgrade() throws IOException {

    // Upgrade the connection
    // Set the upgraded flag already to prevent new requests after this one
    if (allAreSet(state, UPGRADED | CLOSE_REQ | CLOSED)) {
      throw new IOException(UndertowClientMessages.MESSAGES.connectionClosed());
    }
    state |= UPGRADED;
    return connection;
  }
 @Override
 public void sendRequest(
     final ClientRequest request, final ClientCallback<ClientExchange> clientCallback) {
   if (anyAreSet(state, UPGRADE_REQUESTED | UPGRADED | CLOSE_REQ | CLOSED)) {
     throw UndertowClientMessages.MESSAGES.invalidConnectionState();
   }
   final HttpClientExchange httpClientExchange =
       new HttpClientExchange(clientCallback, request, this);
   if (currentRequest == null) {
     initiateRequest(httpClientExchange);
   } else {
     pendingQueue.add(httpClientExchange);
   }
 }
  private void initiateRequest(HttpClientExchange httpClientExchange) {
    currentRequest = httpClientExchange;
    pendingResponse = new HttpResponseBuilder();
    ClientRequest request = httpClientExchange.getRequest();

    String connectionString = request.getRequestHeaders().getFirst(CONNECTION);
    if (connectionString != null) {
      HttpString connectionHttpString = new HttpString(connectionString);
      if (connectionHttpString.equals(CLOSE)) {
        state |= CLOSE_REQ;
      } else if (connectionHttpString.equals(UPGRADE)) {
        state |= UPGRADE_REQUESTED;
      }
    } else if (request.getProtocol() != Protocols.HTTP_1_1) {
      state |= CLOSE_REQ;
    }
    if (request.getRequestHeaders().contains(UPGRADE)) {
      state |= UPGRADE_REQUESTED;
    }

    // setup the client request conduits
    final ConduitStreamSourceChannel sourceChannel = connection.getSourceChannel();
    sourceChannel.setReadListener(clientReadListener);
    sourceChannel.resumeReads();

    ConduitStreamSinkChannel sinkChannel = connection.getSinkChannel();
    StreamSinkConduit conduit = originalSinkConduit;
    conduit = new HttpRequestConduit(conduit, bufferPool, request);

    String fixedLengthString = request.getRequestHeaders().getFirst(CONTENT_LENGTH);
    String transferEncodingString = request.getRequestHeaders().getLast(TRANSFER_ENCODING);

    boolean hasContent = true;

    if (fixedLengthString != null) {
      try {
        long length = Long.parseLong(fixedLengthString);
        conduit =
            new ClientFixedLengthStreamSinkConduit(conduit, length, false, false, currentRequest);
        hasContent = length != 0;
      } catch (NumberFormatException e) {
        handleError(new IOException(e));
        return;
      }
    } else if (transferEncodingString != null) {
      if (!transferEncodingString
          .toLowerCase(Locale.ENGLISH)
          .contains(Headers.CHUNKED.toString())) {
        handleError(
            UndertowClientMessages.MESSAGES.unknownTransferEncoding(transferEncodingString));
        return;
      }
      conduit =
          new ChunkedStreamSinkConduit(
              conduit,
              httpClientExchange.getConnection().getBufferPool(),
              false,
              false,
              httpClientExchange.getRequest().getRequestHeaders(),
              requestFinishListener,
              httpClientExchange);
    } else {
      conduit = new ClientFixedLengthStreamSinkConduit(conduit, 0, false, false, currentRequest);
      hasContent = false;
    }
    sinkChannel.setConduit(conduit);

    httpClientExchange.invokeReadReadyCallback(httpClientExchange);
    if (!hasContent) {
      // if there is no content we flush the response channel.
      // otherwise it is up to the user
      try {
        sinkChannel.shutdownWrites();
        if (!sinkChannel.flush()) {
          sinkChannel.setWriteListener(
              ChannelListeners.flushingChannelListener(
                  null,
                  new ChannelExceptionHandler<ConduitStreamSinkChannel>() {
                    @Override
                    public void handleException(
                        ConduitStreamSinkChannel channel, IOException exception) {
                      handleError(exception);
                    }
                  }));
        }
      } catch (IOException e) {
        handleError(e);
      }
    } else if (!sinkChannel.isWriteResumed()) {
      try {
        // TODO: this needs some more thought
        if (!sinkChannel.flush()) {
          sinkChannel.setWriteListener(
              new ChannelListener<ConduitStreamSinkChannel>() {
                @Override
                public void handleEvent(ConduitStreamSinkChannel channel) {
                  try {
                    if (channel.flush()) {
                      channel.suspendWrites();
                    }
                  } catch (IOException e) {
                    handleError(e);
                  }
                }
              });
          sinkChannel.resumeWrites();
        }
      } catch (IOException e) {
        handleError(e);
      }
    }
  }