/** * Fake response * * @param method * @return */ private void fakeResponse(final HttpMethod method) throws IOException, HttpException { // What is to follow is an ugly hack. // I REALLY hate having to resort to such // an appalling trick // The only feasible solution is to split monolithic // HttpMethod into HttpRequest/HttpResponse pair. // That would allow to execute CONNECT method // behind the scene and return CONNECT HttpResponse // object in response to the original request that // contains the correct status line, headers & // response body. LOG.debug("CONNECT failed, fake the response for the original method"); // Pass the status, headers and response stream to the wrapped // method. // To ensure that the connection is not released more than once // this method is still responsible for releasing the connection. // This will happen when the response body is consumed, or when // the wrapped method closes the response connection in // releaseConnection(). if (method instanceof HttpMethodBase) { ((HttpMethodBase) method) .fakeResponse( this.connectMethod.getStatusLine(), this.connectMethod.getResponseHeaderGroup(), this.connectMethod.getResponseBodyAsStream()); method .getProxyAuthState() .setAuthScheme(this.connectMethod.getProxyAuthState().getAuthScheme()); this.connectMethod = null; } else { releaseConnection = true; LOG.warn("Unable to fake response on method as it is not derived from HttpMethodBase."); } }
private boolean processProxyAuthChallenge(final HttpMethod method) throws MalformedChallengeException, AuthenticationException { AuthState authstate = method.getProxyAuthState(); Map proxyChallenges = AuthChallengeParser.parseChallenges(method.getResponseHeaders(PROXY_AUTH_CHALLENGE)); if (proxyChallenges.isEmpty()) { LOG.debug("Proxy authentication challenge(s) not found"); return false; } AuthScheme authscheme = null; try { authscheme = this.authProcessor.processChallenge(authstate, proxyChallenges); } catch (AuthChallengeException e) { if (LOG.isWarnEnabled()) { LOG.warn(e.getMessage()); } } if (authscheme == null) { return false; } AuthScope authscope = new AuthScope( conn.getProxyHost(), conn.getProxyPort(), authscheme.getRealm(), authscheme.getSchemeName()); if (LOG.isDebugEnabled()) { LOG.debug("Proxy authentication scope: " + authscope); } if (authstate.isAuthAttempted() && authscheme.isComplete()) { // Already tried and failed Credentials credentials = promptForProxyCredentials(authscheme, method.getParams(), authscope); if (credentials == null) { if (LOG.isInfoEnabled()) { LOG.info("Failure authenticating with " + authscope); } return false; } else { return true; } } else { authstate.setAuthAttempted(true); Credentials credentials = this.state.getProxyCredentials(authscope); if (credentials == null) { credentials = promptForProxyCredentials(authscheme, method.getParams(), authscope); } if (credentials == null) { if (LOG.isInfoEnabled()) { LOG.info("No credentials available for " + authscope); } return false; } else { return true; } } }
/** * Tests if the {@link HttpMethod method} requires authentication. * * @param method HTTP method * @return boolean <tt>true</tt> if a retry is needed, <tt>false</tt> otherwise. */ private boolean isAuthenticationNeeded(final HttpMethod method) { method .getHostAuthState() .setAuthRequested(method.getStatusCode() == HttpStatus.SC_UNAUTHORIZED); method .getProxyAuthState() .setAuthRequested(method.getStatusCode() == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED); if (method.getHostAuthState().isAuthRequested() || method.getProxyAuthState().isAuthRequested()) { LOG.debug("Authorization required"); if (method.getDoAuthentication()) { // process authentication response return true; } else { // let the client handle the authenticaiton LOG.info("Authentication requested but doAuthentication is " + "disabled"); return false; } } else { return false; } }
private void authenticateProxy(final HttpMethod method) throws AuthenticationException { // Clean up existing authentication headers if (!cleanAuthHeaders(method, PROXY_AUTH_RESP)) { // User defined authentication header(s) present return; } AuthState authstate = method.getProxyAuthState(); AuthScheme authscheme = authstate.getAuthScheme(); if (authscheme == null) { return; } if (authstate.isAuthRequested() || !authscheme.isConnectionBased()) { AuthScope authscope = new AuthScope( conn.getProxyHost(), conn.getProxyPort(), authscheme.getRealm(), authscheme.getSchemeName()); if (LOG.isDebugEnabled()) { LOG.debug("Authenticating with " + authscope); } Credentials credentials = this.state.getProxyCredentials(authscope); if (credentials != null) { String authstring = authscheme.authenticate(credentials, method); if (authstring != null) { method.addRequestHeader(new Header(PROXY_AUTH_RESP, authstring, true)); } } else { if (LOG.isWarnEnabled()) { LOG.warn("Required proxy credentials not available for " + authscope); if (method.getProxyAuthState().isPreemptive()) { LOG.warn( "Preemptive authentication requested but no default " + "proxy credentials available"); } } } } }
/** * Executes the method associated with this method director. * * @throws IOException * @throws HttpException */ public void executeMethod(final HttpMethod method) throws IOException, HttpException { if (method == null) { throw new IllegalArgumentException("Method may not be null"); } // Link all parameter collections to form the hierarchy: // Global -> HttpClient -> HostConfiguration -> HttpMethod this.hostConfiguration.getParams().setDefaults(this.params); method.getParams().setDefaults(this.hostConfiguration.getParams()); // Generate default request headers Collection defaults = (Collection) this.hostConfiguration.getParams().getParameter(HostParams.DEFAULT_HEADERS); if (defaults != null) { Iterator i = defaults.iterator(); while (i.hasNext()) { method.addRequestHeader((Header) i.next()); } } try { int maxRedirects = this.params.getIntParameter(HttpClientParams.MAX_REDIRECTS, 100); for (int redirectCount = 0; ; ) { // make sure the connection we have is appropriate if (this.conn != null && !hostConfiguration.hostEquals(this.conn)) { this.conn.setLocked(false); this.conn.releaseConnection(); this.conn = null; } // get a connection, if we need one if (this.conn == null) { this.conn = connectionManager.getConnectionWithTimeout( hostConfiguration, this.params.getConnectionManagerTimeout()); this.conn.setLocked(true); if (this.params.isAuthenticationPreemptive() || this.state.isAuthenticationPreemptive()) { LOG.debug("Preemptively sending default basic credentials"); method.getHostAuthState().setPreemptive(); method.getHostAuthState().setAuthAttempted(true); if (this.conn.isProxied() && !this.conn.isSecure()) { method.getProxyAuthState().setPreemptive(); method.getProxyAuthState().setAuthAttempted(true); } } } authenticate(method); executeWithRetry(method); if (this.connectMethod != null) { fakeResponse(method); break; } boolean retry = false; if (isRedirectNeeded(method)) { if (processRedirectResponse(method)) { retry = true; ++redirectCount; if (redirectCount >= maxRedirects) { LOG.error("Narrowly avoided an infinite loop in execute"); throw new RedirectException("Maximum redirects (" + maxRedirects + ") exceeded"); } if (LOG.isDebugEnabled()) { LOG.debug("Execute redirect " + redirectCount + " of " + maxRedirects); } } } if (isAuthenticationNeeded(method)) { if (processAuthenticationResponse(method)) { LOG.debug("Retry authentication"); retry = true; } } if (!retry) { break; } // retry - close previous stream. Caution - this causes // responseBodyConsumed to be called, which may also close the // connection. if (method.getResponseBodyAsStream() != null) { method.getResponseBodyAsStream().close(); } } // end of retry loop } finally { if (this.conn != null) { this.conn.setLocked(false); } // If the response has been fully processed, return the connection // to the pool. Use this flag, rather than other tests (like // responseStream == null), as subclasses, might reset the stream, // for example, reading the entire response into a file and then // setting the file as the stream. if ((releaseConnection || method.getResponseBodyAsStream() == null) && this.conn != null) { this.conn.releaseConnection(); } } }