View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.transport.http;
8   
9   import org.mule.api.MuleEvent;
10  import org.mule.api.MuleMessage;
11  import org.mule.api.expression.ExpressionManager;
12  
13  import java.net.URI;
14  import java.net.URISyntaxException;
15  import java.text.SimpleDateFormat;
16  import java.util.ArrayList;
17  import java.util.Collection;
18  import java.util.Date;
19  import java.util.Iterator;
20  import java.util.List;
21  import java.util.Locale;
22  import java.util.Map;
23  import java.util.Map.Entry;
24  import java.util.TimeZone;
25  
26  import org.apache.commons.httpclient.Cookie;
27  import org.apache.commons.httpclient.Header;
28  import org.apache.commons.httpclient.HttpClient;
29  import org.apache.commons.httpclient.cookie.CookiePolicy;
30  import org.apache.commons.httpclient.cookie.CookieSpec;
31  import org.apache.commons.httpclient.cookie.MalformedCookieException;
32  import org.apache.commons.httpclient.cookie.NetscapeDraftSpec;
33  import org.apache.commons.httpclient.cookie.RFC2109Spec;
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  import org.apache.tomcat.util.http.Cookies;
37  import org.apache.tomcat.util.http.MimeHeaders;
38  import org.apache.tomcat.util.http.ServerCookie;
39  
40  /**
41   * <p>
42   * Helper functions for parsing, formatting, storing and retrieving cookie headers.
43   * </p>
44   * <p>
45   * It is important that all access to Cookie data is done using this class. This will
46   * help to prevent ClassCastExceptions and data corruption.
47   * </p>
48   * <p>
49   * The reasons for such a very complex CookieHelper class are historical and are
50   * related to the fact that cookies are a multivalued property and we store them as a
51   * single message property under the name
52   * {@linkplain HttpConnector#HTTP_COOKIES_PROPERTY "cookies"}.
53   * </p>
54   * <p>
55   * In an HTTP message going from client to server the cookies come on their own
56   * {@linkplain HttpConstants#HEADER_COOKIE "Cookie"} header. The HTTP message can
57   * have several of these Cookie headers and each of them can store 1 or more cookies.
58   * One problem with this is that in Mule we use {@link Map} instances to store the
59   * HTTP headers and this means that we can only have one object with the key
60   * {@linkplain HttpConnector#HTTP_COOKIES_PROPERTY "cookies"} (yes, we use that
61   * constant instead of {@linkplain HttpConstants#HEADER_COOKIE "Cookie"} when we
62   * store the cookies inside a {@link MuleMessage}).
63   * </p>
64   * <p>
65   * In an HTTP message going from server to client the Cookies go in their own
66   * {@linkplain HttpConstants#HEADER_COOKIE_SET "Set-Cookie"} header. But, again,
67   * internally we store all the HTTP headers inside a {@link Map} that maps each HTTP
68   * header with a single value. For Cookies it is a special case so have to be able to
69   * store many cookies in the value from that map.
70   * </p>
71   * <p>
72   * With all these layed out one could say that we could just have a
73   * {@link Collection} of {@link Cookie} instances. But this is not that simple. In
74   * some parts of the code the cookies are stored as an array of Cookies and in some
75   * others it is stored as a {@link Map} where each entry corresponds to a cookie's
76   * name/value pair (which is not strictly a cookie). Specifically, when parsing
77   * cookies from the client (ie, acting as a server), the code stores it as an array
78   * of cookies. When the cookies are specified as a property in the endpoint (like <a
79   * href=
80   * "http://www.mulesoft.org/documentation/display/MULE3USER/HTTP+Transport#HTTPTransport-Cookies"
81   * >explained in the docs</a>), they are stored as a {@link Map}.
82   * </p>
83   * <p>
84   * This class has helper methods that helps making code that is independent of the
85   * way the cookies are stored and still keep backward compatibility. It is very
86   * hacky, but I think it is better than what we had before.
87   * </p>
88   * <p>
89   * <b>Know Limitation:</b> because of how cookies are handled in Mule, we don't
90   * handle well the host, port and path of a Cookie. We just handle Cookies as if they
91   * were only name/value pairs. This means that, for example, if a message with
92   * cookies is received on an endpoint called http://localhost:4020/hello (1) and that
93   * message goes to http://www.mulesoft.org/jira/ (2), then service (2) will receive
94   * all the cookies that were sent to service (1) as if they were their own.
95   * Furthermore, the same thing will happend on the response: all the returned cookies
96   * from service (2) will reach service (1) and then the client will receive them as
97   * if they were from service (1).
98   * </p>
99   */
100 public class CookieHelper
101 {
102     /**
103      * This is used as the default {@link URI} for
104      * {@link #parseCookiesAsAClient(String, String, URI)} and overloading methods
105      * for when the {@link URI} supplied is null.
106      */
107     private static final String DEFAULT_URI_STRING = "http://localhost:80/";
108 
109     /**
110      * logger used by this class
111      */
112     protected static final Log logger = LogFactory.getLog(CookieHelper.class);
113 
114 	private static final String EXPIRE_PATTERN = "EEE, d-MMM-yyyy HH:mm:ss z";
115     private static final SimpleDateFormat EXPIRE_FORMATTER;
116 
117     static
118     {
119         EXPIRE_FORMATTER = new SimpleDateFormat(EXPIRE_PATTERN, Locale.US);
120         EXPIRE_FORMATTER.setTimeZone(TimeZone.getTimeZone("GMT"));
121     }
122 
123     /**
124      * Do not instantiate.
125      */
126     private CookieHelper()
127     {
128         // no op
129     }
130 
131     /**
132      * @return the {@link CookieSpec} (defaults to {@link RFC2109Spec} when spec is
133      *         null)
134      */
135     public static CookieSpec getCookieSpec(String spec)
136     {
137         if (spec != null && spec.equalsIgnoreCase(HttpConnector.COOKIE_SPEC_NETSCAPE))
138         {
139             return new NetscapeDraftSpec();
140         }
141         else
142         {
143             return new RFC2109Spec();
144         }
145     }
146 
147     /**
148      * @return the cookie policy (defaults to {@link CookiePolicy#RFC_2109} when spec
149      *         is null).
150      */
151     public static String getCookiePolicy(String spec)
152     {
153         if (spec != null && spec.equalsIgnoreCase(HttpConnector.COOKIE_SPEC_NETSCAPE))
154         {
155             return CookiePolicy.NETSCAPE;
156         }
157         else
158         {
159             return CookiePolicy.RFC_2109;
160         }
161     }
162 
163     /**
164      * @see #parseCookiesAsAClient(String, String, URI)
165      */
166     public static Cookie[] parseCookiesAsAClient(Header cookieHeader, String spec)
167         throws MalformedCookieException
168     {
169         return parseCookiesAsAClient(cookieHeader.getValue(), spec, null);
170     }
171 
172     /**
173      * @see #parseCookiesAsAClient(String, String, URI)
174      */
175     public static Cookie[] parseCookiesAsAClient(String cookieHeaderValue, String spec)
176         throws MalformedCookieException
177     {
178         return parseCookiesAsAClient(cookieHeaderValue, spec, null);
179     }
180 
181     /**
182      * @see #parseCookiesAsAClient(String, String, URI)
183      */
184     public static Cookie[] parseCookiesAsAClient(Header cookieHeader, String spec, URI uri)
185         throws MalformedCookieException
186     {
187         return parseCookiesAsAClient(cookieHeader.getValue(), spec, uri);
188     }
189 
190     /**
191      * This method parses the value of {@linkplain HttpConstants#HEADER_COOKIE_SET
192      * "Set-Cookie"} HTTP header, returning an array with all the {@link Cookie}s
193      * found. This method is intended to be used from the client side of the HTTP
194      * connection.
195      *
196      * @param cookieHeaderValue the value with the cookie/s to parse.
197      * @param spec the spec according to {@link #getCookieSpec(String)} (can be null)
198      * @param uri the uri information that will be use to complete Cookie information
199      *            (host, port and path). If null then the
200      *            <code>DEFAULT_URI_STRING</code> will be used.
201      */
202     public static Cookie[] parseCookiesAsAClient(String cookieHeaderValue, String spec, URI uri)
203         throws MalformedCookieException
204     {
205         if (uri == null)
206         {
207             try
208             {
209                 uri = new URI(DEFAULT_URI_STRING);
210             }
211             catch (URISyntaxException e)
212             {
213                 throw new RuntimeException("This should have not happened", e);
214             }
215         }
216         CookieSpec cookieSpec = getCookieSpec(spec);
217         boolean secure = uri.getScheme() != null && uri.getScheme().equalsIgnoreCase("https");
218         String host = uri.getHost();
219         int port = getPortFromURI(uri);
220         String path = uri.getPath();
221 
222         return cookieSpec.parse(host, port, path, secure, cookieHeaderValue);
223     }
224 
225     private static int getPortFromURI(URI uri) throws MalformedCookieException
226     {
227         int port = uri.getPort();
228         if (port < 0)
229         {
230             String scheme = uri.getScheme();
231             if (scheme.equalsIgnoreCase("https"))
232             {
233                 port = 443;
234             }
235             else if (scheme.equalsIgnoreCase("http"))
236             {
237                 port = 80;
238             }
239             else
240             {
241                 String message = String.format(
242                     "The uri (%1s) does not specify a port and no default is available for its scheme (%2s).",
243                     uri, scheme);
244                 throw new MalformedCookieException(message);
245             }
246         }
247         return port;
248     }
249 
250     /**
251      * This method parses the value of an HTTP
252      * {@linkplain HttpConstants#HEADER_COOKIE "Cookie"} header that comes from a
253      * client to a server. It returns all the Cookies present in the header.
254      *
255      * @param header the header from which the cookie will be parsed. Please not that
256      *            only the {@link Header#getValue() value} of this header will be
257      *            used. No validation will be made to make sure that the
258      *            {@linkplain Header#getName() headerName} is actually a
259      *            {@link HttpConstants#HEADER_COOKIE}.
260      */
261     public static Cookie[] parseCookiesAsAServer(Header header, URI uri)
262     {
263         return parseCookiesAsAServer(header.getValue(), uri);
264     }
265 
266     /**
267      * This method parses the value of an HTTP {@linkplain HttpConstants#HEADER_COOKIE
268      * "Cookie"} header that comes from a client to a server. It returns all the
269      * Cookies present in the header.
270      *
271      * @param headerValue the value of the header from which the cookie will be
272      *            parsed.
273      */
274     public static Cookie[] parseCookiesAsAServer(String headerValue, URI uri)
275     {
276         MimeHeaders mimeHeaders = new MimeHeaders();
277         mimeHeaders.addValue(HttpConstants.HEADER_COOKIE).setBytes(headerValue.getBytes(), 0,
278             headerValue.length());
279 
280         Cookies cs = new Cookies(mimeHeaders);
281         Cookie[] cookies = new Cookie[cs.getCookieCount()];
282         for (int i = 0; i < cs.getCookieCount(); i++)
283         {
284             ServerCookie serverCookie = cs.getCookie(i);
285             cookies[i] = transformServerCookieToClientCookie(serverCookie);
286             if (uri != null)
287             {
288                 cookies[i].setSecure(uri.getScheme() != null && uri.getScheme().equalsIgnoreCase("https"));
289                 cookies[i].setDomain(uri.getHost());
290                 cookies[i].setPath(uri.getPath());
291             }
292         }
293         return cookies;
294     }
295 
296     /**
297      * Transforms a {@link ServerCookie} (from Apache Tomcat) into a {@link Cookie}
298      * (from commons httpclient). Both types of Cookie hold the same data but the
299      * {@link ServerCookie} is the type that you get when parsing cookies as a
300      * Server.
301      */
302     protected static Cookie transformServerCookieToClientCookie(ServerCookie serverCookie)
303     {
304         Cookie clientCookie = new Cookie(serverCookie.getDomain().toString(), serverCookie.getName()
305             .toString(), serverCookie.getValue().toString(), serverCookie.getPath().toString(),
306             serverCookie.getMaxAge(), serverCookie.getSecure());
307         clientCookie.setComment(serverCookie.getComment().toString());
308         clientCookie.setVersion(serverCookie.getVersion());
309         return clientCookie;
310     }
311 
312     /**
313      * This method formats the cookie so it can be send from server to client in a
314      * {@linkplain HttpConstants#HEADER_COOKIE_SET "Set-Cookie"} header.
315      */
316     public static String formatCookieForASetCookieHeader(Cookie cookie)
317     {
318         StringBuffer sb = new StringBuffer();
319         ServerCookie.appendCookieValue(sb, cookie.getVersion(), cookie.getName(), cookie.getValue(),
320             cookie.getPath(), cookie.getDomain(), cookie.getComment(), -1, cookie.getSecure());
321 
322         Date expiryDate = cookie.getExpiryDate();
323         if (expiryDate != null)
324         {
325             sb.append("; Expires=");
326             sb.append(EXPIRE_FORMATTER.format(expiryDate));
327         }
328 
329         return sb.toString();
330     }
331 
332     /**
333      * Adds to the client all the cookies present in the cookiesObject.
334      *
335      * @param cookiesObject this must be either a {@link Map Map&lt;String,
336      *            String&gt;} or a {@link Cookie Cookie[]}. It can be null.
337      * @param event this one is used only if the cookies are stored in a {@link Map}
338      *            in order to resolve expressions with the {@link ExpressionManager}.
339      * @param destinationUri the host, port and path of this {@link URI} will be used
340      *            as the data of the cookies that are added.
341      */
342     public static void addCookiesToClient(HttpClient client,
343                                           Object cookiesObject,
344                                              String policy,
345                                              MuleEvent event,
346                                              URI destinationUri)
347     {
348         CookieStorageType.resolveCookieStorageType(cookiesObject).addCookiesToClient(client, cookiesObject,
349             policy, event, destinationUri);
350     }
351 
352     /**
353      * <p>
354      * This method merges a new Cookie (or override the previous one if it exists) to
355      * the preExistentCookies. The result (the old cookies with the new one added) is
356      * returned. If a cookie with the same name already exists, then it will be
357      * overridden.
358      * </p>
359      * <p>
360      * It is <b>important</b> that you use the returned value of this method because
361      * for some implementations of preExistentCookies it is not possible to add new
362      * Cookies (for example, on Cookie[]).
363      * </p>
364      *
365      * @param preExistentCookies this must be either a
366      *            <code>java.util.Map&lt;String, String&gt;</code> or a
367      *            <code>Cookie[]</code>. It can be null.
368      * @param cookieName the new cookie name to be added.
369      * @param cookieValue the new cookie value to be added.
370      */
371     public static Object putAndMergeCookie(Object preExistentCookies, String cookieName, String cookieValue)
372     {
373         return CookieStorageType.resolveCookieStorageType(preExistentCookies).putAndMergeCookie(
374             preExistentCookies, cookieName, cookieValue);
375     }
376 
377     /**
378      * <p>
379      * Merges all the Cookies in newCookiesArray with the preExistentCookies, adding
380      * the new ones and overwriting the existing ones (existing means same cookie
381      * name).
382      * </p>
383      * <p>
384      * It is <b>important</b> that you use the returned value of this method because
385      * for some implementations of preExistentCookies it is not possible to add new
386      * Cookies (for example, on Cookie[]).
387      * </p>
388      */
389     public static Object putAndMergeCookie(Object preExistentCookies, Cookie[] newCookiesArray)
390     {
391         return CookieStorageType.resolveCookieStorageType(preExistentCookies).putAndMergeCookie(
392             preExistentCookies, newCookiesArray);
393     }
394 
395     /**
396      * <p>
397      * Merges all the Cookies in newCookiesMap with the preExistentCookies, adding
398      * the new ones and overwriting the existing ones (existing means same cookie
399      * name).
400      * </p>
401      * <p>
402      * It is <b>important</b> that you use the returned value of this method because
403      * for some implementations of preExistentCookies it is not possible to add new
404      * Cookies (for example, on Cookie[]).
405      * </p>
406      */
407     public static Object putAndMergeCookie(Object preExistentCookies, Map<String, String> newCookiesMap)
408     {
409         return CookieStorageType.resolveCookieStorageType(preExistentCookies).putAndMergeCookie(
410             preExistentCookies, newCookiesMap);
411     }
412 
413     /**
414      * Searches and return the cookie with the cookieName in the cookiesObject. It
415      * returns <code>null</code> if the cookie is not present.
416      */
417     public static String getCookieValueFromCookies(Object cookiesObject, String cookieName)
418     {
419         return CookieStorageType.resolveCookieStorageType(cookiesObject).getCookieValueFromCookies(
420             cookiesObject, cookieName);
421     }
422 
423     /**
424      * Returns an array view of the cookiesObject.
425      */
426     public static Cookie[] asArrayOfCookies(Object cookiesObject)
427     {
428         return CookieStorageType.resolveCookieStorageType(cookiesObject).asArrayOfCookies(cookiesObject);
429     }
430 
431 }
432 
433 /**
434  * This enum type is here to distinguish and handle the two type of cookie storage
435  * that we have. The method
436  * {@link CookieStorageType#resolveCookieStorageType(Object)} allows you to select
437  * the appropriate {@link CookieStorageType} for the cookiesObject that you have.
438  */
439 enum CookieStorageType
440 {
441     /**
442      * <p>
443      * This corresponds to the storage of cookies as a Cookie[].
444      * </p>
445      * <p>
446      * All the parameters of type {@link Object} in the method of this object are
447      * assumed to be of type Cookie[] and won't be checked. They will be cast to
448      * Cookie[].
449      * </p>
450      */
451     ARRAY_OF_COOKIES
452     {
453         @Override
454         public Object putAndMergeCookie(Object preExistentCookies, String cookieName, String cookieValue)
455         {
456             final Cookie[] preExistentCookiesArray = (Cookie[]) preExistentCookies;
457 
458             final int sessionIndex = getCookieIndexFromCookiesArray(cookieName, preExistentCookiesArray);
459 
460             // domain, path, secure (https) and expiry are handled in method
461             // CookieHelper.addCookiesToClient()
462             final Cookie newSessionCookie = new Cookie(null, cookieName, cookieValue);
463             final Cookie[] mergedCookiesArray;
464             if (sessionIndex >= 0)
465             {
466                 preExistentCookiesArray[sessionIndex] = newSessionCookie;
467                 mergedCookiesArray = preExistentCookiesArray;
468             }
469             else
470             {
471                 Cookie[] newSessionCookieArray = new Cookie[]{newSessionCookie};
472                 mergedCookiesArray = concatenateCookies(preExistentCookiesArray, newSessionCookieArray);
473             }
474             return mergedCookiesArray;
475         }
476 
477         protected Cookie[] concatenateCookies(Cookie[] cookies1, Cookie[] cookies2)
478         {
479             if (cookies1 == null)
480             {
481                 return cookies2;
482             }
483             else if (cookies2 == null)
484             {
485                 return null;
486             }
487             else
488             {
489                 Cookie[] mergedCookies = new Cookie[cookies1.length + cookies2.length];
490                 System.arraycopy(cookies1, 0, mergedCookies, 0, cookies1.length);
491                 System.arraycopy(cookies2, 0, mergedCookies, cookies1.length, cookies2.length);
492                 return mergedCookies;
493             }
494         }
495 
496         protected int getCookieIndexFromCookiesArray(String cookieName, Cookie[] preExistentCookies)
497         {
498             if (preExistentCookies != null && cookieName != null)
499             {
500                 for (int i = 0; i < preExistentCookies.length; i++)
501                 {
502                     if (cookieName.equals(preExistentCookies[i].getName()))
503                     {
504                         return i;
505                     }
506                 }
507             }
508             return -1;
509         }
510 
511         @Override
512         public String getCookieValueFromCookies(Object cookiesObject, String cookieName)
513         {
514             Cookie[] cookies = (Cookie[]) cookiesObject;
515 
516             int sessionIndex = getCookieIndexFromCookiesArray(cookieName, cookies);
517             if (sessionIndex >= 0)
518             {
519                 return cookies[sessionIndex].getValue();
520             }
521             else
522             {
523                 return null;
524             }
525         }
526 
527         @Override
528         public void addCookiesToClient(HttpClient client,
529                                        Object cookiesObject,
530                                        String policy,
531                                        MuleEvent event,
532                                        URI destinationUri)
533         {
534             Cookie[] cookies = (Cookie[]) cookiesObject;
535 
536             if (cookies != null && cookies.length > 0)
537             {
538                 String host = destinationUri.getHost();
539                 String path = destinationUri.getPath();
540                 for (Cookie cookie : cookies)
541                 {
542                     client.getState().addCookie(
543                         new Cookie(host, cookie.getName(), cookie.getValue(), path, cookie.getExpiryDate(),
544                             cookie.getSecure()));
545                 }
546                 client.getParams().setCookiePolicy(CookieHelper.getCookiePolicy(policy));
547             }
548         }
549 
550         @Override
551         public Object putAndMergeCookie(Object preExistentCookies, Cookie[] newCookiesArray)
552         {
553             if (newCookiesArray == null)
554             {
555                 return preExistentCookies;
556             }
557             final List<Cookie> cookiesThatAreReallyNew = new ArrayList<Cookie>(newCookiesArray.length);
558             final Cookie[] preExistentCookiesArray = (Cookie[]) preExistentCookies;
559             for (Cookie newCookie : newCookiesArray)
560             {
561                 int newCookieInPreExistentArrayIndex = getCookieIndexFromCookiesArray(newCookie.getName(),
562                     preExistentCookiesArray);
563                 if (newCookieInPreExistentArrayIndex >= 0)
564                 {
565                     // overwrite the old one
566                     preExistentCookiesArray[newCookieInPreExistentArrayIndex] = newCookie;
567                 }
568                 else
569                 {
570                     // needs to add it at the end
571                     cookiesThatAreReallyNew.add(newCookie);
572                 }
573             }
574 
575             return concatenateCookies(preExistentCookiesArray,
576                 cookiesThatAreReallyNew.toArray(new Cookie[cookiesThatAreReallyNew.size()]));
577         }
578 
579         @Override
580         public Object putAndMergeCookie(Object preExistentCookies, Map<String, String> newCookiesMap)
581         {
582             if (newCookiesMap == null)
583             {
584                 return putAndMergeCookie(preExistentCookies, (Cookie[]) null);
585             }
586             else
587             {
588                 Cookie[] cookiesArray = new Cookie[newCookiesMap.size()];
589                 int i = 0;
590                 for (Entry<String, String> cookieEntry : newCookiesMap.entrySet())
591                 {
592                     Cookie cookie = new Cookie();
593                     cookie.setName(cookieEntry.getKey());
594                     cookie.setValue(cookieEntry.getValue());
595                     cookiesArray[i++] = cookie;
596                 }
597                 return putAndMergeCookie(preExistentCookies, cookiesArray);
598             }
599         }
600 
601         @Override
602         public Cookie[] asArrayOfCookies(Object cookiesObject)
603         {
604             if (cookiesObject == null)
605             {
606                 return ZERO_COOKIES;
607             }
608             else
609             {
610                 return (Cookie[]) cookiesObject;
611             }
612         }
613 
614     },
615 
616     /**
617      * <p>
618      * This corresponds to the storage of cookies as {@link Map<String, String>},
619      * where the keys are the cookie names and the values are the cookie values.
620      * </p>
621      * <p>
622      * All the parameters of type {@link Object} in the method of this object are
623      * assumed to be of type {@link Map<String, String>} and won't be checked. They
624      * will be cast to {@link Map} and used as if all the keys and values are of type
625      * {@link String}.
626      */
627     MAP_STRING_STRING
628     {
629         @Override
630         @SuppressWarnings("unchecked")
631         public Object putAndMergeCookie(Object preExistentCookies, String cookieName, String cookieValue)
632         {
633             final Map<String, String> cookieMap = (Map<String, String>) preExistentCookies;
634 
635             cookieMap.put(cookieName, cookieValue);
636             return cookieMap;
637         }
638 
639         @Override
640         @SuppressWarnings("unchecked")
641         public String getCookieValueFromCookies(Object cookiesObject, String cookieName)
642         {
643             return ((Map<String, String>) cookiesObject).get(cookieName);
644         }
645 
646         @Override
647         @SuppressWarnings("unchecked")
648         public void addCookiesToClient(HttpClient client,
649                                        Object cookiesObject,
650                                        String policy,
651                                        MuleEvent event,
652                                        URI destinationUri)
653         {
654             Map<String, String> cookieMap = (Map<String, String>) cookiesObject;
655 
656             client.getParams().setCookiePolicy(CookieHelper.getCookiePolicy(policy));
657 
658             String host = destinationUri.getHost();
659             String path = destinationUri.getPath();
660             Iterator<String> keyIter = cookieMap.keySet().iterator();
661             while (keyIter.hasNext())
662             {
663                 String key = keyIter.next();
664                 String cookieValue = cookieMap.get(key);
665 
666                 final String value;
667                 if (event != null)
668                 {
669                     value = event.getMuleContext()
670                         .getExpressionManager()
671                         .parse(cookieValue, event.getMessage());
672                 }
673                 else
674                 {
675                     value = cookieValue;
676                 }
677 
678                 Cookie cookie = new Cookie(host, key, value, path, null, false);
679                 client.getState().addCookie(cookie);
680             }
681 
682         }
683 
684         @Override
685         public Object putAndMergeCookie(Object preExistentCookies, Cookie[] newCookiesArray)
686         {
687             if (newCookiesArray == null)
688             {
689                 return preExistentCookies;
690             }
691             for (Cookie cookie : newCookiesArray)
692             {
693                 preExistentCookies = putAndMergeCookie(preExistentCookies, cookie.getName(),
694                     cookie.getValue());
695             }
696             return preExistentCookies;
697         }
698 
699         @Override
700         public Object putAndMergeCookie(Object preExistentCookies, Map<String, String> newCookiesMap)
701         {
702             if (newCookiesMap == null)
703             {
704                 return preExistentCookies;
705             }
706             for (Entry<String, String> cookieEntry : newCookiesMap.entrySet())
707             {
708                 preExistentCookies = putAndMergeCookie(preExistentCookies, cookieEntry.getKey(),
709                     cookieEntry.getValue());
710             }
711             return preExistentCookies;
712         }
713 
714         @Override
715         @SuppressWarnings("unchecked")
716         public Cookie[] asArrayOfCookies(Object cookiesObject)
717         {
718             Map<String, String> cookieMap = (Map<String, String>) cookiesObject;
719             Cookie[] arrayOfCookies = new Cookie[cookieMap.size()];
720             int i = 0;
721             for (Entry<String, String> cookieEntry : cookieMap.entrySet())
722             {
723                 Cookie cookie = new Cookie();
724                 cookie.setName(cookieEntry.getKey());
725                 cookie.setValue(cookieEntry.getValue());
726                 arrayOfCookies[i++] = cookie;
727             }
728             return arrayOfCookies;
729         }
730 
731     };
732 
733     private static final Cookie[] ZERO_COOKIES = new Cookie[0];
734 
735     /**
736      * Resolves the cookiesObject to the appropriate {@link CookieStorageType}.
737      *
738      * @param cookiesObject
739      * @return
740      */
741     public static CookieStorageType resolveCookieStorageType(Object cookiesObject)
742     {
743         if (cookiesObject == null || cookiesObject instanceof Cookie[])
744         {
745             return CookieStorageType.ARRAY_OF_COOKIES;
746         }
747         else if (cookiesObject instanceof Map)
748         {
749             return CookieStorageType.MAP_STRING_STRING;
750         }
751         else
752         {
753             throw new IllegalArgumentException("Invalid cookiesObject. Only " + Cookie.class + "[] and "
754                                                + Map.class + " are supported: " + cookiesObject);
755         }
756     }
757 
758     /**
759      * @see CookieHelper#putAndMergeCookie(Object, String, String)
760      */
761     public abstract Object putAndMergeCookie(Object preExistentCookies, String cookieName, String cookieValue);
762 
763     /**
764      * @see CookieHelper#putAndMergeCookie(Object, Cookie[])
765      */
766     public abstract Object putAndMergeCookie(Object preExistentCookies, Cookie[] newCookiesArray);
767 
768     /**
769      * @see CookieHelper#putAndMergeCookie(Object, Map)
770      */
771     public abstract Object putAndMergeCookie(Object preExistentCookies, Map<String, String> newCookiesMap);
772 
773     /**
774      * @see CookieHelper#getCookieValueFromCookies(Object, String)
775      */
776     public abstract String getCookieValueFromCookies(Object cookiesObject, String cookieName);
777 
778     /**
779      * @see CookieHelper#addCookiesToClient(HttpClient, Object, String, MuleEvent,
780      *      URI)
781      */
782     public abstract void addCookiesToClient(HttpClient client,
783                                             Object cookiesObject,
784                                             String policy,
785                                             MuleEvent event,
786                                             URI destinationUri);
787 
788     /**
789      * @see CookieHelper#asArrayOfCookies(Object)
790      */
791     public abstract Cookie[] asArrayOfCookies(Object cookiesObject);
792 }