1 /*
2  * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/HttpState.java,v 1.38 2004/12/20 11:50:54 olegk Exp $
3  * $Revision: 561099 $
4  * $Date: 2007-07-30 21:41:17 +0200 (Mon, 30 Jul 2007) $
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: 561099 $ $Date: 2007-07-30 21:41:17 +0200 (Mon, 30 Jul 2007) $
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      */
HttpState()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      */
addCookie(Cookie cookie)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      */
addCookies(Cookie[] cookies)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      */
getCookies()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      */
getCookies( String domain, int port, String path, boolean secure )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      */
purgeExpiredCookies()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      */
purgeExpiredCookies(Date date)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 
getCookiePolicy()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 
setAuthenticationPreemptive(boolean value)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 
isAuthenticationPreemptive()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 
setCookiePolicy(int policy)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 
setCredentials(String realm, String host, Credentials credentials)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      */
setCredentials(final AuthScope authscope, final Credentials credentials)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      */
matchCredentials(final HashMap map, final AuthScope authscope)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 
getCredentials(String realm, String host)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      */
getCredentials(final AuthScope authscope)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      */
setProxyCredentials( String realm, String proxyHost, Credentials credentials )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      */
setProxyCredentials(final AuthScope authscope, final Credentials credentials)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      */
getProxyCredentials(String realm, String proxyHost)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      */
getProxyCredentials(final AuthScope authscope)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      */
toString()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      */
getCredentialsStringRepresentation(final Map credMap)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      */
getCookiesStringRepresentation(final List cookies)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      */
clearCredentials()600     public void clearCredentials() {
601         this.credMap.clear();
602     }
603 
604     /**
605      * Clears all proxy credentials.
606      */
clearProxyCredentials()607     public void clearProxyCredentials() {
608         this.proxyCred.clear();
609     }
610 
611     /**
612      * Clears all cookies.
613      */
clearCookies()614     public synchronized void clearCookies() {
615         this.cookies.clear();
616     }
617 
618     /**
619      * Clears the state information (all cookies, credentials and proxy credentials).
620      */
clear()621     public void clear() {
622         clearCookies();
623         clearCredentials();
624         clearProxyCredentials();
625     }
626 }
627