KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > httpclient > HttpMethodDirector


1 /*
2  * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/HttpMethodDirector.java,v 1.34 2005/01/14 19:40:39 olegk Exp $
3  * $Revision: 486658 $
4  * $Date: 2006-12-13 14:05:50 +0000 (Wed, 13 Dec 2006) $
5  *
6  * ====================================================================
7  *
8  * Licensed to the Apache Software Foundation (ASF) under one or more
9  * contributor license agreements. See the NOTICE file distributed with
10  * this work for additional information regarding copyright ownership.
11  * The ASF licenses this file to You under the Apache License, Version 2.0
12  * (the "License"); you may not use this file except in compliance with
13  * the License. You may obtain a copy of the License at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  * ====================================================================
23  *
24  * This software consists of voluntary contributions made by many
25  * individuals on behalf of the Apache Software Foundation. For more
26  * information on the Apache Software Foundation, please see
27  * <http://www.apache.org/>.
28  *
29  */

30
31 package org.apache.commons.httpclient;
32
33 import java.io.IOException JavaDoc;
34 import java.util.Collection JavaDoc;
35 import java.util.HashSet JavaDoc;
36 import java.util.Iterator JavaDoc;
37 import java.util.Map JavaDoc;
38 import java.util.Set JavaDoc;
39
40 import org.apache.commons.httpclient.auth.AuthChallengeException;
41 import org.apache.commons.httpclient.auth.AuthChallengeParser;
42 import org.apache.commons.httpclient.auth.AuthChallengeProcessor;
43 import org.apache.commons.httpclient.auth.AuthScheme;
44 import org.apache.commons.httpclient.auth.AuthState;
45 import org.apache.commons.httpclient.auth.AuthenticationException;
46 import org.apache.commons.httpclient.auth.CredentialsProvider;
47 import org.apache.commons.httpclient.auth.CredentialsNotAvailableException;
48 import org.apache.commons.httpclient.auth.AuthScope;
49 import org.apache.commons.httpclient.auth.MalformedChallengeException;
50 import org.apache.commons.httpclient.params.HostParams;
51 import org.apache.commons.httpclient.params.HttpClientParams;
52 import org.apache.commons.httpclient.params.HttpConnectionParams;
53 import org.apache.commons.httpclient.params.HttpMethodParams;
54 import org.apache.commons.httpclient.params.HttpParams;
55 import org.apache.commons.logging.Log;
56 import org.apache.commons.logging.LogFactory;
57
58 /**
59  * Handles the process of executing a method including authentication, redirection and retries.
60  *
61  * @since 3.0
62  */

