View Javadoc

1   /*
2    * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpState.java,v 1.22.2.5 2004/09/22 23:36:58 mbecke Exp $
3    * $Revision: 1.22.2.5 $
4    * $Date: 2004/09/22 23:36:58 $
5    *
6    * ====================================================================
7    *
8    *  Copyright 1999-2004 The Apache Software Foundation
9    *
10   *  Licensed under the Apache License, Version 2.0 (the "License");
11   *  you may not use this file except in compliance with the License.
12   *  You may obtain a copy of the License at
13   *
14   *      http://www.apache.org/licenses/LICENSE-2.0
15   *
16   *  Unless required by applicable law or agreed to in writing, software
17   *  distributed under the License is distributed on an "AS IS" BASIS,
18   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19   *  See the License for the specific language governing permissions and
20   *  limitations under the License.
21   * ====================================================================
22   *
23   * This software consists of voluntary contributions made by many
24   * individuals on behalf of the Apache Software Foundation.  For more
25   * information on the Apache Software Foundation, please see
26   * <http://www.apache.org/>.
27   *
28   * [Additional notices, if required by prior licensing conditions]
29   *
30   */
31  
32  package org.apache.commons.httpclient;
33  
34  import java.util.ArrayList;
35  import java.util.Date;
36  import java.util.HashMap;
37  import java.util.Map;
38  import java.util.List;
39  import java.util.Iterator;
40  import org.apache.commons.httpclient.cookie.CookieSpec;
41  import org.apache.commons.httpclient.cookie.CookiePolicy;
42  import org.apache.commons.httpclient.auth.HttpAuthRealm; 
43  import org.apache.commons.logging.Log;
44  import org.apache.commons.logging.LogFactory;
45  
46  /***
47   * <p>
48   * A container for HTTP attributes that may persist from request
49   * to request, such as {@link Cookie cookies} and authentication
50   * {@link Credentials credentials}.
51   * </p>
52   * <p>
53   * Preemptive authentication can be turned on by using the property value of
54   * #PREEMPTIVE_PROPERTY.  If left unspecified, it has the default value of
55   * #PREEMPTIVE_DEFAULT.  This configurable behaviour conforms to rcf2617:
56   * </p>
57   * 
58   * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
59   * @author Rodney Waldhoff
60   * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
61   * @author Sean C. Sullivan
62   * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
63   * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
64   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
65   * @author <a href="mailto:adrian@intencha.com">Adrian Sutton</a>
66   * 
67   * @version $Revision: 1.22.2.5 $ $Date: 2004/09/22 23:36:58 $
68   * 
69   */
70  public class HttpState {
71  
72      // ----------------------------------------------------- Instance Variables
73  
74      /***
75       * Whether authentication should be attempted preemptively.
76       */
77      private boolean preemptive;
78  
79      /***
80       * The boolean system property name to turn on preemptive authentication.
81       * @deprecated This field and feature will be removed following HttpClient 3.0.
82       */
83      public static final String PREEMPTIVE_PROPERTY = "httpclient.authentication.preemptive";
84  
85      /***
86       * The default value for {@link #PREEMPTIVE_PROPERTY}.
87       * @deprecated This field and feature will be removed following HttpClient 3.0.
88       */
89      public static final String PREEMPTIVE_DEFAULT = "false";
90  
91      /***
92       * Map of {@link Credentials credentials} by realm that this 
93       * HTTP state contains.
94       */
95      private HashMap credMap = new HashMap();
96  
97      /***
98       * Map of {@link Credentials proxy credentials} by realm that this
99       * HTTP state contains
100      */
101     private HashMap proxyCred = new HashMap();
102 
103     /***
104      * The default authentication realm.
105      */
106     public static final HttpAuthRealm DEFAULT_AUTH_REALM = new HttpAuthRealm(null, null); 
107 
108     /***
109      * Array of {@link Cookie cookies} that this HTTP state contains.
110      */
111     private ArrayList cookies = new ArrayList();
112     /***
113      * Cookie policy that applies to this HTTP state. Default is 
114      * {@link CookiePolicy#RFC2109}
115      */
116     private int cookiePolicy = CookiePolicy.RFC2109;
117 
118     /*** The current connection manager */
119     private HttpConnectionManager httpConnectionManager;
120 
121     // -------------------------------------------------------- Class Variables
122 
123     /*** Log object for this class. */
124     private static final Log LOG = LogFactory.getLog(HttpState.class);
125 
126     /***
127      * Default constructor.
128      */
129     public HttpState() {
130         
131         super();
132         
133         this.cookiePolicy = CookiePolicy.getDefaultPolicy();
134 
135         // check the preemptive policy
136         String preemptiveDefault = null;
137         try {
138             preemptiveDefault = System.getProperty(PREEMPTIVE_PROPERTY); 
139         } catch (SecurityException ignore) {
140         }
141         if (preemptiveDefault == null) {
142             preemptiveDefault = PREEMPTIVE_DEFAULT; 
143         }
144         preemptiveDefault = preemptiveDefault.trim().toLowerCase();
145 
146         if (!(preemptiveDefault.equals("true")
147                     || preemptiveDefault.equals("false"))) { // property problem
148             LOG.warn("Configuration property " + PREEMPTIVE_PROPERTY
149                      + " must be either true or false.  Using default: "
150                      + PREEMPTIVE_DEFAULT);
151             preemptiveDefault = PREEMPTIVE_DEFAULT;
152         }
153         this.preemptive = ("true".equals(preemptiveDefault));
154     }
155 
156     // ------------------------------------------------------------- Properties
157 
158     /***
159      * Adds an {@link Cookie HTTP cookie}, replacing any existing equivalent cookies.
160      * If the given cookie has already expired it will not be added, but existing 
161      * values will still be removed.
162      * 
163      * @param cookie the {@link Cookie cookie} to be added
164      * 
165      * @see #addCookies(Cookie[])
166      * 
167      */
168     public synchronized void addCookie(Cookie cookie) {
169         LOG.trace("enter HttpState.addCookie(Cookie)");
170 
171         if (cookie != null) {
172             // first remove any old cookie that is equivalent
173             for (Iterator it = cookies.iterator(); it.hasNext();) {
174                 Cookie tmp = (Cookie) it.next();
175                 if (cookie.equals(tmp)) {
176                     it.remove();
177                     break;
178                 }
179             }
180             if (!cookie.isExpired()) {
181                 cookies.add(cookie);
182             }
183         }
184     }
185 
186     /***
187      * Adds an array of {@link Cookie HTTP cookies}. Cookies are added individually and 
188      * in the given array order. If any of the given cookies has already expired it will 
189      * not be added, but existing values will still be removed.
190      * 
191      * @param cookies the {@link Cookie cookies} to be added
192      * 
193      * @see #addCookie(Cookie)
194      * 
195      */
196     public synchronized void addCookies(Cookie[] cookies) {
197         LOG.trace("enter HttpState.addCookies(Cookie[])");
198 
199         if (cookies != null) {
200             for (int i = 0; i < cookies.length; i++) {
201                 this.addCookie(cookies[i]);
202             }
203         }
204     }
205 
206     /***
207      * Returns an array of {@link Cookie cookies} that this HTTP
208      * state currently contains.
209      * 
210      * @return an array of {@link Cookie cookies}.
211      * 
212      * @see #getCookies(String, int, String, boolean, java.util.Date)
213      * 
214      */
215     public synchronized Cookie[] getCookies() {
216         LOG.trace("enter HttpState.getCookies()");
217         return (Cookie[]) (cookies.toArray(new Cookie[cookies.size()]));
218     }
219 
220     /***
221      * Returns an array of {@link Cookie cookies} in this HTTP 
222      * state that match the given request parameters.
223      * 
224      * @param domain the request domain
225      * @param port the request port
226      * @param path the request path
227      * @param secure <code>true</code> when using HTTPS
228      * @param now the {@link Date date} by which expiration is determined
229      * 
230      * @return an array of {@link Cookie cookies}.
231      * 
232      * @see #getCookies()
233      * 
234      * @deprecated use CookieSpec#match(String, int, String, boolean, Cookie)
235      */
236     public synchronized Cookie[] getCookies(
237         String domain, 
238         int port, 
239         String path, 
240         boolean secure, 
241         Date now
242     ) {
243         return getCookies(domain, port, path, secure);
244     }
245 
246 
247     /***
248      * Returns an array of {@link Cookie cookies} in this HTTP
249      * state that match the given request parameters.
250      * 
251      * @param domain the request domain
252      * @param port the request port
253      * @param path the request path
254      * @param secure <code>true</code> when using HTTPS
255      * 
256      * @return an array of {@link Cookie cookies}.
257      * 
258      * @see #getCookies()
259      * 
260      * @deprecated use {@link CookieSpec#match(String, int, String, boolean, Cookie)}
261      */
262     public synchronized Cookie[] getCookies(
263         String domain, 
264         int port, 
265         String path, 
266         boolean secure
267     ) {
268         LOG.trace("enter HttpState.getCookies(String, int, String, boolean)");
269 
270         CookieSpec matcher = CookiePolicy.getDefaultSpec();
271         ArrayList list = new ArrayList(cookies.size());
272         for (int i = 0, m = cookies.size(); i < m; i++) {
273             Cookie cookie = (Cookie) (cookies.get(i));
274             if (matcher.match(domain, port, path, secure, cookie)) {
275                 list.add(cookie);
276             }
277         }
278         return (Cookie[]) (list.toArray(new Cookie[list.size()]));
279     }
280 
281     /***
282      * Removes all of {@link Cookie cookies} in this HTTP state
283      * that have expired according to the current system time.
284      * 
285      * @see #purgeExpiredCookies(java.util.Date)
286      * 
287      */
288     public synchronized boolean purgeExpiredCookies() {
289         LOG.trace("enter HttpState.purgeExpiredCookies()");
290         return purgeExpiredCookies(new Date());
291     }
292 
293     /***
294      * Removes all of {@link Cookie cookies} in this HTTP state
295      * that have expired by the specified {@link java.util.Date date}. 
296      * 
297      * @param date The {@link java.util.Date date} to compare against.
298      * 
299      * @return true if any cookies were purged.
300      * 
301      * @see Cookie#isExpired(java.util.Date)
302      * 
303      * @see #purgeExpiredCookies()
304      */
305     public synchronized boolean purgeExpiredCookies(Date date) {
306         LOG.trace("enter HttpState.purgeExpiredCookies(Date)");
307         boolean removed = false;
308         Iterator it = cookies.iterator();
309         while (it.hasNext()) {
310             if (((Cookie) (it.next())).isExpired(date)) {
311                 it.remove();
312                 removed = true;
313             }
314         }
315         return removed;
316     }
317 
318 
319     /***
320      * Returns the current {@link CookiePolicy cookie policy} for this
321      * HTTP state.
322      * 
323      * @return The {@link CookiePolicy cookie policy}.
324      */
325     
326     public int getCookiePolicy() {
327         return this.cookiePolicy;
328     }
329     
330 
331     /***
332      * Defines whether preemptive authentication should be 
333      * attempted.
334      * 
335      * @param value <tt>true</tt> if preemptive authentication should be 
336      * attempted, <tt>false</tt> otherwise. 
337      */
338     
339     public void setAuthenticationPreemptive(boolean value) {
340         this.preemptive = value;
341     }
342 
343 
344     /***
345      * Returns <tt>true</tt> if preemptive authentication should be 
346      * attempted, <tt>false</tt> otherwise.
347      * 
348      * @return boolean flag.
349      */
350     
351     public boolean isAuthenticationPreemptive() {
352         return this.preemptive;
353     }
354     
355 
356     /***
357      * Sets the current {@link CookiePolicy cookie policy} for this HTTP
358      * state to one of the following supported policies: 
359      * {@link CookiePolicy#COMPATIBILITY}, 
360      * {@link CookiePolicy#NETSCAPE_DRAFT} or
361      * {@link CookiePolicy#RFC2109}.
362      * 
363      * @param policy new {@link CookiePolicy cookie policy}
364      */
365     
366     public void setCookiePolicy(int policy) {
367         this.cookiePolicy = policy;
368     }
369 
370     /***
371      * Sets the {@link Credentials credentials} for the given authentication 
372      * realm. The <code>null</code> realm signifies default credentials, which 
373      * should be used when no {@link Credentials credentials} have been explictly 
374      * supplied for the given challenging realm. Any previous credentials for
375      * the given realm will be overwritten.
376      *
377      * @deprecated This method does not distinguish between realms with the
378      * same name on different hosts.  
379      * Use {@link #setCredentials(String, String, Credentials)} instead.
380      * 
381      * @param realm the authentication realm
382      * @param credentials the authentication credentials for the given realm
383      * 
384      * @see #getCredentials(String, String)
385      * @see #setProxyCredentials(String, String, Credentials)
386      */
387     
388     public synchronized void setCredentials(String realm, Credentials credentials) {
389         LOG.trace("enter HttpState.setCredentials(String, Credentials)");
390         setCredentials(realm, null, credentials);
391     }
392     
393     /*** 
394      * Sets the {@link Credentials credentials} for the given authentication 
395      * realm on the given host. The <code>null</code> realm signifies default 
396      * credentials for the given host, which should be used when no 
397      * {@link Credentials credentials} have been explictly supplied for the 
398      * challenging realm. The <code>null</code> host signifies default 
399      * credentials, which should be used when no {@link Credentials credentials} 
400      * have been explictly supplied for the challenging host. Any previous 
401      * credentials for the given realm on the given host will be overwritten.
402      * 
403      * @param realm the authentication realm
404      * @param host the host the realm belongs to
405      * @param credentials the authentication {@link Credentials credentials} 
406      * for the given realm.
407      * 
408      * @see #getCredentials(String, String)
409      * @see #setProxyCredentials(String, String, Credentials) 
410      */
411     
412     public synchronized void setCredentials(String realm, String host, Credentials credentials) {
413         LOG.trace(
414             "enter HttpState.setCredentials(String realm, String host, Credentials credentials)");
415         credMap.put(new HttpAuthRealm(host, realm), credentials);
416     }
417 
418 
419     /***
420      * Find matching {@link Credentials credentials} for the given authentication realm and host.
421      *
422      * If the <i>realm</i> exists on <i>host</i>, return the coresponding credentials.
423      * If the <i>host</i> exists with a <tt>null</tt> <i>realm</i>, return the corresponding
424      * credentials.
425      * If the <i>realm</i> exists with a <tt>null</tt> <i>host</i>, return the
426      * corresponding credentials.  If the <i>realm</i> does not exist, return
427      * the default Credentials.  If there are no default credentials, return
428      * <code>null</code>.
429      *
430      * @param map the credentials hash map
431      * @param realm the authentication realm
432      * @param host the host the realm is on
433      * @return the credentials 
434      * 
435      */
436     private static Credentials matchCredentials(HashMap map, String realm, String host) {
437         HttpAuthRealm entry = new HttpAuthRealm(host, realm);
438         Credentials creds = (Credentials) map.get(entry);
439         if (creds == null && host != null && realm != null) {
440             entry = new HttpAuthRealm(host, null);
441             creds = (Credentials) map.get(entry);
442             if (creds == null) {
443                 entry = new HttpAuthRealm(null, realm);
444                 creds = (Credentials) map.get(entry);
445             }
446         }
447         if (creds == null) {
448             creds = (Credentials) map.get(DEFAULT_AUTH_REALM);
449         }
450         return creds;
451     }
452     
453     /***
454      * Get the {@link Credentials credentials} for the given authentication realm on the 
455      * given host.
456      *
457      * If the <i>realm</i> exists on <i>host</i>, return the coresponding credentials.
458      * If the <i>host</i> exists with a <tt>null</tt> <i>realm</i>, return the corresponding
459      * credentials.
460      * If the <i>realm</i> exists with a <tt>null</tt> <i>host</i>, return the
461      * corresponding credentials.  If the <i>realm</i> does not exist, return
462      * the default Credentials.  If there are no default credentials, return
463      * <code>null</code>.
464      *
465      * @param realm the authentication realm
466      * @param host the host the realm is on
467      * @return the credentials 
468      * 
469      * @see #setCredentials(String, String, Credentials)
470      */
471     
472     public synchronized Credentials getCredentials(String realm, String host) {
473         LOG.trace("enter HttpState.getCredentials(String, String");
474         return matchCredentials(this.credMap, realm, host);
475     }
476 
477     /***
478      * Get the {@link Credentials credentials} for the given authentication realm.
479      *
480      * @deprecated This method does not distinguish between realms on different
481      * servers with the same name.  Use {@link #getCredentials(String, String)}
482      * instead.
483      * 
484      * @param realm the authentication realm
485      * @return the credentials 
486      * 
487      * @see #setCredentials(String, String, Credentials)
488      * 
489      */
490     
491     public synchronized Credentials getCredentials(String realm) {
492         LOG.trace("enter HttpState.getCredentials(String)");
493 
494         return getCredentials(realm, null);
495     }
496 
497     /***
498      * Sets the {@link Credentials credentials} for the given proxy authentication 
499      * realm. The <code>null</code> realm signifies default credentials, which 
500      * should be used when no {@link Credentials credentials} have been explictly 
501      * supplied for the given challenging proxy realm. Any previous credentials for
502      * the given realm will be overwritten.
503      * 
504      * @deprecated This method does not differentiate between realms with
505      * the same name on different servers.  Use
506      * {@link #setProxyCredentials(String, String, Credentials)} instead.
507      * 
508      * @param realm the authentication realm
509      * @param credentials the authentication credentials for the given realm
510      * 
511      * @see #getProxyCredentials(String)
512      * @see #setCredentials(String, Credentials)
513      * 
514      */
515     
516     public synchronized void setProxyCredentials(String realm, Credentials credentials) {
517         LOG.trace("enter HttpState.setProxyCredentials(String, credentials)");
518         setProxyCredentials(realm, null, credentials);
519     }
520     
521     /***
522      * Sets the {@link Credentials credentials} for the given proxy authentication 
523      * realm on the given proxy host. The <code>null</code> proxy realm signifies 
524      * default credentials for the given proxy host, which should be used when no 
525      * {@link Credentials credentials} have been explictly supplied for the 
526      * challenging proxy realm. The <code>null</code> proxy host signifies default 
527      * credentials, which should be used when no {@link Credentials credentials} 
528      * have been explictly supplied for the challenging proxy host. Any previous 
529      * credentials for the given proxy realm on the given proxy host will be 
530      * overwritten.
531      * 
532      * @param realm the authentication realm
533      * @param proxyHost the proxy host
534      * @param credentials the authentication credentials for the given realm
535      * 
536      * @see #getProxyCredentials(String)
537      * @see #setCredentials(String, Credentials)
538      * 
539      */
540     public synchronized void setProxyCredentials(
541         String realm, 
542         String proxyHost, 
543         Credentials credentials
544     ) {
545         LOG.trace("enter HttpState.setProxyCredentials(String, String, Credentials");
546         proxyCred.put(new HttpAuthRealm(proxyHost, realm), credentials);
547     }
548 
549     /***
550      * Get the {@link Credentials credentials} for the given 
551      * proxy authentication realm.
552      *
553      * If the <i>realm</i> exists, return the coresponding credentials.  If the 
554      * <i>realm</i> does not exist, return the default Credentials.  If there is 
555      * no default credentials, return <code>null</code>.
556      * 
557      * @deprecated This method does not distinguish between realms on different hosts.
558      * Use {@link #getProxyCredentials(String, String)} instead.
559      *
560      * @param realm the authentication realm
561      * @return the credentials 
562      * @see #setProxyCredentials(String, String, Credentials)
563      */
564     
565     public synchronized Credentials getProxyCredentials(String realm) {
566         LOG.trace("enter HttpState.getProxyCredentials(String)");
567         return getProxyCredentials(realm, null);
568     }
569     
570     /***
571      * Get the {@link Credentials credentials} for the proxy host with the given 
572      * authentication realm.
573      *
574      * If the <i>realm</i> exists on <i>host</i>, return the coresponding credentials.
575      * If the <i>host</i> exists with a <tt>null</tt> <i>realm</i>, return the corresponding
576      * credentials.
577      * If the <i>realm</i> exists with a <tt>null</tt> <i>host</i>, return the
578      * corresponding credentials.  If the <i>realm</i> does not exist, return
579      * the default Credentials.  If there are no default credentials, return
580      * <code>null</code>.
581      * 
582      * @param realm the authentication realm
583      * @param proxyHost the proxy host the realm is on
584      * @return the credentials 
585      * @see #setProxyCredentials(String, String, Credentials)
586      */
587     public synchronized Credentials getProxyCredentials(String realm, String proxyHost) {
588        LOG.trace("enter HttpState.getCredentials(String, String");
589         return matchCredentials(this.proxyCred, realm, proxyHost);
590     }
591     
592     /***
593      * Returns a string representation of this HTTP state.
594      * 
595      * @return The string representation of the HTTP state.
596      * 
597      * @see java.lang.Object#toString()
598      */
599     public synchronized String toString() {
600         StringBuffer sbResult = new StringBuffer();
601 
602         sbResult.append("[");
603         sbResult.append(getProxyCredentialsStringRepresentation(proxyCred));
604         sbResult.append(" | ");
605         sbResult.append(getCredentialsStringRepresentation(proxyCred));
606         sbResult.append(" | ");
607         sbResult.append(getCookiesStringRepresentation(cookies));
608         sbResult.append("]");
609 
610         String strResult = sbResult.toString();
611 
612         return strResult;
613     }
614     
615     /***
616      * Returns a string representation of the proxy credentials
617      * @param proxyCredMap The proxy credentials
618      * @return The string representation.
619      */
620     private static String getProxyCredentialsStringRepresentation(final Map proxyCredMap) {
621         StringBuffer sbResult = new StringBuffer();
622         Iterator iter = proxyCredMap.keySet().iterator();
623         while (iter.hasNext()) {
624             Object key = iter.next();
625             Credentials cred = (Credentials) proxyCredMap.get(key);
626             if (sbResult.length() > 0) {
627                 sbResult.append(", ");
628             }
629             sbResult.append(key);
630             sbResult.append("#");
631             sbResult.append(cred.toString());
632         }
633         return sbResult.toString();
634     }
635     
636     /***
637      * Returns a string representation of the credentials.
638      * @param credMap The credentials.
639      * @return The string representation.
640      */
641     private static String getCredentialsStringRepresentation(final Map credMap) {
642         StringBuffer sbResult = new StringBuffer();
643         Iterator iter = credMap.keySet().iterator();
644         while (iter.hasNext()) {
645             Object key = iter.next();
646             Credentials cred = (Credentials) credMap.get(key);
647             if (sbResult.length() > 0) {
648                 sbResult.append(", ");
649             }
650             sbResult.append(key);
651             sbResult.append("#");
652             sbResult.append(cred.toString());
653         }
654         return sbResult.toString();
655     }
656     
657     /***
658      * Return a string representation of the cookies.
659      * @param cookies The cookies
660      * @return The string representation.
661      */
662     private static String getCookiesStringRepresentation(final List cookies) {
663         StringBuffer sbResult = new StringBuffer();
664         Iterator iter = cookies.iterator();
665         while (iter.hasNext()) {
666             Cookie ck = (Cookie) iter.next();
667             if (sbResult.length() > 0) {
668                 sbResult.append("#");
669             }
670             sbResult.append(ck.toExternalForm());
671         }
672         return sbResult.toString();
673     }
674     
675     /***
676      * Returns the httpConnectionManager.
677      * @return HttpConnectionManager
678      * 
679      * @deprecated Connection manager is controlled by the HttpClient class.
680      * Use {@link HttpClient#getHttpConnectionManager()} instead.
681      * 
682      * @since 2.0
683      */
684     public synchronized HttpConnectionManager getHttpConnectionManager() {
685         return httpConnectionManager;
686     }
687 
688     /***
689      * Sets the httpConnectionManager.
690      * @param httpConnectionManager The httpConnectionManager to set
691      * 
692      * @deprecated Connection manager is controlled by the HttpClient class.
693      * Use {@link HttpClient#setHttpConnectionManager(HttpConnectionManager)} instead.
694      *
695      * @since 2.0
696      */
697     public synchronized void setHttpConnectionManager(
698         HttpConnectionManager httpConnectionManager
699     ) {
700         this.httpConnectionManager = httpConnectionManager;
701     }
702 }