View Javadoc

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