63 class HttpMethodDirector {
64
65     /** The www authenticate challange header. */
66     public static final String JavaDoc WWW_AUTH_CHALLENGE = "WWW-Authenticate";
67
68     /** The www authenticate response header. */
69     public static final String JavaDoc WWW_AUTH_RESP = "Authorization";
70
71     /** The proxy authenticate challange header. */
72     public static final String JavaDoc PROXY_AUTH_CHALLENGE = "Proxy-Authenticate";
73
74     /** The proxy authenticate response header. */
75     public static final String JavaDoc PROXY_AUTH_RESP = "Proxy-Authorization";
76
77     private static final Log LOG = LogFactory.getLog(HttpMethodDirector.class);
78
79     private ConnectMethod connectMethod;
80     
81     private HttpState state;
82     
83     private HostConfiguration hostConfiguration;
84     
85     private HttpConnectionManager connectionManager;
86     
87     private HttpClientParams params;
88     
89     private HttpConnection conn;
90     
91     /** A flag to indicate if the connection should be released after the method is executed. */
92     private boolean releaseConnection = false;
93
94     /** Authentication processor */
95     private AuthChallengeProcessor authProcessor = null;
96
97     private Set JavaDoc redirectLocations = null;
98     
99     public HttpMethodDirector(
100         final HttpConnectionManager connectionManager,
101         final HostConfiguration hostConfiguration,
102         final HttpClientParams params,
103         final HttpState state
104     ) {
105         super();
106         this.connectionManager = connectionManager;
107         this.hostConfiguration = hostConfiguration;
108         this.params = params;
109         this.state = state;
110         this.authProcessor = new AuthChallengeProcessor(this.params);
111     }
112     
113     
114     /**
115      * Executes the method associated with this method director.
116      *
117      * @throws IOException
118      * @throws HttpException
119      */

120     public void executeMethod(final HttpMethod method) throws IOException JavaDoc, HttpException {
121         if (method == null) {
122             throw new IllegalArgumentException JavaDoc("Method may not be null");
123         }
124         // Link all parameter collections to form the hierarchy:
125
// Global -> HttpClient -> HostConfiguration -> HttpMethod
126
this.hostConfiguration.getParams().setDefaults(this.params);
127         method.getParams().setDefaults(this.hostConfiguration.getParams());
128         
129         // Generate default request headers
130
Collection JavaDoc defaults = (Collection JavaDoc)this.hostConfiguration.getParams().
131             getParameter(HostParams.DEFAULT_HEADERS);
132         if (defaults != null) {
133             Iterator JavaDoc i = defaults.iterator();
134             while (i.hasNext()) {
135                 method.addRequestHeader((Header)i.next());
136             }
137         }
138         
139         try {
140             int maxRedirects = this.params.getIntParameter(HttpClientParams.MAX_REDIRECTS, 100);
141
142             for (int redirectCount = 0;;) {
143
144                 // make sure the connection we have is appropriate
145
if (this.conn != null && !hostConfiguration.hostEquals(this.conn)) {
146                     this.conn.setLocked(false);
147                     this.conn.releaseConnection();
148                     this.conn = null;
149                 }
150         
151                 // get a connection, if we need one
152
if (this.conn == null) {
153                     this.conn = connectionManager.getConnectionWithTimeout(
154                         hostConfiguration,
155                         this.params.getConnectionManagerTimeout()
156                     );
157                     this.conn.setLocked(true);
158                     if (this.params.isAuthenticationPreemptive()
159                      || this.state.isAuthenticationPreemptive())
160                     {
161                         LOG.debug("Preemptively sending default basic credentials");
162                         method.getHostAuthState().setPreemptive();
163                         method.getHostAuthState().setAuthAttempted(true);
164                         if (this.conn.isProxied() && !this.conn.isSecure()) {
165                             method.getProxyAuthState().setPreemptive();
166                             method.getProxyAuthState().setAuthAttempted(true);
167                         }
168                     }
169                 }
170                 authenticate(method);
171                 executeWithRetry(method);
172                 if (this.connectMethod != null) {
173                     fakeResponse(method);
174                     break;
175                 }
176                 
177                 boolean retry = false;
178                 if (isRedirectNeeded(method)) {
179                     if (processRedirectResponse(method)) {
180                         retry = true;
181                         ++redirectCount;
182                         if (redirectCount >= maxRedirects) {
183                             LOG.error("Narrowly avoided an infinite loop in execute");
184                             throw new RedirectException("Maximum redirects ("
185                                 + maxRedirects + ") exceeded");
186                         }
187                         if (LOG.isDebugEnabled()) {
188                             LOG.debug("Execute redirect " + redirectCount + " of " + maxRedirects);
189                         }
190                     }
191                 }
192                 if (isAuthenticationNeeded(method)) {
193                     if (processAuthenticationResponse(method)) {
194                         LOG.debug("Retry authentication");
195                         retry = true;
196                     }
197                 }
198                 if (!retry) {
199                     break;
200                 }
201                 // retry - close previous stream. Caution - this causes
202
// responseBodyConsumed to be called, which may also close the
203
// connection.
204
if (method.getResponseBodyAsStream() != null) {
205                     method.getResponseBodyAsStream().close();
206                 }
207
208             } //end of retry loop
209
} finally {
210             if (this.conn != null) {
211                 this.conn.setLocked(false);
212             }
213             // If the response has been fully processed, return the connection
214
// to the pool. Use this flag, rather than other tests (like
215
// responseStream == null), as subclasses, might reset the stream,
216
// for example, reading the entire response into a file and then
217
// setting the file as the stream.
218
if (
219                 (releaseConnection || method.getResponseBodyAsStream() == null)
220                 && this.conn != null
221             ) {
222                 this.conn.releaseConnection();
223             }
224         }
225
226     }
227
228     
229     private void authenticate(final HttpMethod method) {
230         try {
231             if (this.conn.isProxied() && !this.conn.isSecure()) {
232                 authenticateProxy(method);
233             }
234             authenticateHost(method);
235         } catch (AuthenticationException e) {
236             LOG.error(e.getMessage(), e);
237         }
238     }
239
240
241     private boolean cleanAuthHeaders(final HttpMethod method, final String JavaDoc name) {
242         Header[] authheaders = method.getRequestHeaders(name);
243         boolean clean = true;
244         for (int i = 0; i < authheaders.length; i++) {
245             Header authheader = authheaders[i];
246             if (authheader.isAutogenerated()) {
247                 method.removeRequestHeader(authheader);
248             } else {
249                 clean = false;
250             }
251         }
252         return clean;
253     }
254     
255
256     private void authenticateHost(final HttpMethod method) throws AuthenticationException {
257         // Clean up existing authentication headers
258
if (!cleanAuthHeaders(method, WWW_AUTH_RESP)) {
259             // User defined authentication header(s) present
260
return;
261         }
262         AuthState authstate = method.getHostAuthState();
263         AuthScheme authscheme = authstate.getAuthScheme();
264         if (authscheme == null) {
265             return;
266         }
267         if (authstate.isAuthRequested() || !authscheme.isConnectionBased()) {
268             String JavaDoc host = method.getParams().getVirtualHost();
269             if (host == null) {
270                 host = conn.getHost();
271             }
272             int port = conn.getPort();
273             AuthScope authscope = new AuthScope(
274                 host, port,
275                 authscheme.getRealm(),
276                 authscheme.getSchemeName());
277             if (LOG.isDebugEnabled()) {
278                 LOG.debug("Authenticating with " + authscope);
279             }
280             Credentials credentials = this.state.getCredentials(authscope);
281             if (credentials != null) {
282                 String JavaDoc authstring = authscheme.authenticate(credentials, method);
283                 if (authstring != null) {
284                     method.addRequestHeader(new Header(WWW_AUTH_RESP, authstring, true));
285                 }
286             } else {
287                 if (LOG.isWarnEnabled()) {
288                     LOG.warn("Required credentials not available for " + authscope);
289                     if (method.getHostAuthState().isPreemptive()) {
290                         LOG.warn("Preemptive authentication requested but no default " +
291                             "credentials available");
292                     }
293                 }
294             }
295         }
296     }
297
298
299     private void authenticateProxy(final HttpMethod method) throws AuthenticationException {
300         // Clean up existing authentication headers
301
if (!cleanAuthHeaders(method, PROXY_AUTH_RESP)) {
302             // User defined authentication header(s) present
303
return;
304         }
305         AuthState authstate = method.getProxyAuthState();
306         AuthScheme authscheme = authstate.getAuthScheme();
307         if (authscheme == null) {
308             return;
309         }
310         if (authstate.isAuthRequested() || !authscheme.isConnectionBased()) {
311             AuthScope authscope = new AuthScope(
312                 conn.getProxyHost(), conn.getProxyPort(),
313                 authscheme.getRealm(),
314                 authscheme.getSchemeName());
315             if (LOG.isDebugEnabled()) {
316                 LOG.debug("Authenticating with " + authscope);
317             }
318             Credentials credentials = this.state.getProxyCredentials(authscope);
319             if (credentials != null) {
320                 String JavaDoc authstring = authscheme.authenticate(credentials, method);
321                 if (authstring != null) {
322                     method.addRequestHeader(new Header(PROXY_AUTH_RESP, authstring, true));
323                 }
324             } else {
325                 if (LOG.isWarnEnabled()) {
326                     LOG.warn("Required proxy credentials not available for " + authscope);
327                     if (method.getProxyAuthState().isPreemptive()) {
328                         LOG.warn("Preemptive authentication requested but no default " +
329                             "proxy credentials available");
330                     }
331                 }
332             }
333         }
334     }
335     
336     
337     /**
338      * Applies connection parameters specified for a given method
339      *
340      * @param method HTTP method
341      *
342      * @throws IOException if an I/O occurs setting connection parameters
343      */

344     private void applyConnectionParams(final HttpMethod method) throws IOException JavaDoc {
345         int timeout = 0;
346         // see if a timeout is given for this method
347
Object JavaDoc param = method.getParams().getParameter(HttpMethodParams.SO_TIMEOUT);
348         if (param == null) {
349             // if not, use the default value
350
param = this.conn.getParams().getParameter(HttpConnectionParams.SO_TIMEOUT);
351         }
352         if (param != null) {
353             timeout = ((Integer JavaDoc)param).intValue();
354         }
355         this.conn.setSocketTimeout(timeout);
356     }
357     
358     /**
359      * Executes a method with the current hostConfiguration.
360      *
361      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
362      * can be recovered from.
363      * @throws HttpException if a protocol exception occurs. Usually protocol exceptions
364      * cannot be recovered from.
365      */

366     private void executeWithRetry(final HttpMethod method)
367         throws IOException JavaDoc, HttpException {
368         
369         /** How many times did this transparently handle a recoverable exception? */
370         int execCount = 0;
371         // loop until the method is successfully processed, the retryHandler
372
// returns false or a non-recoverable exception is thrown
373
try {
374             while (true) {
375                 execCount++;
376                 try {
377
378                     if (LOG.isTraceEnabled()) {
379                         LOG.trace("Attempt number " + execCount + " to process request");
380                     }
381                     if (this.conn.getParams().isStaleCheckingEnabled()) {
382                         this.conn.closeIfStale();
383                     }
384                     if (!this.conn.isOpen()) {
385                         // this connection must be opened before it can be used
386
// This has nothing to do with opening a secure tunnel
387
this.conn.open();
388                         if (this.conn.isProxied() && this.conn.isSecure()
389                         && !(method instanceof ConnectMethod)) {
390                             // we need to create a secure tunnel before we can execute the real method
391
if (!executeConnect()) {
392                                 // abort, the connect method failed
393
return;
394                             }
395                         }
396                     }
397                     applyConnectionParams(method);
398                     method.execute(state, this.conn);
399                     break;
400                 } catch (HttpException e) {
401                     // filter out protocol exceptions which cannot be recovered from
402
throw e;
403                 } catch (IOException JavaDoc e) {
404                     LOG.debug("Closing the connection.");
405                     this.conn.close();
406                     // test if this method should be retried
407
// ========================================
408
// this code is provided for backward compatibility with 2.0
409
// will be removed in the next major release
410
if (method instanceof HttpMethodBase) {
411                         MethodRetryHandler handler =
412                             ((HttpMethodBase)method).getMethodRetryHandler();
413                         if (handler != null) {
414                             if (!handler.retryMethod(
415                                     method,
416                                     this.conn,
417                                     new HttpRecoverableException(e.getMessage()),
418                                     execCount,
419                                     method.isRequestSent())) {
420                                 LOG.debug("Method retry handler returned false. "
421                                         + "Automatic recovery will not be attempted");
422                                 throw e;
423                             }
424                         }
425                     }
426                     // ========================================
427
HttpMethodRetryHandler handler =
428                         (HttpMethodRetryHandler)method.getParams().getParameter(
429                                 HttpMethodParams.RETRY_HANDLER);
430                     if (handler == null) {
431                         handler = new DefaultHttpMethodRetryHandler();
432                     }
433                     if (!handler.retryMethod(method, e, execCount)) {
434                         LOG.debug("Method retry handler returned false. "
435                                 + "Automatic recovery will not be attempted");
436                         throw e;
437                     }
438                     if (LOG.isInfoEnabled()) {
439                         LOG.info("I/O exception ("+ e.getClass().getName() +") caught when processing request: "
440                                 + e.getMessage());
441                     }
442                     if (LOG.isDebugEnabled()) {
443                         LOG.debug(e.getMessage(), e);
444                     }
445                     LOG.info("Retrying request");
446                 }
447             }
448         } catch (IOException JavaDoc e) {
449             if (this.conn.isOpen()) {
450                 LOG.debug("Closing the connection.");
451                 this.conn.close();
452             }
453             releaseConnection = true;
454             throw e;
455         } catch (RuntimeException JavaDoc e) {
456             if (this.conn.isOpen()) {
457                 LOG.debug("Closing the connection.");
458                 this.conn.close();
459             }
460             releaseConnection = true;
461             throw e;
462         }
463     }
464     
465     /**
466      * Executes a ConnectMethod to establish a tunneled connection.
467      *
468      * @return <code>true</code> if the connect was successful
469      *
470      * @throws IOException
471      * @throws HttpException
472      */

473     private boolean executeConnect()
474         throws IOException JavaDoc, HttpException {
475
476         this.connectMethod = new ConnectMethod(this.hostConfiguration);
477         this.connectMethod.getParams().setDefaults(this.hostConfiguration.getParams());
478         
479         int code;
480         for (;;) {
481             if (!this.conn.isOpen()) {
482                 this.conn.open();
483             }
484             if (this.params.isAuthenticationPreemptive()
485                     || this.state.isAuthenticationPreemptive()) {
486                 LOG.debug("Preemptively sending default basic credentials");
487                 this.connectMethod.getProxyAuthState().setPreemptive();
488                 this.connectMethod.getProxyAuthState().setAuthAttempted(true);
489             }
490             try {
491                 authenticateProxy(this.connectMethod);
492             } catch (AuthenticationException e) {
493                 LOG.error(e.getMessage(), e);
494             }
495             applyConnectionParams(this.connectMethod);
496             this.connectMethod.execute(state, this.conn);
497             code = this.connectMethod.getStatusCode();
498             boolean retry = false;
499             AuthState authstate = this.connectMethod.getProxyAuthState();
500             authstate.setAuthRequested(code == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED);
501             if (authstate.isAuthRequested()) {
502                 if (processAuthenticationResponse(this.connectMethod)) {
503                     retry = true;
504                 }
505             }
506             if (!retry) {
507                 break;
508             }
509             if (this.connectMethod.getResponseBodyAsStream() != null) {
510                 this.connectMethod.getResponseBodyAsStream().close();
511             }
512         }
513         if ((code >= 200) && (code < 300)) {
514             this.conn.tunnelCreated();
515             // Drop the connect method, as it is no longer needed
516
this.connectMethod = null;
517             return true;
518         } else {
519             this.conn.close();
520             return false;
521         }
522     }
523
524     /**
525      * Fake response
526      * @param method
527      * @return
528      */

529     
530     private void fakeResponse(final HttpMethod method)
531         throws IOException JavaDoc, HttpException {
532         // What is to follow is an ugly hack.
533
// I REALLY hate having to resort to such
534
// an appalling trick
535
// The only feasible solution is to split monolithic
536
// HttpMethod into HttpRequest/HttpResponse pair.
537
// That would allow to execute CONNECT method
538
// behind the scene and return CONNECT HttpResponse
539
// object in response to the original request that
540
// contains the correct status line, headers &
541
// response body.
542
LOG.debug("CONNECT failed, fake the response for the original method");
543         // Pass the status, headers and response stream to the wrapped
544
// method.
545
// To ensure that the connection is not released more than once
546
// this method is still responsible for releasing the connection.
547
// This will happen when the response body is consumed, or when
548
// the wrapped method closes the response connection in
549
// releaseConnection().
550
if (method instanceof HttpMethodBase) {
551             ((HttpMethodBase) method).fakeResponse(
552                 this.connectMethod.getStatusLine(),
553                 this.connectMethod.getResponseHeaderGroup(),
554                 this.connectMethod.getResponseBodyAsStream()
555             );
556             method.getProxyAuthState().setAuthScheme(
557                 this.connectMethod.getProxyAuthState().getAuthScheme());
558             this.connectMethod = null;
559         } else {
560             releaseConnection = true;
561             LOG.warn(
562                 "Unable to fake response on method as it is not derived from HttpMethodBase.");
563         }
564     }
565     
566     /**
567      * Process the redirect response.
568      *
569      * @return <code>true</code> if the redirect was successful
570      */

571     private boolean processRedirectResponse(final HttpMethod method)
572      throws RedirectException {
573         //get the location header to find out where to redirect to
574
Header locationHeader = method.getResponseHeader("location");
575         if (locationHeader == null) {
576             // got a redirect response, but no location header
577
LOG.error("Received redirect response " + method.getStatusCode()
578                     + " but no location header");
579             return false;
580         }
581         String JavaDoc location = locationHeader.getValue();
582         if (LOG.isDebugEnabled()) {
583             LOG.debug("Redirect requested to location '" + location + "'");
584         }
585         
586         //rfc2616 demands the location value be a complete URI
587
//Location = "Location" ":" absoluteURI
588
URI redirectUri = null;
589         URI currentUri = null;
590
591         try {
592             currentUri = new URI(
593                 this.conn.getProtocol().getScheme(),
594                 null,
595                 this.conn.getHost(),
596                 this.conn.getPort(),
597                 method.getPath()
598             );
599             
600             String JavaDoc charset = method.getParams().getUriCharset();
601             redirectUri = new URI(location, true, charset);
602             
603             if (redirectUri.isRelativeURI()) {
604                 if (this.params.isParameterTrue(HttpClientParams.REJECT_RELATIVE_REDIRECT)) {
605                     LOG.warn("Relative redirect location '" + location + "' not allowed");
606                     return false;
607                 } else {
608                     //location is incomplete, use current values for defaults
609
LOG.debug("Redirect URI is not absolute - parsing as relative");
610                     redirectUri = new URI(currentUri, redirectUri);
611                 }
612             } else {
613                 // Reset the default params
614
method.getParams().setDefaults(this.params);
615             }
616             method.setURI(redirectUri);
617             hostConfiguration.setHost(redirectUri);
618         } catch (URIException ex) {
619             throw new InvalidRedirectLocationException(
620                     "Invalid redirect location: " + location, location, ex);
621         }
622
623         if (this.params.isParameterFalse(HttpClientParams.ALLOW_CIRCULAR_REDIRECTS)) {
624             if (this.redirectLocations == null) {
625                 this.redirectLocations = new HashSet JavaDoc();
626             }
627             this.redirectLocations.add(currentUri);
628             try {
629                 if(redirectUri.hasQuery()) {
630                     redirectUri.setQuery(null);
631                 }
632             } catch (URIException e) {
633                 // Should never happen
634
return false;
635             }
636
637             if (this.redirectLocations.contains(redirectUri)) {
638                 throw new CircularRedirectException("Circular redirect to '" +
639                     redirectUri + "'");
640             }
641         }
642
643         if (LOG.isDebugEnabled()) {
644             LOG.debug("Redirecting from '" + currentUri.getEscapedURI()
645                 + "' to '" + redirectUri.getEscapedURI());
646         }
647         //And finally invalidate the actual authentication scheme
648
method.getHostAuthState().invalidate();
649         return true;
650     }
651
652     /**
653      * Processes a response that requires authentication
654      *
655      * @param method the current {@link HttpMethod HTTP method}
656      *
657      * @return <tt>true</tt> if the authentication challenge can be responsed to,
658      * (that is, at least one of the requested authentication scheme is supported,
659      * and matching credentials have been found), <tt>false</tt> otherwise.
660      */

661     private boolean processAuthenticationResponse(final HttpMethod method) {
662         LOG.trace("enter HttpMethodBase.processAuthenticationResponse("
663             + "HttpState, HttpConnection)");
664
665         try {
666             switch (method.getStatusCode()) {
667                 case HttpStatus.SC_UNAUTHORIZED:
668                     return processWWWAuthChallenge(method);
669                 case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
670                     return processProxyAuthChallenge(method);
671                 default:
672                     return false;
673             }
674         } catch (Exception JavaDoc e) {
675             if (LOG.isErrorEnabled()) {
676                 LOG.error(e.getMessage(), e);
677             }
678             return false;
679         }
680     }
681
682     private boolean processWWWAuthChallenge(final HttpMethod method)
683         throws MalformedChallengeException, AuthenticationException
684     {
685         AuthState authstate = method.getHostAuthState();
686         Map JavaDoc challenges = AuthChallengeParser.parseChallenges(
687             method.getResponseHeaders(WWW_AUTH_CHALLENGE));
688         if (challenges.isEmpty()) {
689             LOG.debug("Authentication challenge(s) not found");
690             return false;
691         }
692         AuthScheme authscheme = null;
693         try {
694             authscheme = this.authProcessor.processChallenge(authstate, challenges);
695         } catch (AuthChallengeException e) {
696             if (LOG.isWarnEnabled()) {
697                 LOG.warn(e.getMessage());
698             }
699         }
700         if (authscheme == null) {
701             return false;
702         }
703         String JavaDoc host = method.getParams().getVirtualHost();
704         if (host == null) {
705             host = conn.getHost();
706         }
707         int port = conn.getPort();
708         AuthScope authscope = new AuthScope(
709             host, port,
710             authscheme.getRealm(),
711             authscheme.getSchemeName());
712         
713         if (LOG.isDebugEnabled()) {
714             LOG.debug("Authentication scope: " + authscope);
715         }
716         if (authstate.isAuthAttempted() && authscheme.isComplete()) {
717             // Already tried and failed
718
Credentials credentials = promptForCredentials(
719                 authscheme, method.getParams(), authscope);
720             if (credentials == null) {
721                 if (LOG.isInfoEnabled()) {
722                     LOG.info("Failure authenticating with " + authscope);
723                 }
724                 return false;
725             } else {
726                 return true;
727             }
728         } else {
729             authstate.setAuthAttempted(true);
730             Credentials credentials = this.state.getCredentials(authscope);
731             if (credentials == null) {
732                 credentials = promptForCredentials(
733                     authscheme, method.getParams(), authscope);
734             }
735             if (credentials == null) {
736                 if (LOG.isInfoEnabled()) {
737                     LOG.info("No credentials available for " + authscope);
738                 }
739                 return false;
740             } else {
741                 return true;
742             }
743         }
744     }
745
746     private boolean processProxyAuthChallenge(final HttpMethod method)
747         throws MalformedChallengeException, AuthenticationException
748     {
749         AuthState authstate = method.getProxyAuthState();
750         Map JavaDoc proxyChallenges = AuthChallengeParser.parseChallenges(
751             method.getResponseHeaders(PROXY_AUTH_CHALLENGE));
752         if (proxyChallenges.isEmpty()) {
753             LOG.debug("Proxy authentication challenge(s) not found");
754             return false;
755         }
756         AuthScheme authscheme = null;
757         try {
758             authscheme = this.authProcessor.processChallenge(authstate, proxyChallenges);
759         } catch (AuthChallengeException e) {
760             if (LOG.isWarnEnabled()) {
761                 LOG.warn(e.getMessage());
762             }
763         }
764         if (authscheme == null) {
765             return false;
766         }
767         AuthScope authscope = new AuthScope(
768             conn.getProxyHost(), conn.getProxyPort(),
769             authscheme.getRealm(),
770             authscheme.getSchemeName());
771
772         if (LOG.isDebugEnabled()) {
773             LOG.debug("Proxy authentication scope: " + authscope);
774         }
775         if (authstate.isAuthAttempted() && authscheme.isComplete()) {
776             // Already tried and failed
777
Credentials credentials = promptForProxyCredentials(
778                 authscheme, method.getParams(), authscope);
779             if (credentials == null) {
780                 if (LOG.isInfoEnabled()) {
781                     LOG.info("Failure authenticating with " + authscope);
782                 }
783                 return false;
784             } else {
785                 return true;
786             }
787         } else {
788             authstate.setAuthAttempted(true);
789             Credentials credentials = this.state.getProxyCredentials(authscope);
790             if (credentials == null) {
791                 credentials = promptForProxyCredentials(
792                     authscheme, method.getParams(), authscope);
793             }
794             if (credentials == null) {
795                 if (LOG.isInfoEnabled()) {
796                     LOG.info("No credentials available for " + authscope);
797                 }
798                 return false;
799             } else {
800                 return true;
801             }
802         }
803     }
804
805     /**
806      * Tests if the {@link HttpMethod method} requires a redirect to another location.
807      *
808      * @param method HTTP method
809      *
810      * @return boolean <tt>true</tt> if a retry is needed, <tt>false</tt> otherwise.
811      */

812     private boolean isRedirectNeeded(final HttpMethod method) {
813         switch (method.getStatusCode()) {
814             case HttpStatus.SC_MOVED_TEMPORARILY:
815             case HttpStatus.SC_MOVED_PERMANENTLY:
816             case HttpStatus.SC_SEE_OTHER:
817             case HttpStatus.SC_TEMPORARY_REDIRECT:
818                 LOG.debug("Redirect required");
819                 if (method.getFollowRedirects()) {
820                     return true;
821                 } else {
822                     return false;
823                 }
824             default:
825                 return false;
826         } //end of switch
827
}
828
829     /**
830      * Tests if the {@link HttpMethod method} requires authentication.
831      *
832      * @param method HTTP method
833      *
834      * @return boolean <tt>true</tt> if a retry is needed, <tt>false</tt> otherwise.
835      */

836     private boolean isAuthenticationNeeded(final HttpMethod method) {
837         method.getHostAuthState().setAuthRequested(
838                 method.getStatusCode() == HttpStatus.SC_UNAUTHORIZED);
839         method.getProxyAuthState().setAuthRequested(
840                 method.getStatusCode() == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED);
841         if (method.getHostAuthState().isAuthRequested() ||
842             method.getProxyAuthState().isAuthRequested()) {
843             LOG.debug("Authorization required");
844             if (method.getDoAuthentication()) { //process authentication response
845
return true;
846             } else { //let the client handle the authenticaiton
847
LOG.info("Authentication requested but doAuthentication is "
848                         + "disabled");
849                 return false;
850             }
851         } else {
852             return false;
853         }
854     }
855
856     private Credentials promptForCredentials(
857         final AuthScheme authScheme,
858         final HttpParams params,
859         final AuthScope authscope)
860     {
861         LOG.debug("Credentials required");
862         Credentials creds = null;
863         CredentialsProvider credProvider =
864             (CredentialsProvider)params.getParameter(CredentialsProvider.PROVIDER);
865         if (credProvider != null) {
866             try {
867                 creds = credProvider.getCredentials(
868                     authScheme, authscope.getHost(), authscope.getPort(), false);
869             } catch (CredentialsNotAvailableException e) {
870                 LOG.warn(e.getMessage());
871             }
872             if (creds != null) {
873                 this.state.setCredentials(authscope, creds);
874                 if (LOG.isDebugEnabled()) {
875                     LOG.debug(authscope + " new credentials given");
876                 }
877             }
878         } else {
879             LOG.debug("Credentials provider not available");
880         }
881         return creds;
882     }
883
884     private Credentials promptForProxyCredentials(
885         final AuthScheme authScheme,
886         final HttpParams params,
887         final AuthScope authscope)
888     {
889         LOG.debug("Proxy credentials required");
890         Credentials creds = null;
891         CredentialsProvider credProvider =
892             (CredentialsProvider)params.getParameter(CredentialsProvider.PROVIDER);
893         if (credProvider != null) {
894             try {
895                 creds = credProvider.getCredentials(
896                     authScheme, authscope.getHost(), authscope.getPort(), true);
897             } catch (CredentialsNotAvailableException e) {
898                 LOG.warn(e.getMessage());
899             }
900             if (creds != null) {
901                 this.state.setProxyCredentials(authscope, creds);
902                 if (LOG.isDebugEnabled()) {
903                     LOG.debug(authscope + " new credentials given");
904                 }
905             }
906         } else {
907             LOG.debug("Proxy credentials provider not available");
908         }
909         return creds;
910     }
911
912     /**
913      * @return
914      */

915     public HostConfiguration getHostConfiguration() {
916         return hostConfiguration;
917     }
918
919     /**
920      * @return
921      */

922     public HttpState getState() {
923         return state;
924     }
925
926     /**
927      * @return
928      */

929     public HttpConnectionManager getConnectionManager() {
930         return connectionManager;
931     }
932
933     /**
934      * @return
935      */

936     public HttpParams getParams() {
937         return this.params;
938     }
939 }
940
Popular Tags