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; }
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; }