Пример #1
0
  private boolean exitAfterSpecialCases(
      final HttpResponse response, final Channel channel, final NettyResponseFuture<?> future)
      throws Exception {

    HttpRequest httpRequest = future.getNettyRequest().getHttpRequest();
    ProxyServer proxyServer = future.getProxyServer();
    int statusCode = response.getStatus().code();
    Request request = future.getCurrentRequest();
    Realm realm = request.getRealm() != null ? request.getRealm() : config.getRealm();

    if (statusCode == UNAUTHORIZED_401) {
      return exitAfterHandling401(
          channel, future, response, request, statusCode, realm, proxyServer, httpRequest);

    } else if (statusCode == PROXY_AUTHENTICATION_REQUIRED_407) {
      return exitAfterHandling407(
          channel, future, response, request, statusCode, proxyServer, httpRequest);

    } else if (statusCode == CONTINUE_100) {
      return exitAfterHandling100(channel, future, statusCode);

    } else if (REDIRECT_STATUSES.contains(statusCode)) {
      return exitAfterHandlingRedirect(channel, future, response, request, statusCode, realm);

    } else if (httpRequest.getMethod() == HttpMethod.CONNECT && statusCode == OK_200) {
      return exitAfterHandlingConnect(
          channel, future, request, proxyServer, statusCode, httpRequest);
    }
    return false;
  }
Пример #2
0
  private void ntlmChallenge(
      String authenticateHeader, //
      Request request, //
      HttpHeaders headers, //
      Realm realm, //
      NettyResponseFuture<?> future) {

    if (authenticateHeader.equals("NTLM")) {
      // server replied bare NTLM => we didn't preemptively sent Type1Msg
      String challengeHeader = NtlmEngine.INSTANCE.generateType1Msg();
      // FIXME we might want to filter current NTLM and add (leave other
      // Authorization headers untouched)
      headers.set(HttpHeaders.Names.AUTHORIZATION, "NTLM " + challengeHeader);
      future.getInAuth().set(false);

    } else {
      String serverChallenge = authenticateHeader.substring("NTLM ".length()).trim();
      String challengeHeader =
          NtlmEngine.INSTANCE.generateType3Msg(
              realm.getPrincipal(),
              realm.getPassword(),
              realm.getNtlmDomain(),
              realm.getNtlmHost(),
              serverChallenge);
      // FIXME we might want to filter current NTLM and add (leave other
      // Authorization headers untouched)
      headers.set(HttpHeaders.Names.AUTHORIZATION, "NTLM " + challengeHeader);
    }
  }
Пример #3
0
 private boolean exitAfterHandling100(
     final Channel channel, final NettyResponseFuture<?> future, int statusCode) {
   future.setHeadersAlreadyWrittenOnContinue(true);
   future.setDontWriteBodyBecauseExpectContinue(false);
   // directly send the body
   Channels.setAttribute(
       channel,
       new Callback(future) {
         @Override
         public void call() throws IOException {
           Channels.setAttribute(channel, future);
           requestSender.writeRequest(future, channel);
         }
       });
   return true;
 }
Пример #4
0
 private boolean updateBodyAndInterrupt(
     NettyResponseFuture<?> future, AsyncHandler<?> handler, HttpResponseBodyPart bodyPart)
     throws Exception {
   boolean interrupt = handler.onBodyPartReceived(bodyPart) != State.CONTINUE;
   if (interrupt) future.setKeepAlive(false);
   return interrupt;
 }
Пример #5
0
  private void finishUpdate(
      final NettyResponseFuture<?> future, Channel channel, boolean expectOtherChunks)
      throws IOException {

    future.cancelTimeouts();

    boolean keepAlive = future.isKeepAlive();
    if (expectOtherChunks && keepAlive) channelManager.drainChannelAndOffer(channel, future);
    else
      channelManager.tryToOfferChannelToPool(
          channel, future.getAsyncHandler(), keepAlive, future.getPartitionKey());

    try {
      future.done();
    } catch (Exception t) {
      // Never propagate exception once we know we are done.
      logger.debug(t.getMessage(), t);
    }
  }
Пример #6
0
 private boolean exitAfterHandlingStatus(
     Channel channel,
     NettyResponseFuture<?> future,
     HttpResponse response,
     AsyncHandler<?> handler,
     NettyResponseStatus status,
     HttpRequest httpRequest)
     throws IOException, Exception {
   return !future.getAndSetStatusReceived(true)
       && handler.onStatusReceived(status) != State.CONTINUE;
 }
Пример #7
0
  @Override
  public void handle(final Channel channel, final NettyResponseFuture<?> future, final Object e)
      throws Exception {

    future.touch();

    // future is already done because of an exception or a timeout
    if (future.isDone()) {
      // FIXME isn't the channel already properly closed?
      channelManager.closeChannel(channel);
      return;
    }

    AsyncHandler<?> handler = future.getAsyncHandler();
    try {
      if (e instanceof HttpResponse) {
        if (handleHttpResponse((HttpResponse) e, channel, future, handler)) return;

      } else if (e instanceof HttpContent) {
        handleChunk((HttpContent) e, channel, future, handler);
      }
    } catch (Exception t) {
      // e.g. an IOException when trying to open a connection and send the
      // next request
      if (hasIOExceptionFilters //
          && t instanceof IOException //
          && requestSender.applyIoExceptionFiltersAndReplayRequest(
              future, IOException.class.cast(t), channel)) {
        return;
      }

      try {
        requestSender.abort(channel, future, t);
      } catch (Exception abortException) {
        logger.debug("Abort failed", abortException);
      } finally {
        finishUpdate(future, channel, false);
      }
      throw t;
    }
  }
