Source Code Cross Referenced for HttpMethodDirector.java in  » Net » Apache-common-HttpClient » org » apache » commons » httpclient » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Net » Apache common HttpClient » org.apache.commons.httpclient 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * $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 $
003:         * $Revision: 486658 $
004:         * $Date: 2006-12-13 15:05:50 +0100 (Wed, 13 Dec 2006) $
005:         *
006:         * ====================================================================
007:         *
008:         *  Licensed to the Apache Software Foundation (ASF) under one or more
009:         *  contributor license agreements.  See the NOTICE file distributed with
010:         *  this work for additional information regarding copyright ownership.
011:         *  The ASF licenses this file to You under the Apache License, Version 2.0
012:         *  (the "License"); you may not use this file except in compliance with
013:         *  the License.  You may obtain a copy of the License at
014:         *
015:         *      http://www.apache.org/licenses/LICENSE-2.0
016:         *
017:         *  Unless required by applicable law or agreed to in writing, software
018:         *  distributed under the License is distributed on an "AS IS" BASIS,
019:         *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
020:         *  See the License for the specific language governing permissions and
021:         *  limitations under the License.
022:         * ====================================================================
023:         *
024:         * This software consists of voluntary contributions made by many
025:         * individuals on behalf of the Apache Software Foundation.  For more
026:         * information on the Apache Software Foundation, please see
027:         * <http://www.apache.org/>.
028:         *
029:         */
030:
031:        package org.apache.commons.httpclient;
032:
033:        import java.io.IOException;
034:        import java.util.Collection;
035:        import java.util.HashSet;
036:        import java.util.Iterator;
037:        import java.util.Map;
038:        import java.util.Set;
039:
040:        import org.apache.commons.httpclient.auth.AuthChallengeException;
041:        import org.apache.commons.httpclient.auth.AuthChallengeParser;
042:        import org.apache.commons.httpclient.auth.AuthChallengeProcessor;
043:        import org.apache.commons.httpclient.auth.AuthScheme;
044:        import org.apache.commons.httpclient.auth.AuthState;
045:        import org.apache.commons.httpclient.auth.AuthenticationException;
046:        import org.apache.commons.httpclient.auth.CredentialsProvider;
047:        import org.apache.commons.httpclient.auth.CredentialsNotAvailableException;
048:        import org.apache.commons.httpclient.auth.AuthScope;
049:        import org.apache.commons.httpclient.auth.MalformedChallengeException;
050:        import org.apache.commons.httpclient.params.HostParams;
051:        import org.apache.commons.httpclient.params.HttpClientParams;
052:        import org.apache.commons.httpclient.params.HttpConnectionParams;
053:        import org.apache.commons.httpclient.params.HttpMethodParams;
054:        import org.apache.commons.httpclient.params.HttpParams;
055:        import org.apache.commons.logging.Log;
056:        import org.apache.commons.logging.LogFactory;
057:
058:        /**
059:         * Handles the process of executing a method including authentication, redirection and retries.
060:         * 
061:         * @since 3.0
062:         */
063:        class HttpMethodDirector {
064:
065:            /** The www authenticate challange header. */
066:            public static final String WWW_AUTH_CHALLENGE = "WWW-Authenticate";
067:
068:            /** The www authenticate response header. */
069:            public static final String WWW_AUTH_RESP = "Authorization";
070:
071:            /** The proxy authenticate challange header. */
072:            public static final String PROXY_AUTH_CHALLENGE = "Proxy-Authenticate";
073:
074:            /** The proxy authenticate response header. */
075:            public static final String PROXY_AUTH_RESP = "Proxy-Authorization";
076:
077:            private static final Log LOG = LogFactory
078:                    .getLog(HttpMethodDirector.class);
079:
080:            private ConnectMethod connectMethod;
081:
082:            private HttpState state;
083:
084:            private HostConfiguration hostConfiguration;
085:
086:            private HttpConnectionManager connectionManager;
087:
088:            private HttpClientParams params;
089:
090:            private HttpConnection conn;
091:
092:            /** A flag to indicate if the connection should be released after the method is executed. */
093:            private boolean releaseConnection = false;
094:
095:            /** Authentication processor */
096:            private AuthChallengeProcessor authProcessor = null;
097:
098:            private Set redirectLocations = null;
099:
100:            public HttpMethodDirector(
101:                    final HttpConnectionManager connectionManager,
102:                    final HostConfiguration hostConfiguration,
103:                    final HttpClientParams params, final HttpState state) {
104:                super ();
105:                this .connectionManager = connectionManager;
106:                this .hostConfiguration = hostConfiguration;
107:                this .params = params;
108:                this .state = state;
109:                this .authProcessor = new AuthChallengeProcessor(this .params);
110:            }
111:
112:            /**
113:             * Executes the method associated with this method director.
114:             * 
115:             * @throws IOException
116:             * @throws HttpException
117:             */
118:            public void executeMethod(final HttpMethod method)
119:                    throws IOException, HttpException {
120:                if (method == null) {
121:                    throw new IllegalArgumentException("Method may not be null");
122:                }
123:                // Link all parameter collections to form the hierarchy:
124:                // Global -> HttpClient -> HostConfiguration -> HttpMethod
125:                this .hostConfiguration.getParams().setDefaults(this .params);
126:                method.getParams().setDefaults(
127:                        this .hostConfiguration.getParams());
128:
129:                // Generate default request headers
130:                Collection defaults = (Collection) this .hostConfiguration
131:                        .getParams().getParameter(HostParams.DEFAULT_HEADERS);
132:                if (defaults != null) {
133:                    Iterator i = defaults.iterator();
134:                    while (i.hasNext()) {
135:                        method.addRequestHeader((Header) i.next());
136:                    }
137:                }
138:
139:                try {
140:                    int maxRedirects = this .params.getIntParameter(
141:                            HttpClientParams.MAX_REDIRECTS, 100);
142:
143:                    for (int redirectCount = 0;;) {
144:
145:                        // make sure the connection we have is appropriate
146:                        if (this .conn != null
147:                                && !hostConfiguration.hostEquals(this .conn)) {
148:                            this .conn.setLocked(false);
149:                            this .conn.releaseConnection();
150:                            this .conn = null;
151:                        }
152:
153:                        // get a connection, if we need one
154:                        if (this .conn == null) {
155:                            this .conn = connectionManager
156:                                    .getConnectionWithTimeout(
157:                                            hostConfiguration,
158:                                            this .params
159:                                                    .getConnectionManagerTimeout());
160:                            this .conn.setLocked(true);
161:                            if (this .params.isAuthenticationPreemptive()
162:                                    || this .state.isAuthenticationPreemptive()) {
163:                                LOG
164:                                        .debug("Preemptively sending default basic credentials");
165:                                method.getHostAuthState().setPreemptive();
166:                                method.getHostAuthState()
167:                                        .setAuthAttempted(true);
168:                                if (this .conn.isProxied()
169:                                        && !this .conn.isSecure()) {
170:                                    method.getProxyAuthState().setPreemptive();
171:                                    method.getProxyAuthState()
172:                                            .setAuthAttempted(true);
173:                                }
174:                            }
175:                        }
176:                        authenticate(method);
177:                        executeWithRetry(method);
178:                        if (this .connectMethod != null) {
179:                            fakeResponse(method);
180:                            break;
181:                        }
182:
183:                        boolean retry = false;
184:                        if (isRedirectNeeded(method)) {
185:                            if (processRedirectResponse(method)) {
186:                                retry = true;
187:                                ++redirectCount;
188:                                if (redirectCount >= maxRedirects) {
189:                                    LOG
190:                                            .error("Narrowly avoided an infinite loop in execute");
191:                                    throw new RedirectException(
192:                                            "Maximum redirects ("
193:                                                    + maxRedirects
194:                                                    + ") exceeded");
195:                                }
196:                                if (LOG.isDebugEnabled()) {
197:                                    LOG.debug("Execute redirect "
198:                                            + redirectCount + " of "
199:                                            + maxRedirects);
200:                                }
201:                            }
202:                        }
203:                        if (isAuthenticationNeeded(method)) {
204:                            if (processAuthenticationResponse(method)) {
205:                                LOG.debug("Retry authentication");
206:                                retry = true;
207:                            }
208:                        }
209:                        if (!retry) {
210:                            break;
211:                        }
212:                        // retry - close previous stream.  Caution - this causes
213:                        // responseBodyConsumed to be called, which may also close the
214:                        // connection.
215:                        if (method.getResponseBodyAsStream() != null) {
216:                            method.getResponseBodyAsStream().close();
217:                        }
218:
219:                    } //end of retry loop
220:                } finally {
221:                    if (this .conn != null) {
222:                        this .conn.setLocked(false);
223:                    }
224:                    // If the response has been fully processed, return the connection
225:                    // to the pool.  Use this flag, rather than other tests (like
226:                    // responseStream == null), as subclasses, might reset the stream,
227:                    // for example, reading the entire response into a file and then
228:                    // setting the file as the stream.
229:                    if ((releaseConnection || method.getResponseBodyAsStream() == null)
230:                            && this .conn != null) {
231:                        this .conn.releaseConnection();
232:                    }
233:                }
234:
235:            }
236:
237:            private void authenticate(final HttpMethod method) {
238:                try {
239:                    if (this .conn.isProxied() && !this .conn.isSecure()) {
240:                        authenticateProxy(method);
241:                    }
242:                    authenticateHost(method);
243:                } catch (AuthenticationException e) {
244:                    LOG.error(e.getMessage(), e);
245:                }
246:            }
247:
248:            private boolean cleanAuthHeaders(final HttpMethod method,
249:                    final String name) {
250:                Header[] authheaders = method.getRequestHeaders(name);
251:                boolean clean = true;
252:                for (int i = 0; i < authheaders.length; i++) {
253:                    Header authheader = authheaders[i];
254:                    if (authheader.isAutogenerated()) {
255:                        method.removeRequestHeader(authheader);
256:                    } else {
257:                        clean = false;
258:                    }
259:                }
260:                return clean;
261:            }
262:
263:            private void authenticateHost(final HttpMethod method)
264:                    throws AuthenticationException {
265:                // Clean up existing authentication headers
266:                if (!cleanAuthHeaders(method, WWW_AUTH_RESP)) {
267:                    // User defined authentication header(s) present
268:                    return;
269:                }
270:                AuthState authstate = method.getHostAuthState();
271:                AuthScheme authscheme = authstate.getAuthScheme();
272:                if (authscheme == null) {
273:                    return;
274:                }
275:                if (authstate.isAuthRequested()
276:                        || !authscheme.isConnectionBased()) {
277:                    String host = method.getParams().getVirtualHost();
278:                    if (host == null) {
279:                        host = conn.getHost();
280:                    }
281:                    int port = conn.getPort();
282:                    AuthScope authscope = new AuthScope(host, port, authscheme
283:                            .getRealm(), authscheme.getSchemeName());
284:                    if (LOG.isDebugEnabled()) {
285:                        LOG.debug("Authenticating with " + authscope);
286:                    }
287:                    Credentials credentials = this .state
288:                            .getCredentials(authscope);
289:                    if (credentials != null) {
290:                        String authstring = authscheme.authenticate(
291:                                credentials, method);
292:                        if (authstring != null) {
293:                            method.addRequestHeader(new Header(WWW_AUTH_RESP,
294:                                    authstring, true));
295:                        }
296:                    } else {
297:                        if (LOG.isWarnEnabled()) {
298:                            LOG.warn("Required credentials not available for "
299:                                    + authscope);
300:                            if (method.getHostAuthState().isPreemptive()) {
301:                                LOG
302:                                        .warn("Preemptive authentication requested but no default "
303:                                                + "credentials available");
304:                            }
305:                        }
306:                    }
307:                }
308:            }
309:
310:            private void authenticateProxy(final HttpMethod method)
311:                    throws AuthenticationException {
312:                // Clean up existing authentication headers
313:                if (!cleanAuthHeaders(method, PROXY_AUTH_RESP)) {
314:                    // User defined authentication header(s) present
315:                    return;
316:                }
317:                AuthState authstate = method.getProxyAuthState();
318:                AuthScheme authscheme = authstate.getAuthScheme();
319:                if (authscheme == null) {
320:                    return;
321:                }
322:                if (authstate.isAuthRequested()
323:                        || !authscheme.isConnectionBased()) {
324:                    AuthScope authscope = new AuthScope(conn.getProxyHost(),
325:                            conn.getProxyPort(), authscheme.getRealm(),
326:                            authscheme.getSchemeName());
327:                    if (LOG.isDebugEnabled()) {
328:                        LOG.debug("Authenticating with " + authscope);
329:                    }
330:                    Credentials credentials = this .state
331:                            .getProxyCredentials(authscope);
332:                    if (credentials != null) {
333:                        String authstring = authscheme.authenticate(
334:                                credentials, method);
335:                        if (authstring != null) {
336:                            method.addRequestHeader(new Header(PROXY_AUTH_RESP,
337:                                    authstring, true));
338:                        }
339:                    } else {
340:                        if (LOG.isWarnEnabled()) {
341:                            LOG
342:                                    .warn("Required proxy credentials not available for "
343:                                            + authscope);
344:                            if (method.getProxyAuthState().isPreemptive()) {
345:                                LOG
346:                                        .warn("Preemptive authentication requested but no default "
347:                                                + "proxy credentials available");
348:                            }
349:                        }
350:                    }
351:                }
352:            }
353:
354:            /**
355:             * Applies connection parameters specified for a given method
356:             * 
357:             * @param method HTTP method
358:             * 
359:             * @throws IOException if an I/O occurs setting connection parameters 
360:             */
361:            private void applyConnectionParams(final HttpMethod method)
362:                    throws IOException {
363:                int timeout = 0;
364:                // see if a timeout is given for this method
365:                Object param = method.getParams().getParameter(
366:                        HttpMethodParams.SO_TIMEOUT);
367:                if (param == null) {
368:                    // if not, use the default value
369:                    param = this .conn.getParams().getParameter(
370:                            HttpConnectionParams.SO_TIMEOUT);
371:                }
372:                if (param != null) {
373:                    timeout = ((Integer) param).intValue();
374:                }
375:                this .conn.setSocketTimeout(timeout);
376:            }
377:
378:            /**
379:             * Executes a method with the current hostConfiguration.
380:             *
381:             * @throws IOException if an I/O (transport) error occurs. Some transport exceptions 
382:             * can be recovered from.
383:             * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
384:             * cannot be recovered from.
385:             */
386:            private void executeWithRetry(final HttpMethod method)
387:                    throws IOException, HttpException {
388:
389:                /** How many times did this transparently handle a recoverable exception? */
390:                int execCount = 0;
391:                // loop until the method is successfully processed, the retryHandler 
392:                // returns false or a non-recoverable exception is thrown
393:                try {
394:                    while (true) {
395:                        execCount++;
396:                        try {
397:
398:                            if (LOG.isTraceEnabled()) {
399:                                LOG.trace("Attempt number " + execCount
400:                                        + " to process request");
401:                            }
402:                            if (this .conn.getParams().isStaleCheckingEnabled()) {
403:                                this .conn.closeIfStale();
404:                            }
405:                            if (!this .conn.isOpen()) {
406:                                // this connection must be opened before it can be used
407:                                // This has nothing to do with opening a secure tunnel
408:                                this .conn.open();
409:                                if (this .conn.isProxied()
410:                                        && this .conn.isSecure()
411:                                        && !(method instanceof  ConnectMethod)) {
412:                                    // we need to create a secure tunnel before we can execute the real method
413:                                    if (!executeConnect()) {
414:                                        // abort, the connect method failed
415:                                        return;
416:                                    }
417:                                }
418:                            }
419:                            applyConnectionParams(method);
420:                            method.execute(state, this .conn);
421:                            break;
422:                        } catch (HttpException e) {
423:                            // filter out protocol exceptions which cannot be recovered from
424:                            throw e;
425:                        } catch (IOException e) {
426:                            LOG.debug("Closing the connection.");
427:                            this .conn.close();
428:                            // test if this method should be retried
429:                            // ========================================
430:                            // this code is provided for backward compatibility with 2.0
431:                            // will be removed in the next major release
432:                            if (method instanceof  HttpMethodBase) {
433:                                MethodRetryHandler handler = ((HttpMethodBase) method)
434:                                        .getMethodRetryHandler();
435:                                if (handler != null) {
436:                                    if (!handler.retryMethod(method, this .conn,
437:                                            new HttpRecoverableException(e
438:                                                    .getMessage()), execCount,
439:                                            method.isRequestSent())) {
440:                                        LOG
441:                                                .debug("Method retry handler returned false. "
442:                                                        + "Automatic recovery will not be attempted");
443:                                        throw e;
444:                                    }
445:                                }
446:                            }
447:                            // ========================================
448:                            HttpMethodRetryHandler handler = (HttpMethodRetryHandler) method
449:                                    .getParams().getParameter(
450:                                            HttpMethodParams.RETRY_HANDLER);
451:                            if (handler == null) {
452:                                handler = new DefaultHttpMethodRetryHandler();
453:                            }
454:                            if (!handler.retryMethod(method, e, execCount)) {
455:                                LOG
456:                                        .debug("Method retry handler returned false. "
457:                                                + "Automatic recovery will not be attempted");
458:                                throw e;
459:                            }
460:                            if (LOG.isInfoEnabled()) {
461:                                LOG.info("I/O exception ("
462:                                        + e.getClass().getName()
463:                                        + ") caught when processing request: "
464:                                        + e.getMessage());
465:                            }
466:                            if (LOG.isDebugEnabled()) {
467:                                LOG.debug(e.getMessage(), e);
468:                            }
469:                            LOG.info("Retrying request");
470:                        }
471:                    }
472:                } catch (IOException e) {
473:                    if (this .conn.isOpen()) {
474:                        LOG.debug("Closing the connection.");
475:                        this .conn.close();
476:                    }
477:                    releaseConnection = true;
478:                    throw e;
479:                } catch (RuntimeException e) {
480:                    if (this .conn.isOpen()) {
481:                        LOG.debug("Closing the connection.");
482:                        this .conn.close();
483:                    }
484:                    releaseConnection = true;
485:                    throw e;
486:                }
487:            }
488:
489:            /**
490:             * Executes a ConnectMethod to establish a tunneled connection.
491:             * 
492:             * @return <code>true</code> if the connect was successful
493:             * 
494:             * @throws IOException
495:             * @throws HttpException
496:             */
497:            private boolean executeConnect() throws IOException, HttpException {
498:
499:                this .connectMethod = new ConnectMethod(this .hostConfiguration);
500:                this .connectMethod.getParams().setDefaults(
501:                        this .hostConfiguration.getParams());
502:
503:                int code;
504:                for (;;) {
505:                    if (!this .conn.isOpen()) {
506:                        this .conn.open();
507:                    }
508:                    if (this .params.isAuthenticationPreemptive()
509:                            || this .state.isAuthenticationPreemptive()) {
510:                        LOG
511:                                .debug("Preemptively sending default basic credentials");
512:                        this .connectMethod.getProxyAuthState().setPreemptive();
513:                        this .connectMethod.getProxyAuthState()
514:                                .setAuthAttempted(true);
515:                    }
516:                    try {
517:                        authenticateProxy(this .connectMethod);
518:                    } catch (AuthenticationException e) {
519:                        LOG.error(e.getMessage(), e);
520:                    }
521:                    applyConnectionParams(this .connectMethod);
522:                    this .connectMethod.execute(state, this .conn);
523:                    code = this .connectMethod.getStatusCode();
524:                    boolean retry = false;
525:                    AuthState authstate = this .connectMethod
526:                            .getProxyAuthState();
527:                    authstate
528:                            .setAuthRequested(code == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED);
529:                    if (authstate.isAuthRequested()) {
530:                        if (processAuthenticationResponse(this .connectMethod)) {
531:                            retry = true;
532:                        }
533:                    }
534:                    if (!retry) {
535:                        break;
536:                    }
537:                    if (this .connectMethod.getResponseBodyAsStream() != null) {
538:                        this .connectMethod.getResponseBodyAsStream().close();
539:                    }
540:                }
541:                if ((code >= 200) && (code < 300)) {
542:                    this .conn.tunnelCreated();
543:                    // Drop the connect method, as it is no longer needed
544:                    this .connectMethod = null;
545:                    return true;
546:                } else {
547:                    this .conn.close();
548:                    return false;
549:                }
550:            }
551:
552:            /**
553:             * Fake response
554:             * @param method
555:             * @return
556:             */
557:
558:            private void fakeResponse(final HttpMethod method)
559:                    throws IOException, HttpException {
560:                // What is to follow is an ugly hack.
561:                // I REALLY hate having to resort to such
562:                // an appalling trick
563:                // The only feasible solution is to split monolithic
564:                // HttpMethod into HttpRequest/HttpResponse pair.
565:                // That would allow to execute CONNECT method 
566:                // behind the scene and return CONNECT HttpResponse 
567:                // object in response to the original request that 
568:                // contains the correct status line, headers & 
569:                // response body.
570:                LOG
571:                        .debug("CONNECT failed, fake the response for the original method");
572:                // Pass the status, headers and response stream to the wrapped
573:                // method.
574:                // To ensure that the connection is not released more than once
575:                // this method is still responsible for releasing the connection. 
576:                // This will happen when the response body is consumed, or when
577:                // the wrapped method closes the response connection in 
578:                // releaseConnection().
579:                if (method instanceof  HttpMethodBase) {
580:                    ((HttpMethodBase) method).fakeResponse(this .connectMethod
581:                            .getStatusLine(), this .connectMethod
582:                            .getResponseHeaderGroup(), this .connectMethod
583:                            .getResponseBodyAsStream());
584:                    method.getProxyAuthState().setAuthScheme(
585:                            this .connectMethod.getProxyAuthState()
586:                                    .getAuthScheme());
587:                    this .connectMethod = null;
588:                } else {
589:                    releaseConnection = true;
590:                    LOG
591:                            .warn("Unable to fake response on method as it is not derived from HttpMethodBase.");
592:                }
593:            }
594:
595:            /**
596:             * Process the redirect response.
597:             * 
598:             * @return <code>true</code> if the redirect was successful
599:             */
600:            private boolean processRedirectResponse(final HttpMethod method)
601:                    throws RedirectException {
602:                //get the location header to find out where to redirect to
603:                Header locationHeader = method.getResponseHeader("location");
604:                if (locationHeader == null) {
605:                    // got a redirect response, but no location header
606:                    LOG.error("Received redirect response "
607:                            + method.getStatusCode()
608:                            + " but no location header");
609:                    return false;
610:                }
611:                String location = locationHeader.getValue();
612:                if (LOG.isDebugEnabled()) {
613:                    LOG.debug("Redirect requested to location '" + location
614:                            + "'");
615:                }
616:
617:                //rfc2616 demands the location value be a complete URI
618:                //Location       = "Location" ":" absoluteURI
619:                URI redirectUri = null;
620:                URI currentUri = null;
621:
622:                try {
623:                    currentUri = new URI(this .conn.getProtocol().getScheme(),
624:                            null, this .conn.getHost(), this .conn.getPort(),
625:                            method.getPath());
626:
627:                    String charset = method.getParams().getUriCharset();
628:                    redirectUri = new URI(location, true, charset);
629:
630:                    if (redirectUri.isRelativeURI()) {
631:                        if (this .params
632:                                .isParameterTrue(HttpClientParams.REJECT_RELATIVE_REDIRECT)) {
633:                            LOG.warn("Relative redirect location '" + location
634:                                    + "' not allowed");
635:                            return false;
636:                        } else {
637:                            //location is incomplete, use current values for defaults
638:                            LOG
639:                                    .debug("Redirect URI is not absolute - parsing as relative");
640:                            redirectUri = new URI(currentUri, redirectUri);
641:                        }
642:                    } else {
643:                        // Reset the default params
644:                        method.getParams().setDefaults(this .params);
645:                    }
646:                    method.setURI(redirectUri);
647:                    hostConfiguration.setHost(redirectUri);
648:                } catch (URIException ex) {
649:                    throw new InvalidRedirectLocationException(
650:                            "Invalid redirect location: " + location, location,
651:                            ex);
652:                }
653:
654:                if (this .params
655:                        .isParameterFalse(HttpClientParams.ALLOW_CIRCULAR_REDIRECTS)) {
656:                    if (this .redirectLocations == null) {
657:                        this .redirectLocations = new HashSet();
658:                    }
659:                    this .redirectLocations.add(currentUri);
660:                    try {
661:                        if (redirectUri.hasQuery()) {
662:                            redirectUri.setQuery(null);
663:                        }
664:                    } catch (URIException e) {
665:                        // Should never happen
666:                        return false;
667:                    }
668:
669:                    if (this .redirectLocations.contains(redirectUri)) {
670:                        throw new CircularRedirectException(
671:                                "Circular redirect to '" + redirectUri + "'");
672:                    }
673:                }
674:
675:                if (LOG.isDebugEnabled()) {
676:                    LOG.debug("Redirecting from '" + currentUri.getEscapedURI()
677:                            + "' to '" + redirectUri.getEscapedURI());
678:                }
679:                //And finally invalidate the actual authentication scheme
680:                method.getHostAuthState().invalidate();
681:                return true;
682:            }
683:
684:            /**
685:             * Processes a response that requires authentication
686:             *
687:             * @param method the current {@link HttpMethod HTTP method}
688:             *
689:             * @return <tt>true</tt> if the authentication challenge can be responsed to,
690:             *   (that is, at least one of the requested authentication scheme is supported, 
691:             *   and matching credentials have been found), <tt>false</tt> otherwise.
692:             */
693:            private boolean processAuthenticationResponse(
694:                    final HttpMethod method) {
695:                LOG.trace("enter HttpMethodBase.processAuthenticationResponse("
696:                        + "HttpState, HttpConnection)");
697:
698:                try {
699:                    switch (method.getStatusCode()) {
700:                    case HttpStatus.SC_UNAUTHORIZED:
701:                        return processWWWAuthChallenge(method);
702:                    case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
703:                        return processProxyAuthChallenge(method);
704:                    default:
705:                        return false;
706:                    }
707:                } catch (Exception e) {
708:                    if (LOG.isErrorEnabled()) {
709:                        LOG.error(e.getMessage(), e);
710:                    }
711:                    return false;
712:                }
713:            }
714:
715:            private boolean processWWWAuthChallenge(final HttpMethod method)
716:                    throws MalformedChallengeException, AuthenticationException {
717:                AuthState authstate = method.getHostAuthState();
718:                Map challenges = AuthChallengeParser.parseChallenges(method
719:                        .getResponseHeaders(WWW_AUTH_CHALLENGE));
720:                if (challenges.isEmpty()) {
721:                    LOG.debug("Authentication challenge(s) not found");
722:                    return false;
723:                }
724:                AuthScheme authscheme = null;
725:                try {
726:                    authscheme = this .authProcessor.processChallenge(authstate,
727:                            challenges);
728:                } catch (AuthChallengeException e) {
729:                    if (LOG.isWarnEnabled()) {
730:                        LOG.warn(e.getMessage());
731:                    }
732:                }
733:                if (authscheme == null) {
734:                    return false;
735:                }
736:                String host = method.getParams().getVirtualHost();
737:                if (host == null) {
738:                    host = conn.getHost();
739:                }
740:                int port = conn.getPort();
741:                AuthScope authscope = new AuthScope(host, port, authscheme
742:                        .getRealm(), authscheme.getSchemeName());
743:
744:                if (LOG.isDebugEnabled()) {
745:                    LOG.debug("Authentication scope: " + authscope);
746:                }
747:                if (authstate.isAuthAttempted() && authscheme.isComplete()) {
748:                    // Already tried and failed
749:                    Credentials credentials = promptForCredentials(authscheme,
750:                            method.getParams(), authscope);
751:                    if (credentials == null) {
752:                        if (LOG.isInfoEnabled()) {
753:                            LOG
754:                                    .info("Failure authenticating with "
755:                                            + authscope);
756:                        }
757:                        return false;
758:                    } else {
759:                        return true;
760:                    }
761:                } else {
762:                    authstate.setAuthAttempted(true);
763:                    Credentials credentials = this .state
764:                            .getCredentials(authscope);
765:                    if (credentials == null) {
766:                        credentials = promptForCredentials(authscheme, method
767:                                .getParams(), authscope);
768:                    }
769:                    if (credentials == null) {
770:                        if (LOG.isInfoEnabled()) {
771:                            LOG.info("No credentials available for "
772:                                    + authscope);
773:                        }
774:                        return false;
775:                    } else {
776:                        return true;
777:                    }
778:                }
779:            }
780:
781:            private boolean processProxyAuthChallenge(final HttpMethod method)
782:                    throws MalformedChallengeException, AuthenticationException {
783:                AuthState authstate = method.getProxyAuthState();
784:                Map proxyChallenges = AuthChallengeParser
785:                        .parseChallenges(method
786:                                .getResponseHeaders(PROXY_AUTH_CHALLENGE));
787:                if (proxyChallenges.isEmpty()) {
788:                    LOG.debug("Proxy authentication challenge(s) not found");
789:                    return false;
790:                }
791:                AuthScheme authscheme = null;
792:                try {
793:                    authscheme = this .authProcessor.processChallenge(authstate,
794:                            proxyChallenges);
795:                } catch (AuthChallengeException e) {
796:                    if (LOG.isWarnEnabled()) {
797:                        LOG.warn(e.getMessage());
798:                    }
799:                }
800:                if (authscheme == null) {
801:                    return false;
802:                }
803:                AuthScope authscope = new AuthScope(conn.getProxyHost(), conn
804:                        .getProxyPort(), authscheme.getRealm(), authscheme
805:                        .getSchemeName());
806:
807:                if (LOG.isDebugEnabled()) {
808:                    LOG.debug("Proxy authentication scope: " + authscope);
809:                }
810:                if (authstate.isAuthAttempted() && authscheme.isComplete()) {
811:                    // Already tried and failed
812:                    Credentials credentials = promptForProxyCredentials(
813:                            authscheme, method.getParams(), authscope);
814:                    if (credentials == null) {
815:                        if (LOG.isInfoEnabled()) {
816:                            LOG
817:                                    .info("Failure authenticating with "
818:                                            + authscope);
819:                        }
820:                        return false;
821:                    } else {
822:                        return true;
823:                    }
824:                } else {
825:                    authstate.setAuthAttempted(true);
826:                    Credentials credentials = this .state
827:                            .getProxyCredentials(authscope);
828:                    if (credentials == null) {
829:                        credentials = promptForProxyCredentials(authscheme,
830:                                method.getParams(), authscope);
831:                    }
832:                    if (credentials == null) {
833:                        if (LOG.isInfoEnabled()) {
834:                            LOG.info("No credentials available for "
835:                                    + authscope);
836:                        }
837:                        return false;
838:                    } else {
839:                        return true;
840:                    }
841:                }
842:            }
843:
844:            /**
845:             * Tests if the {@link HttpMethod method} requires a redirect to another location.
846:             * 
847:             * @param method HTTP method
848:             * 
849:             * @return boolean <tt>true</tt> if a retry is needed, <tt>false</tt> otherwise.
850:             */
851:            private boolean isRedirectNeeded(final HttpMethod method) {
852:                switch (method.getStatusCode()) {
853:                case HttpStatus.SC_MOVED_TEMPORARILY:
854:                case HttpStatus.SC_MOVED_PERMANENTLY:
855:                case HttpStatus.SC_SEE_OTHER:
856:                case HttpStatus.SC_TEMPORARY_REDIRECT:
857:                    LOG.debug("Redirect required");
858:                    if (method.getFollowRedirects()) {
859:                        return true;
860:                    } else {
861:                        return false;
862:                    }
863:                default:
864:                    return false;
865:                } //end of switch
866:            }
867:
868:            /**
869:             * Tests if the {@link HttpMethod method} requires authentication.
870:             * 
871:             * @param method HTTP method
872:             * 
873:             * @return boolean <tt>true</tt> if a retry is needed, <tt>false</tt> otherwise.
874:             */
875:            private boolean isAuthenticationNeeded(final HttpMethod method) {
876:                method.getHostAuthState().setAuthRequested(
877:                        method.getStatusCode() == HttpStatus.SC_UNAUTHORIZED);
878:                method
879:                        .getProxyAuthState()
880:                        .setAuthRequested(
881:                                method.getStatusCode() == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED);
882:                if (method.getHostAuthState().isAuthRequested()
883:                        || method.getProxyAuthState().isAuthRequested()) {
884:                    LOG.debug("Authorization required");
885:                    if (method.getDoAuthentication()) { //process authentication response
886:                        return true;
887:                    } else { //let the client handle the authenticaiton
888:                        LOG
889:                                .info("Authentication requested but doAuthentication is "
890:                                        + "disabled");
891:                        return false;
892:                    }
893:                } else {
894:                    return false;
895:                }
896:            }
897:
898:            private Credentials promptForCredentials(
899:                    final AuthScheme authScheme, final HttpParams params,
900:                    final AuthScope authscope) {
901:                LOG.debug("Credentials required");
902:                Credentials creds = null;
903:                CredentialsProvider credProvider = (CredentialsProvider) params
904:                        .getParameter(CredentialsProvider.PROVIDER);
905:                if (credProvider != null) {
906:                    try {
907:                        creds = credProvider
908:                                .getCredentials(authScheme,
909:                                        authscope.getHost(), authscope
910:                                                .getPort(), false);
911:                    } catch (CredentialsNotAvailableException e) {
912:                        LOG.warn(e.getMessage());
913:                    }
914:                    if (creds != null) {
915:                        this .state.setCredentials(authscope, creds);
916:                        if (LOG.isDebugEnabled()) {
917:                            LOG.debug(authscope + " new credentials given");
918:                        }
919:                    }
920:                } else {
921:                    LOG.debug("Credentials provider not available");
922:                }
923:                return creds;
924:            }
925:
926:            private Credentials promptForProxyCredentials(
927:                    final AuthScheme authScheme, final HttpParams params,
928:                    final AuthScope authscope) {
929:                LOG.debug("Proxy credentials required");
930:                Credentials creds = null;
931:                CredentialsProvider credProvider = (CredentialsProvider) params
932:                        .getParameter(CredentialsProvider.PROVIDER);
933:                if (credProvider != null) {
934:                    try {
935:                        creds = credProvider.getCredentials(authScheme,
936:                                authscope.getHost(), authscope.getPort(), true);
937:                    } catch (CredentialsNotAvailableException e) {
938:                        LOG.warn(e.getMessage());
939:                    }
940:                    if (creds != null) {
941:                        this .state.setProxyCredentials(authscope, creds);
942:                        if (LOG.isDebugEnabled()) {
943:                            LOG.debug(authscope + " new credentials given");
944:                        }
945:                    }
946:                } else {
947:                    LOG.debug("Proxy credentials provider not available");
948:                }
949:                return creds;
950:            }
951:
952:            /**
953:             * @return
954:             */
955:            public HostConfiguration getHostConfiguration() {
956:                return hostConfiguration;
957:            }
958:
959:            /**
960:             * @return
961:             */
962:            public HttpState getState() {
963:                return state;
964:            }
965:
966:            /**
967:             * @return
968:             */
969:            public HttpConnectionManager getConnectionManager() {
970:                return connectionManager;
971:            }
972:
973:            /**
974:             * @return
975:             */
976:            public HttpParams getParams() {
977:                return this.params;
978:            }
979:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.