KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > jmeter > protocol > http > control > CookieManager


1 // $Header: /home/cvs/jakarta-jmeter/src/protocol/http/org/apache/jmeter/protocol/http/control/CookieManager.java,v 1.26.2.3 2005/03/05 13:20:11 sebb Exp $
2
/*
3  * Copyright 2001-2004 The Apache Software Foundation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17 */

18
19 package org.apache.jmeter.protocol.http.control;
20
21 import java.io.BufferedReader;
22 import java.io.File;
23 import java.io.FileReader;
24 import java.io.FileWriter;
25 import java.io.IOException;
26 import java.io.PrintWriter;
27 import java.io.Serializable;
28 import java.net.URL;
29 import java.text.ParseException;
30 import java.text.SimpleDateFormat;
31 import java.util.ArrayList;
32 import java.util.Date;
33 import java.util.Enumeration;
34 import java.util.Locale;
35 import java.util.StringTokenizer;
36 import java.util.TimeZone;
37 import java.util.Vector;
38
39 import junit.framework.TestCase;
40
41 import org.apache.jmeter.config.ConfigTestElement;
42 import org.apache.jmeter.engine.event.LoopIterationEvent;
43 import org.apache.jmeter.protocol.http.sampler.HTTPSampler;
44 import org.apache.jmeter.testelement.TestListener;
45 import org.apache.jmeter.testelement.property.BooleanProperty;
46 import org.apache.jmeter.testelement.property.CollectionProperty;
47 import org.apache.jmeter.testelement.property.PropertyIterator;
48 import org.apache.jmeter.threads.JMeterContext;
49 import org.apache.jmeter.threads.JMeterContextService;
50 import org.apache.jmeter.util.JMeterUtils;
51 import org.apache.jorphan.logging.LoggingManager;
52 import org.apache.log.Logger;
53
54 /**
55  * This class provides an interface to the netscape cookies file to
56  * pass cookies along with a request.
57  *
58  * @author <a HREF="mailto:sdowd@arcmail.com">Sean Dowd</a>
59  * @version $Revision: 1.26.2.3 $ $Date: 2005/03/05 13:20:11 $
60  */