Пример #8
0
  private boolean exitAfterHandlingConnect( //
      final Channel channel, //
      final NettyResponseFuture<?> future, //
      final Request request, //
      ProxyServer proxyServer, //
      int statusCode, //
      HttpRequest httpRequest)
      throws IOException {

    if (future.isKeepAlive()) future.attachChannel(channel, true);

    Uri requestUri = request.getUri();
    logger.debug("Connecting to proxy {} for scheme {}", proxyServer, requestUri.getScheme());

    channelManager.upgradeProtocol(channel.pipeline(), requestUri);
    future.setReuseChannel(true);
    future.setConnectAllowed(false);
    requestSender.drainChannelAndExecuteNextRequest(
        channel, future, new RequestBuilder(future.getTargetRequest()).build());

    return true;
  }
  @Override
  public void handle(Channel channel, NettyResponseFuture<?> future, Object e) throws Exception {

    if (e instanceof HttpResponse) {
      HttpResponse response = (HttpResponse) e;
      Channels.setAttribute(channel, new UpgradeCallback(future, channel, response));

    } else if (e instanceof WebSocketFrame) {

      final WebSocketFrame frame = (WebSocketFrame) e;
      WebSocketUpgradeHandler handler =
          WebSocketUpgradeHandler.class.cast(future.getAsyncHandler());
      NettyWebSocket webSocket = NettyWebSocket.class.cast(handler.onCompleted());
      invokeOnSucces(channel, handler);

      if (webSocket != null) {
        if (frame instanceof CloseWebSocketFrame) {
          Channels.setDiscard(channel);
          CloseWebSocketFrame closeFrame = CloseWebSocketFrame.class.cast(frame);
          webSocket.onClose(closeFrame.statusCode(), closeFrame.reasonText());
        } else {
          ByteBuf buf = frame.content();
          if (buf != null && buf.readableBytes() > 0) {
            try {
              NettyResponseBodyPart part =
                  nettyConfig
                      .getBodyPartFactory()
                      .newResponseBodyPart(buf, frame.isFinalFragment());
              handler.onBodyPartReceived(part);

              if (frame instanceof BinaryWebSocketFrame) {
                webSocket.onBinaryFragment(part);
              } else if (frame instanceof TextWebSocketFrame) {
                webSocket.onTextFragment(part);
              } else if (frame instanceof PingWebSocketFrame) {
                webSocket.onPing(part);
              } else if (frame instanceof PongWebSocketFrame) {
                webSocket.onPong(part);
              }
            } finally {
              buf.release();
            }
          }
        }
      } else {
        logger.debug("UpgradeHandler returned a null NettyWebSocket ");
      }
    } else {
      logger.error("Invalid message {}", e);
    }
  }
Пример #10
0
  private boolean handleHttpResponse(
      final HttpResponse response,
      final Channel channel,
      final NettyResponseFuture<?> future,
      AsyncHandler<?> handler)
      throws Exception {

    HttpRequest httpRequest = future.getNettyRequest().getHttpRequest();
    logger.debug("\n\nRequest {}\n\nResponse {}\n", httpRequest, response);

    future.setKeepAlive(
        config.getKeepAliveStrategy().keepAlive(future.getTargetRequest(), httpRequest, response));

    NettyResponseStatus status =
        new NettyResponseStatus(future.getUri(), config, response, channel);
    HttpResponseHeaders responseHeaders = new HttpResponseHeaders(response.headers());

    return exitAfterProcessingFilters(channel, future, handler, status, responseHeaders)
        || //
        exitAfterSpecialCases(response, channel, future)
        || //
        exitAfterHandler(channel, future, response, handler, status, httpRequest, responseHeaders);
  }
  @Override
  public void onClose(NettyResponseFuture<?> future) {
    logger.trace("onClose");

    try {
      WebSocketUpgradeHandler h = (WebSocketUpgradeHandler) future.getAsyncHandler();
      NettyWebSocket webSocket = NettyWebSocket.class.cast(h.onCompleted());

      logger.trace("Connection was closed abnormally (that is, with no close frame being sent).");
      if (webSocket != null)
        webSocket.close(
            1006, "Connection was closed abnormally (that is, with no close frame being sent).");
    } catch (Throwable t) {
      logger.error("onError", t);
    }
  }
  @Override
  public void onError(NettyResponseFuture<?> future, Throwable e) {
    logger.warn("onError {}", e);

    try {
      WebSocketUpgradeHandler h = (WebSocketUpgradeHandler) future.getAsyncHandler();

      NettyWebSocket webSocket = NettyWebSocket.class.cast(h.onCompleted());
      if (webSocket != null) {
        webSocket.onError(e.getCause());
        webSocket.close();
      }
    } catch (Throwable t) {
      logger.error("onError", t);
    }
  }
Пример #13
0
  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;
  }