private Realm ntlmChallenge(
      List<String> wwwAuth,
      Request request,
      ProxyServer proxyServer,
      FluentCaseInsensitiveStringsMap headers,
      Realm realm,
      NettyResponseFuture<?> future)
      throws NTLMEngineException {

    boolean useRealm = proxyServer == null && realm != null;

    String ntlmDomain = useRealm ? realm.getNtlmDomain() : proxyServer.getNtlmDomain();
    String ntlmHost = useRealm ? realm.getNtlmHost() : proxyServer.getHost();
    String principal = useRealm ? realm.getPrincipal() : proxyServer.getPrincipal();
    String password = useRealm ? realm.getPassword() : proxyServer.getPassword();

    if (realm != null && !realm.isNtlmMessageType2Received()) {
      String challengeHeader = NTLMEngine.INSTANCE.generateType1Msg(ntlmDomain, ntlmHost);

      URI uri = request.getURI();
      addNTLMAuthorizationHeader(headers, challengeHeader);
      future.getAndSetAuth(false);
      return newRealmBuilder(realm) //
          .setScheme(realm.getAuthScheme()) //
          .setUri(uri.getRawPath()) //
          .setMethodName(request.getMethod()) //
          .setNtlmMessageType2Received(true) //
          .build();

    } else {
      addType3NTLMAuthorizationHeader(wwwAuth, headers, principal, password, ntlmDomain, ntlmHost);
      Realm.AuthScheme authScheme = realm != null ? realm.getAuthScheme() : Realm.AuthScheme.NTLM;
      return newRealmBuilder(realm) //
          .setScheme(authScheme) //
          .setUri(request.getURI().getPath()) //
          .setMethodName(request.getMethod()) //
          .build();
    }
  }
  private boolean handleUnauthorizedAndExit(
      int statusCode,
      Realm realm,
      final Request request,
      HttpResponse response,
      final NettyResponseFuture<?> future,
      ProxyServer proxyServer,
      final Channel channel)
      throws Exception {
    if (statusCode == UNAUTHORIZED.code() && realm != null) {

      List<String> authenticateHeaders =
          response.headers().getAll(HttpHeaders.Names.WWW_AUTHENTICATE);

      if (!authenticateHeaders.isEmpty() && !future.getAndSetAuth(true)) {
        future.setState(NettyResponseFuture.STATE.NEW);
        Realm newRealm = null;
        // NTLM
        boolean negociate = authenticateHeaders.contains("Negotiate");
        if (!authenticateHeaders.contains("Kerberos")
            && (isNTLM(authenticateHeaders) || negociate)) {
          newRealm =
              ntlmChallenge(
                  authenticateHeaders, request, proxyServer, request.getHeaders(), realm, future);
          // SPNEGO KERBEROS
        } else if (negociate) {
          newRealm =
              kerberosChallenge(
                  authenticateHeaders, request, proxyServer, request.getHeaders(), realm, future);
          if (newRealm == null) {
            return true;
          }
        } else {
          newRealm =
              new Realm.RealmBuilder()
                  .clone(realm)
                  .setScheme(realm.getAuthScheme())
                  .setUri(request.getURI().getPath())
                  .setMethodName(request.getMethod())
                  .setUsePreemptiveAuth(true)
                  .parseWWWAuthenticateHeader(authenticateHeaders.get(0))
                  .build();
        }

        Realm nr =
            new Realm.RealmBuilder()
                .clone(newRealm)
                .setUri(URI.create(request.getUrl()).getPath())
                .build();
        final Request nextRequest =
            new RequestBuilder(future.getRequest())
                .setHeaders(request.getHeaders())
                .setRealm(nr)
                .build();

        LOGGER.debug("Sending authentication to {}", request.getUrl());
        Callback callback =
            new Callback(future) {
              public void call() throws Exception {
                channels.drainChannel(channel, future);
                requestSender.sendNextRequest(nextRequest, future);
              }
            };

        if (future.isKeepAlive() && HttpHeaders.isTransferEncodingChunked(response)) {
          // We must make sure there is no bytes left
          // before executing the next request.
          Channels.setDefaultAttribute(channel, callback);
        } else {
          callback.call();
        }

        return true;
      }
    }

    return false;
  }