@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; }
protected boolean exitAfterHandlingRedirect( // Channel channel, // NettyResponseFuture<?> future, // HttpResponse response, // Request request, // int statusCode) throws Exception { if (followRedirect(config, request) && REDIRECT_STATUSES.contains(statusCode)) { if (future.incrementAndGetCurrentRedirectCount() >= config.getMaxRedirects()) { throw new MaxRedirectException("Maximum redirect reached: " + config.getMaxRedirects()); } else { // We must allow 401 handling again. future.getAndSetAuth(false); HttpHeaders responseHeaders = response.headers(); String location = responseHeaders.get(HttpHeaders.Names.LOCATION); Uri uri = Uri.create(future.getUri(), location); if (!uri.equals(future.getUri())) { final RequestBuilder requestBuilder = new RequestBuilder(future.getRequest()); if (!config.isRemoveQueryParamOnRedirect()) requestBuilder.addQueryParams(future.getRequest().getQueryParams()); // if we are to strictly handle 302, we should keep the original method (which browsers // don't) // 303 must force GET if ((statusCode == FOUND.code() && !config.isStrict302Handling()) || statusCode == SEE_OTHER.code()) requestBuilder.setMethod("GET"); // in case of a redirect from HTTP to HTTPS, future attributes might change final boolean initialConnectionKeepAlive = future.isKeepAlive(); final String initialPoolKey = channelManager.getPartitionId(future); future.setUri(uri); String newUrl = uri.toUrl(); if (request.getUri().getScheme().startsWith(WEBSOCKET)) { newUrl = newUrl.replaceFirst(HTTP, WEBSOCKET); } logger.debug("Redirecting to {}", newUrl); for (String cookieStr : responseHeaders.getAll(HttpHeaders.Names.SET_COOKIE)) { Cookie c = CookieDecoder.decode(cookieStr, timeConverter); if (c != null) requestBuilder.addOrReplaceCookie(c); } Callback callback = channelManager.newDrainCallback( future, channel, initialConnectionKeepAlive, initialPoolKey); if (HttpHeaders.isTransferEncodingChunked(response)) { // We must make sure there is no bytes left before // executing the next request. // FIXME investigate this Channels.setAttribute(channel, callback); } else { // FIXME don't understand: this offers the connection to the pool, or even closes it, // while the // request has not been sent, right? callback.call(); } Request redirectRequest = requestBuilder.setUrl(newUrl).build(); // FIXME why not reuse the channel is same host? requestSender.sendNextRequest(redirectRequest, future); return true; } } } return false; }