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

    URI uri = request.getURI();
    String host =
        request.getVirtualHost() == null
            ? AsyncHttpProviderUtils.getHost(uri)
            : request.getVirtualHost();
    String server = proxyServer == null ? host : proxyServer.getHost();
    try {
      String challengeHeader = SpnegoEngine.instance().generateToken(server);
      headers.remove(HttpHeaders.Names.AUTHORIZATION);
      headers.add(HttpHeaders.Names.AUTHORIZATION, "Negotiate " + challengeHeader);

      return newRealmBuilder(realm) //
          .setUri(uri.getRawPath()) //
          .setMethodName(request.getMethod()) //
          .setScheme(Realm.AuthScheme.KERBEROS) //
          .build();

    } catch (Throwable throwable) {
      if (isNTLM(proxyAuth)) {
        return ntlmChallenge(proxyAuth, request, proxyServer, headers, realm, future);
      }
      channels.abort(future, throwable);
      return null;
    }
  }
  private void addGeneralHeaders(final Request request, final HttpRequestPacket requestPacket) {

    if (request.hasHeaders()) {
      final FluentCaseInsensitiveStringsMap map = request.getHeaders();
      for (final Map.Entry<String, List<String>> entry : map.entrySet()) {
        final String headerName = entry.getKey();
        final List<String> headerValues = entry.getValue();
        if (isNonEmpty(headerValues)) {
          for (int i = 0, len = headerValues.size(); i < len; i++) {
            requestPacket.addHeader(headerName, headerValues.get(i));
          }
        }
      }
    }

    final MimeHeaders headers = requestPacket.getHeaders();
    if (!headers.contains(Header.Connection)) {
      // final boolean canCache = context.provider.clientConfig.getAllowPoolingConnection();
      requestPacket.addHeader(Header.Connection, /*(canCache ? */ "keep-alive" /*: "close")*/);
    }

    if (!headers.contains(Header.Accept)) {
      requestPacket.addHeader(Header.Accept, "*/*");
    }

    if (!headers.contains(Header.UserAgent)) {
      requestPacket.addHeader(Header.UserAgent, config.getUserAgent());
    }
  }
  private Realm ntlmProxyChallenge(
      List<String> wwwAuth,
      Request request,
      ProxyServer proxyServer,
      FluentCaseInsensitiveStringsMap headers,
      Realm realm,
      NettyResponseFuture<?> future)
      throws NTLMEngineException {
    future.getAndSetAuth(false);
    headers.remove(HttpHeaders.Names.PROXY_AUTHORIZATION);

    addType3NTLMAuthorizationHeader(
        wwwAuth,
        headers,
        proxyServer.getPrincipal(),
        proxyServer.getPassword(),
        proxyServer.getNtlmDomain(),
        proxyServer.getHost());

    return newRealmBuilder(realm) //
        // .setScheme(realm.getAuthScheme())
        .setUri(request.getURI().getPath()) //
        .setMethodName(request.getMethod())
        .build();
  }
  private void addType3NTLMAuthorizationHeader(
      List<String> auth,
      FluentCaseInsensitiveStringsMap headers,
      String username,
      String password,
      String domain,
      String workstation)
      throws NTLMEngineException {
    headers.remove(HttpHeaders.Names.AUTHORIZATION);

    if (isNonEmpty(auth) && auth.get(0).startsWith("NTLM ")) {
      String serverChallenge = auth.get(0).trim().substring("NTLM ".length());
      String challengeHeader =
          NTLMEngine.INSTANCE.generateType3Msg(
              username, password, domain, workstation, serverChallenge);
      addNTLMAuthorizationHeader(headers, challengeHeader);
    }
  }
  Request buildRequest() {
    RequestBuilder builder = new RequestBuilder(method);

    builder.setUrl(url);
    builder.setQueryParams(new FluentStringsMap(queryParameters));
    builder.setHeaders(headers);

    if (body == null) {
      // do nothing
    } else if (body instanceof String) {
      String stringBody = ((String) body);
      FluentCaseInsensitiveStringsMap headers = new FluentCaseInsensitiveStringsMap(this.headers);

      // Detect and maybe add charset
      String contentType = headers.getFirstValue(HttpHeaders.Names.CONTENT_TYPE);
      if (contentType == null) {
        contentType = "text/plain";
      }
      Charset charset = HttpUtils.parseCharset(contentType);
      if (charset == null) {
        charset = StandardCharsets.UTF_8;
        List<String> contentTypeList = new ArrayList<String>();
        contentTypeList.add(contentType + "; charset=utf-8");
        headers.replace(HttpHeaders.Names.CONTENT_TYPE, contentTypeList);
      }

      byte[] bodyBytes;
      bodyBytes = stringBody.getBytes(charset);

      // If using a POST with OAuth signing, the builder looks at
      // getFormParams() rather than getBody() and constructs the signature
      // based on the form params.
      if (contentType.equals(HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED)) {
        Map<String, List<String>> stringListMap =
            FormUrlEncodedParser.parseAsJava(stringBody, "utf-8");
        for (String key : stringListMap.keySet()) {
          List<String> values = stringListMap.get(key);
          for (String value : values) {
            builder.addFormParam(key, value);
          }
        }
      } else {
        builder.setBody(stringBody);
      }

      builder.setHeaders(headers);
      builder.setBodyCharset(charset);
    } else if (body instanceof JsonNode) {
      JsonNode jsonBody = (JsonNode) body;
      FluentCaseInsensitiveStringsMap headers = new FluentCaseInsensitiveStringsMap(this.headers);
      List<String> contentType = new ArrayList<String>();
      contentType.add("application/json; charset=utf-8");
      headers.replace(HttpHeaders.Names.CONTENT_TYPE, contentType);
      String bodyStr = Json.stringify(jsonBody);
      byte[] bodyBytes;
      try {
        bodyBytes = bodyStr.getBytes("utf-8");
      } catch (UnsupportedEncodingException e) {
        throw new RuntimeException(e);
      }

      builder.setBody(bodyStr);
      builder.setHeaders(headers);
      builder.setBodyCharset(StandardCharsets.UTF_8);
    } else if (body instanceof File) {
      File fileBody = (File) body;
      FileBodyGenerator bodyGenerator = new FileBodyGenerator(fileBody);
      builder.setBody(bodyGenerator);
    } else if (body instanceof InputStream) {
      InputStream inputStreamBody = (InputStream) body;
      InputStreamBodyGenerator bodyGenerator = new InputStreamBodyGenerator(inputStreamBody);
      builder.setBody(bodyGenerator);
    } else if (body instanceof Source) {
      Source<ByteString, ?> sourceBody = (Source<ByteString, ?>) body;
      Publisher<ByteBuffer> publisher =
          sourceBody.map(ByteString::toByteBuffer).runWith(Sink.asPublisher(false), materializer);
      builder.setBody(publisher);
    } else {
      throw new IllegalStateException("Impossible body: " + body);
    }

    if (this.timeout == -1 || this.timeout > 0) {
      builder.setRequestTimeout(this.timeout);
    }

    if (this.followRedirects != null) {
      builder.setFollowRedirect(this.followRedirects);
    }
    if (this.virtualHost != null) {
      builder.setVirtualHost(this.virtualHost);
    }

    if (this.username != null && this.password != null && this.scheme != null) {
      builder.setRealm(auth(this.username, this.password, this.scheme));
    }

    if (this.calculator != null) {
      if (this.calculator instanceof OAuth.OAuthCalculator) {
        OAuthSignatureCalculator calc = ((OAuth.OAuthCalculator) this.calculator).getCalculator();
        builder.setSignatureCalculator(calc);
      } else {
        throw new IllegalStateException("Use OAuth.OAuthCalculator");
      }
    }

    return builder.build();
  }
 private void addNTLMAuthorizationHeader(
     FluentCaseInsensitiveStringsMap headers, String challengeHeader) {
   headers.add(HttpHeaders.Names.AUTHORIZATION, "NTLM " + challengeHeader);
 }