View Javadoc

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