@SuppressWarnings("unchecked")
  @Override
  public NextAction handleEvent(final FilterChainContext ctx, final FilterChainEvent event)
      throws IOException {

    final Object type = event.type();
    if (type == ContinueEvent.class) {
      final ContinueEvent continueEvent = (ContinueEvent) event;
      ((ExpectHandler) continueEvent.getContext().getBodyHandler()).finish(ctx);
    } else if (type == TunnelRequestEvent.class) {
      // Disable SSL for the time being...
      ctx.notifyDownstream(new SSLSwitchingEvent(false, ctx.getConnection()));
      ctx.suspend();
      TunnelRequestEvent tunnelRequestEvent = (TunnelRequestEvent) event;
      final ProxyServer proxyServer = tunnelRequestEvent.getProxyServer();
      final URI requestUri = tunnelRequestEvent.getUri();

      RequestBuilder builder = new RequestBuilder();
      builder.setMethod(Method.CONNECT.getMethodString());
      builder.setUrl("http://" + getAuthority(requestUri));
      Request request = builder.build();

      AsyncHandler handler =
          new AsyncCompletionHandler() {
            @Override
            public Object onCompleted(Response response) throws Exception {
              if (response.getStatusCode() != 200) {
                PROXY_AUTH_FAILURE.set(ctx.getConnection(), Boolean.TRUE);
              }
              ctx.notifyDownstream(new SSLSwitchingEvent(true, ctx.getConnection()));
              ctx.notifyDownstream(event);
              return response;
            }
          };
      final GrizzlyResponseFuture future =
          new GrizzlyResponseFuture(grizzlyAsyncHttpProvider, request, handler, proxyServer);
      future.setDelegate(SafeFutureImpl.create());

      grizzlyAsyncHttpProvider.execute(
          ctx.getConnection(), request, handler, future, HttpTxContext.get(ctx));
      return ctx.getSuspendAction();
    }

    return ctx.getStopAction();
  }
  private boolean exitAfterHandling407( //
      Channel channel, //
      NettyResponseFuture<?> future, //
      HttpResponse response, //
      Request request, //
      int statusCode, //
      ProxyServer proxyServer, //
      HttpRequest httpRequest) {

    if (future.getInProxyAuth().getAndSet(true)) {
      logger.info("Can't handle 407 as auth was already performed");
      return false;
    }

    Realm proxyRealm = future.getProxyRealm();

    if (proxyRealm == null) {
      logger.info("Can't handle 407 as there's no proxyRealm");
      return false;
    }

    List<String> proxyAuthHeaders = response.headers().getAll(HttpHeaders.Names.PROXY_AUTHENTICATE);

    if (proxyAuthHeaders.isEmpty()) {
      logger.info("Can't handle 407 as response doesn't contain Proxy-Authenticate headers");
      return false;
    }

    // FIXME what's this???
    future.setChannelState(ChannelState.NEW);
    HttpHeaders requestHeaders = new DefaultHttpHeaders(false).add(request.getHeaders());

    switch (proxyRealm.getScheme()) {
      case BASIC:
        if (getHeaderWithPrefix(proxyAuthHeaders, "Basic") == null) {
          logger.info(
              "Can't handle 407 with Basic realm as Proxy-Authenticate headers don't match");
          return false;
        }

        if (proxyRealm.isUsePreemptiveAuth()) {
          // FIXME do we need this, as future.getAndSetAuth
          // was tested above?
          // auth was already performed, most likely auth
          // failed
          logger.info(
              "Can't handle 407 with Basic realm as auth was preemptive and already performed");
          return false;
        }

        // FIXME do we want to update the realm, or directly
        // set the header?
        Realm newBasicRealm =
            realm(proxyRealm) //
                .setUsePreemptiveAuth(true) //
                .build();
        future.setProxyRealm(newBasicRealm);
        break;

      case DIGEST:
        String digestHeader = getHeaderWithPrefix(proxyAuthHeaders, "Digest");
        if (digestHeader == null) {
          logger.info(
              "Can't handle 407 with Digest realm as Proxy-Authenticate headers don't match");
          return false;
        }
        Realm newDigestRealm =
            realm(proxyRealm) //
                .setUri(request.getUri()) //
                .setMethodName(request.getMethod()) //
                .setUsePreemptiveAuth(true) //
                .parseProxyAuthenticateHeader(digestHeader) //
                .build();
        future.setProxyRealm(newDigestRealm);
        break;

      case NTLM:
        String ntlmHeader = getHeaderWithPrefix(proxyAuthHeaders, "NTLM");
        if (ntlmHeader == null) {
          logger.info("Can't handle 407 with NTLM realm as Proxy-Authenticate headers don't match");
          return false;
        }
        ntlmProxyChallenge(ntlmHeader, request, proxyRealm, requestHeaders, future);
        Realm newNtlmRealm =
            realm(proxyRealm) //
                .setUsePreemptiveAuth(true) //
                .build();
        future.setProxyRealm(newNtlmRealm);
        break;

      case KERBEROS:
      case SPNEGO:
        if (getHeaderWithPrefix(proxyAuthHeaders, "Negociate") == null) {
          logger.info(
              "Can't handle 407 with Kerberos or Spnego realm as Proxy-Authenticate headers don't match");
          return false;
        }
        try {
          kerberosProxyChallenge(
              channel, proxyAuthHeaders, request, proxyServer, proxyRealm, requestHeaders, future);

        } catch (SpnegoEngineException e) {
          // FIXME
          String ntlmHeader2 = getHeaderWithPrefix(proxyAuthHeaders, "NTLM");
          if (ntlmHeader2 != null) {
            logger.warn("Kerberos/Spnego proxy auth failed, proceeding with NTLM");
            ntlmChallenge(ntlmHeader2, request, requestHeaders, proxyRealm, future);
            Realm newNtlmRealm2 =
                realm(proxyRealm) //
                    .setScheme(AuthScheme.NTLM) //
                    .setUsePreemptiveAuth(true) //
                    .build();
            future.setProxyRealm(newNtlmRealm2);
          } else {
            requestSender.abort(channel, future, e);
            return false;
          }
        }
        break;
      default:
        throw new IllegalStateException("Invalid Authentication scheme " + proxyRealm.getScheme());
    }

    RequestBuilder nextRequestBuilder =
        new RequestBuilder(future.getCurrentRequest()).setHeaders(requestHeaders);
    if (future.getCurrentRequest().getUri().isSecured()) {
      nextRequestBuilder.setMethod(CONNECT);
    }
    final Request nextRequest = nextRequestBuilder.build();

    logger.debug("Sending proxy authentication to {}", request.getUri());
    if (future.isKeepAlive() //
        && HttpHeaders.isKeepAlive(httpRequest) //
        && HttpHeaders.isKeepAlive(response) //
        // support broken Proxy-Connection
        && !response.headers().contains("Proxy-Connection", HttpHeaders.Values.CLOSE, true) //
        && !HttpHeaders.isTransferEncodingChunked(httpRequest) //
        && !HttpHeaders.isTransferEncodingChunked(response)) {
      future.setConnectAllowed(true);
      future.setReuseChannel(true);
      requestSender.drainChannelAndExecuteNextRequest(channel, future, nextRequest);
    } else {
      channelManager.closeChannel(channel);
      requestSender.sendNextRequest(nextRequest, future);
    }

    return true;
  }
Esempio n. 3
0
  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();
  }