61 public class CookieManager
62     extends ConfigTestElement
63     implements TestListener, Serializable
64 {
65     transient private static Logger log = LoggingManager.getLoggerForClass();
66
67     public static final String CLEAR = "CookieManager.clearEachIteration";
68     public static final String COOKIES = "CookieManager.cookies";
69
70     // SimpleDateFormat isn't thread-safe
71
// TestElements are cloned for each thread, so
72
// we use an instance variable.
73
private SimpleDateFormat dateFormat =
74         new SimpleDateFormat("EEEE, dd-MMM-yy HH:mm:ss zzz",Locale.US);
75
76     // See bug 33796
77
private static final boolean DELETE_NULL_COOKIES =
78         JMeterUtils.getPropDefault("CookieManager.delete_null_cookies",true);// $NON-NLS-1$
79

80     public CookieManager()
81     {
82         // The cookie specification requires that the timezone be GMT.
83
// See http://developer.netscape.com/docs/manuals/communicator/jsguide4/cookies.htm
84
// See http://www.cookiecentral.com/faq/
85
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
86
87         setProperty(new CollectionProperty(COOKIES, new ArrayList()));
88         setProperty(new BooleanProperty(CLEAR, false));
89     }
90
91     public CollectionProperty getCookies()
92     {
93         return (CollectionProperty) getProperty(COOKIES);
94     }
95
96     public int getCookieCount()
97     {
98         return getCookies().size();
99     }
100
101     public boolean getClearEachIteration()
102     {
103         return getPropertyAsBoolean(CLEAR);
104     }
105
106     public void setClearEachIteration(boolean clear)
107     {
108         setProperty(new BooleanProperty(CLEAR, clear));
109     }
110
111     // Incorrect method. Always returns String. I changed CookiePanel code to
112
// perform this lookup.
113
//public Class getColumnClass(int column)
114
//{
115
// return columnNames[column].getClass();
116
//}
117

118     public Cookie getCookie(int row)
119     {
120         return (Cookie) getCookies().get(row);
121     }
122
123     /**
124      * Save the cookie data to a file.
125      */

126     public void save(String authFile) throws IOException
127     {
128         File file = new File(authFile);
129         if (!file.isAbsolute())
130             file =
131                 new File(
132                     System.getProperty("user.dir") + File.separator + authFile);
133         PrintWriter writer = new PrintWriter(new FileWriter(file));
134         writer.println("# JMeter generated Cookie file");
135         PropertyIterator cookies = getCookies().iterator();
136         while (cookies.hasNext())
137         {
138             Cookie cook = (Cookie) cookies.next().getObjectValue();
139             writer.println(cook.toString());
140         }
141         writer.flush();
142         writer.close();
143     }
144
145     /**
146      * Add cookie data from a file.
147      */

148     public void addFile(String cookieFile) throws IOException
149     {
150         File file = new File(cookieFile);
151         if (!file.isAbsolute())
152             file =
153                 new File(
154                     System.getProperty("user.dir")
155                         + File.separator
156                         + cookieFile);
157         BufferedReader reader = null;
158         if (file.canRead())
159         {
160             reader = new BufferedReader(new FileReader(file));
161         }
162         else
163         {
164             throw new IOException("The file you specified cannot be read.");
165         }
166
167         String line;
168         while ((line = reader.readLine()) != null)
169         {
170             try
171             {
172                 if (line.startsWith("#") || line.trim().length() == 0)
173                     continue;
174                 String[] st = split(line, "\t", " ");
175                 int domain = 0;
176                 int path = 2;
177                 if (st[path].equals(" "))
178                     st[path] = "/";
179                 boolean secure = Boolean.valueOf(st[3]).booleanValue();
180                 long expires = new Long(st[4]).longValue();
181                 int name = 5;
182                 int value = 6;
183                 Cookie cookie =
184                     new Cookie(
185                         st[name],
186                         st[value],
187                         st[domain],
188                         st[path],
189                         secure,
190                         expires);
191                 getCookies().addItem(cookie);
192             }
193             catch (Exception e)
194             {
195                 reader.close();
196                 throw new IOException(
197                     "Error parsing cookie line\n\t'" + line + "'\n\t" + e);
198             }
199         }
200         reader.close();
201     }
202
203     public void recoverRunningVersion()
204     {
205         //do nothing, the cookie manager has to accept changes.
206
}
207
208     public void setRunningVersion(boolean running)
209     {
210         //do nothing, the cookie manager has to accept changes.
211
}
212
213     /**
214      * Add a cookie.
215      */

216     public void add(Cookie c)
217     {
218         String cv = c.getValue();
219         if (DELETE_NULL_COOKIES && (null == cv || "".equals(cv))){
220             removeCookieNamed(c.getName());
221         } else {
222             JMeterContext context = getThreadContext();
223             getCookies().addItem(c);
224             if(context.isSamplingStarted())
225             {
226                 context.getVariables().put(c.getName(),c.getValue());
227             }
228         }
229     }
230
231 //NOT USED - did it make sense to create an empty cookie, anyway?
232
// /**
233
// * Add an empty cookie.
234
// */
235
// public void add()
236
// {
237
// getCookies().addItem(new Cookie());
238
// }
239

240     /**
241      * Remove all the cookies.
242      */

243     public void clear()
244     {
245         /* boolean clear = getClearEachIteration();
246                 super.clear();
247                 setClearEachIteration(clear);*/

248         setProperty(new CollectionProperty(COOKIES, new ArrayList()));
249     }
250
251     /**
252      * Remove a cookie.
253      */

254     public void remove(int index)
255     {
256         getCookies().remove(index);
257     }
258
259     /**
260      * Return the number of cookies.
261      */

262     public int size()
263     {
264         return getCookies().size();
265     }
266
267     /**
268      * Return the cookie at index i.
269      */

270     public Cookie get(int i)
271     {
272         return (Cookie) getCookies().get(i);
273     }
274
275     public String convertLongToDateFormatStr(long dateLong)
276     {
277         return dateFormat.format(new Date(dateLong));
278     }
279
280     /**
281      * Find cookies applicable to the given URL and build the Cookie header from
282      * them.
283      *
284      * @param url URL of the request to which the returned header will be added.
285      * @return the value string for the cookie header (goes after "Cookie: ").
286      */

287     public String getCookieHeaderForURL(URL url)
288     {
289         if (!url.getProtocol().toUpperCase().trim().equals("HTTP")
290             && !url.getProtocol().toUpperCase().trim().equals("HTTPS"))
291             return null;
292
293         StringBuffer header = new StringBuffer();
294         for (PropertyIterator enum = getCookies().iterator(); enum.hasNext();)
295         {
296             Cookie cookie = (Cookie) enum.next().getObjectValue();
297             // Add a leading dot to the host name so that host X matches
298
// domain .X. This is a breach of the standard, but it's how
299
// browsers behave:
300
String host= "."+url.getHost();
301             if (host.endsWith(cookie.getDomain())
302                 && url.getFile().startsWith(cookie.getPath())
303                 && ((cookie.getExpires()==0) // treat as never expiring (bug 27713)
304
||
305                     (System.currentTimeMillis() / 1000) <= cookie.getExpires()) )
306             {
307                 if (header.length() > 0)
308                 {
309                     header.append("; ");
310                 }
311                 header.append(cookie.getName()).append("=").append(
312                     cookie.getValue());
313             }
314         }
315
316         if (header.length() != 0)
317         {
318             return header.toString();
319         }
320         else
321         {
322             return null;
323         }
324     }
325
326     /**
327      * Parse the set-cookie header value and store the cookies for later
328      * retrieval.
329      *
330      * @param cookieHeader found after the "Set-Cookie: " in the response header
331      * @param url URL used in the request for the above-mentioned response.
332      */

333     public void addCookieFromHeader(String cookieHeader, URL url)
334     {
335         StringTokenizer st = new StringTokenizer(cookieHeader, ";");
336         String nvp;
337
338         // first n=v is name=value
339
nvp = st.nextToken();
340         int index = nvp.indexOf("=");
341         String name = nvp.substring(0, index);
342         String value = nvp.substring(index + 1);
343         String domain = "."+url.getHost(); // this is the default
344
// the leading dot breaks the standard, but helps in
345
// reproducing actual browser behaviour.
346
String path = "/"; // this is the default
347

348         Cookie newCookie =
349             new Cookie(
350                 name,
351                 value,
352                 domain,
353                 path,
354                 false,
355                 System.currentTimeMillis() + 1000 * 60 * 60 * 24);
356         // check the rest of the headers
357
while (st.hasMoreTokens())
358         {
359             nvp = st.nextToken();
360             nvp = nvp.trim();
361             index = nvp.indexOf("=");
362             if (index == -1)
363             {
364                 index = nvp.length();
365             }
366             String key = nvp.substring(0, index);
367             if (key.equalsIgnoreCase("expires"))
368             {
369                 try
370                 {
371                     String expires = nvp.substring(index + 1);
372                     Date date = dateFormat.parse(expires);
373                     //Always set expiry date - see Bugzilla id 29493
374
newCookie.setExpires(date.getTime());
375                 }
376                 catch (ParseException pe)
377                 {
378                     // This means the cookie did not come in the proper format.
379
// Log an error and don't set an expiration time:
380
log.error("Couldn't parse Cookie expiration time.", pe);
381                 }
382                 catch (Exception e)
383                 {
384                     // DateFormat.parse() has been known to throw various
385
// unchecked exceptions in the past, and does still do that
386
// occasionally at the time of this writing (1.4.2 JDKs).
387
// E.g. see http://developer.java.sun.com/developer/bugParade/bugs/4699765.html
388
//
389
// As a workaround for such issues we will catch all
390
// exceptions and react just as we did for ParseException
391
// above:
392
log.error(
393                         "Couln't parse Cookie expiration time: likely JDK bug.",
394                         e);
395                 }
396             }
397             else if (key.equalsIgnoreCase("domain"))
398             {
399                 //trim() is a workaround for bug in Oracle8iAS wherere
400
//cookies would have leading spaces in the domain portion
401
domain= nvp.substring(index + 1).trim();
402                 
403                 // The standard dictates domains must have a leading dot,
404
// but the new standard (Cookie2) tells us to add it if it's not
405
// there:
406
if (!domain.startsWith("."))
407                 {
408                     domain= "."+domain;
409                 }
410                 
411                 newCookie.setDomain(domain);
412             }
413             else if (key.equalsIgnoreCase("path"))
414             {
415                 newCookie.setPath(nvp.substring(index + 1));
416             }
417             else if (key.equalsIgnoreCase("secure"))
418             {
419                 newCookie.setSecure(true);
420             }
421         }
422
423         Vector removeIndices = new Vector();
424         for (int i = getCookies().size() - 1; i >= 0; i--)
425         {
426             Cookie cookie = (Cookie) getCookies().get(i).getObjectValue();
427             if (cookie == null)
428                 continue;
429             if (cookie.getPath().equals(newCookie.getPath())
430                 && cookie.getDomain().equals(newCookie.getDomain())
431                 && cookie.getName().equals(newCookie.getName()))
432             {
433                 removeIndices.addElement(new Integer(i));
434             }
435         }
436
437         for (Enumeration e = removeIndices.elements(); e.hasMoreElements();)
438         {
439             index = ((Integer) e.nextElement()).intValue();
440             remove(index);
441         }
442
443         if (newCookie.getExpires() >= System.currentTimeMillis())
444         {
445             add(newCookie);
446         }
447     }
448
449     public void removeCookieNamed(String name)
450     {
451         PropertyIterator iter = getCookies().iterator();
452         while (iter.hasNext())
453         {
454             Cookie cookie = (Cookie) iter.next().getObjectValue();
455             if (cookie.getName().equals(name))
456             {
457                 iter.remove();
458             }
459         }
460     }
461
462     /**
463      * Takes a String and a tokenizer character, and returns a new array of
464      * strings of the string split by the tokenizer character.
465      *
466      * @param splittee string to be split
467      * @param splitChar character to split the string on
468      * @param def default value to place between two split chars that have
469      * nothing between them
470      * @return array of all the tokens.
471      */

472     public String[] split(String splittee, String splitChar, String def)
473     {
474         if (splittee == null || splitChar == null)
475             return new String[0];
476         StringTokenizer tokens;
477         String temp;
478         int spot;
479         while ((spot = splittee.indexOf(splitChar + splitChar)) != -1)
480             splittee =
481                 splittee.substring(0, spot + splitChar.length())
482                     + def
483                     + splittee.substring(
484                         spot + 1 * splitChar.length(),
485                         splittee.length());
486         Vector returns = new Vector();
487         tokens = new StringTokenizer(splittee, splitChar);
488         while (tokens.hasMoreTokens())
489         {
490             temp = (String) tokens.nextToken();
491             returns.addElement(temp);
492         }
493         String[] values = new String[returns.size()];
494         returns.copyInto(values);
495         return values;
496     }
497
498     public String getClassLabel()
499     {
500         return JMeterUtils.getResString("cookie_manager_title");
501     }
502
503     public void testStarted()
504     {
505     }
506
507     public void testEnded()
508     {
509     }
510
511     public void testStarted(String host)
512     {
513     }
514
515     public void testEnded(String host)
516     {
517     }
518
519     public void testIterationStart(LoopIterationEvent event)
520     {
521         if (getClearEachIteration())
522             clear();
523     }
524
525     public static class Test extends TestCase
526     {
527         CookieManager man = null;
528         
529         public Test(String name)
530         {
531             super(name);
532         }
533         private JMeterContext jmctx = null;
534         
535         public void setUp()
536         {
537             jmctx = JMeterContextService.getContext();
538             man = new CookieManager();
539             man.setThreadContext(jmctx);
540         }
541
542
543         public void testRemoveCookie() throws Exception
544         {
545             man.setThreadContext(jmctx);
546             man.add(new Cookie("id", "me", "127.0.0.1", "/", false, 0));
547             man.removeCookieNamed("id");
548             assertEquals(0, man.getCookieCount());
549         }
550
551         public void testSendCookie() throws Exception
552         {
553             man.add(
554                 new Cookie(
555                     "id",
556                     "value",
557                     "jakarta.apache.org",
558                     "/",
559                     false,
560                     9999999999L));
561             HTTPSampler sampler = new HTTPSampler();
562             sampler.setDomain("jakarta.apache.org");
563             sampler.setPath("/index.html");
564             sampler.setMethod(HTTPSampler.GET);
565             assertNotNull(man.getCookieHeaderForURL(sampler.getUrl()));
566         }
567
568         public void testSendCookie2() throws Exception
569         {
570             man.add(
571                 new Cookie(
572                     "id",
573                     "value",
574                     ".apache.org",
575                     "/",
576                     false,
577                     9999999999L));
578             HTTPSampler sampler = new HTTPSampler();
579             sampler.setDomain("jakarta.apache.org");
580             sampler.setPath("/index.html");
581             sampler.setMethod(HTTPSampler.GET);
582             assertNotNull(man.getCookieHeaderForURL(sampler.getUrl()));
583         }
584         
585         /**
586          * Test that the cookie domain field is actually handled as
587          * browsers do (i.e.: host X matches domain .X):
588          */

589         public void testDomainHandling() throws Exception
590         {
591             URL url= new URL("http://jakarta.apache.org/");
592             man.addCookieFromHeader("test=1;domain=.jakarta.apache.org", url);
593             assertNotNull(man.getCookieHeaderForURL(url));
594         }
595         
596         /**
597          * Test that we won't be tricked by similar host names (this was a past
598          * bug, although it never got reported in the bug database):
599          */

600         public void testSimilarHostNames() throws Exception
601         {
602             URL url= new URL("http://ache.org/");
603             man.addCookieFromHeader("test=1", url);
604             url= new URL("http://jakarta.apache.org/");
605             assertNull(man.getCookieHeaderForURL(url));
606         }
607     }
608 }
Popular Tags