1 /*
2 * ========================================================================
3 *
4 * Licensed to the Apache Software Foundation (ASF) under one or more
5 * contributor license agreements. See the NOTICE file distributed with
6 * this work for additional information regarding copyright ownership.
7 * The ASF licenses this file to You under the Apache License, Version 2.0
8 * (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 * ========================================================================
20 */
21 package org.apache.cactus.internal.util;
22
23 import java.net.URL;
24 import java.util.Vector;
25
26 import org.apache.cactus.Cookie;
27 import org.apache.cactus.ServletURL;
28 import org.apache.cactus.WebRequest;
29 import org.apache.cactus.internal.client.ClientException;
30 import org.apache.commons.httpclient.Header;
31 import org.apache.commons.httpclient.cookie.CookiePolicy;
32 import org.apache.commons.httpclient.cookie.CookieSpec;
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35
36 /**
37 * Utility methods to manipulate cookies and transform Cactus cookie objects
38 * to HttpClient cookie objects.
39 *
40 * @version $Id: CookieUtil.java 238991 2004-05-22 11:34:50Z vmassol $
41 * @since 1.5
42 */
43 public class CookieUtil
44 {
45 /**
46 * The logger.
47 */
48 private static final Log LOGGER = LogFactory.getLog(CookieUtil.class);
49
50 /**
51 * Returns the domain that will be used to send the cookies. If a host
52 * was specified using <code>setURL()</code> then the domain will be
53 * this host. Otherwise it will be the real redirector host.
54 *
55 * @param theRequest the request containing all data to pass to the server
56 * redirector.
57 * @param theRealHost the real host to which we are connecting to. We will
58 * use it if no simulation host has been specified.
59 * @return the cookie domain to use
60 */
61 public static String getCookieDomain(WebRequest theRequest,
62 String theRealHost)
63 {
64 String domain;
65 ServletURL url = theRequest.getURL();
66
67 if ((url != null) && (url.getHost() != null))
68 {
69 domain = url.getHost();
70 }
71 else
72 {
73 domain = theRealHost;
74 }
75
76 LOGGER.debug("Cookie validation domain = [" + domain + "]");
77
78 return domain;
79 }
80
81 /**
82 * Returns the port that will be used to send the cookies. If a port
83 * was specified using <code>setURL()</code> then the port sent will be
84 * this port. Otherwise it will be the real redirector port.
85 *
86 * @param theRequest the request containing all data to pass to the server
87 * redirector.
88 * @param theRealPort the real port to which we are connecting to. We will
89 * use it if no simulation port has been specified.
90 * @return the cookie domain to use
91 */
92 public static int getCookiePort(WebRequest theRequest, int theRealPort)
93 {
94 int port;
95 ServletURL url = theRequest.getURL();
96
97 if ((url != null) && (url.getHost() != null))
98 {
99 port = url.getPort();
100 }
101 else
102 {
103 port = theRealPort;
104 }
105
106 LOGGER.debug("Cookie validation port = [" + port + "]");
107
108 return port;
109 }
110
111 /**
112 * Returns the path that will be used to validate if a cookie will be
113 * sent or not. The algorithm is as follows : if the cookie path is not
114 * set (i.e. null) then the cookie is always sent (provided the domain
115 * is right). If the cookie path is set, the cookie is sent only if
116 * the request path starts with the same string as the cookie path. If
117 * <code>setURL()</code> has been called, return the path it has been
118 * set to (context + servletPath + pathInfo). Otherwise return the
119 * real redirector path.
120 *
121 * @param theRequest the request containing all data to pass to the server
122 * redirector.
123 * @param theRealPath the real path to which we are connecting to. We will
124 * use it if no simulation path has been specified.
125 * @return the path to use to decide if a cookie will get sent
126 */
127 public static String getCookiePath(WebRequest theRequest,
128 String theRealPath)
129 {
130 String path;
131 ServletURL url = theRequest.getURL();
132
133 if ((url != null) && (url.getPath() != null))
134 {
135 path = url.getPath();
136 }
137 else
138 {
139 String file = theRealPath;
140
141 if (file != null)
142 {
143 int q = file.lastIndexOf('?');
144
145 if (q != -1)
146 {
147 path = file.substring(0, q);
148 }
149 else
150 {
151 path = file;
152 }
153 }
154 else
155 {
156 path = null;
157 }
158 }
159
160 LOGGER.debug("Cookie validation path = [" + path + "]");
161
162 return path;
163 }
164
165 /**
166 * Create a Commons-HttpClient cookie from a Cactus cookie, with information
167 * from the web request and the URL.
168 *
169 * @param theRequest The request
170 * @param theUrl The URL
171 * @param theCactusCookie The Cactus Cookie object
172 * @return The HttpClient cookie
173 */
174 public static org.apache.commons.httpclient.Cookie createHttpClientCookie(
175 WebRequest theRequest, URL theUrl, Cookie theCactusCookie)
176 {
177 // If no domain has been specified, use a default one
178 String domain;
179 if (theCactusCookie.getDomain() == null)
180 {
181 domain = CookieUtil.getCookieDomain(theRequest, theUrl.getHost());
182 }
183 else
184 {
185 domain = theCactusCookie.getDomain();
186 }
187
188 // If not path has been specified , use a default one
189 String path;
190 if (theCactusCookie.getPath() == null)
191 {
192 path = CookieUtil.getCookiePath(theRequest, theUrl.getFile());
193 }
194 else
195 {
196 path = theCactusCookie.getPath();
197 }
198
199 // Assemble the HttpClient cookie
200 org.apache.commons.httpclient.Cookie httpclientCookie =
201 new org.apache.commons.httpclient.Cookie(domain,
202 theCactusCookie.getName(), theCactusCookie.getValue());
203 httpclientCookie.setComment(theCactusCookie.getComment());
204 httpclientCookie.setExpiryDate(
205 theCactusCookie.getExpiryDate());
206 httpclientCookie.setPath(path);
207 httpclientCookie.setSecure(theCactusCookie.isSecure());
208
209 return httpclientCookie;
210 }
211
212 /**
213 * Transforms an array of Cactus cookies into an array of Commons-HttpClient
214 * cookies, using information from the request and URL.
215 *
216 * @param theRequest The request
217 * @param theUrl The URL
218 * @return The array of HttpClient cookies
219 */
220 public static org.apache.commons.httpclient.Cookie[]
221 createHttpClientCookies(WebRequest theRequest, URL theUrl)
222 {
223 Vector cactusCookies = theRequest.getCookies();
224
225 // transform the Cactus cookies into HttpClient cookies
226 org.apache.commons.httpclient.Cookie[] httpclientCookies =
227 new org.apache.commons.httpclient.Cookie[cactusCookies.size()];
228
229 for (int i = 0; i < cactusCookies.size(); i++)
230 {
231 Cookie cactusCookie = (Cookie) cactusCookies.elementAt(i);
232 httpclientCookies[i] = CookieUtil.createHttpClientCookie(
233 theRequest, theUrl, cactusCookie);
234 }
235
236 return httpclientCookies;
237 }
238
239 /**
240 * Create a HttpClient {@link Header} for cookies that matches
241 * the domain and path.
242 *
243 * @param theDomain the cookie domain to match
244 * @param thePath the cookie path to match
245 * @param theCookies the list of potential cookies
246 * @return the HttpClient {@link Header} containing the matching
247 * cookies
248 * @throws ClientException if no cookie was matching the domain
249 * and path
250 */
251 public static Header createCookieHeader(String theDomain, String thePath,
252 org.apache.commons.httpclient.Cookie[] theCookies)
253 throws ClientException
254 {
255 Header cookieHeader = null;
256
257 // separate domain into host and port
258 int port = 80;
259 String host = theDomain;
260 int portIndex = theDomain.indexOf(":");
261 if (portIndex != -1)
262 {
263 host = host.substring(0, portIndex);
264 port = Integer.parseInt(theDomain.substring(portIndex + 1));
265 }
266
267 CookieSpec matcher = CookiePolicy.getDefaultSpec();
268 org.apache.commons.httpclient.Cookie[] cookies =
269 matcher.match(host, port, thePath, false, theCookies);
270 if ((cookies != null) && (cookies.length > 0))
271 {
272 cookieHeader = matcher.formatCookieHeader(cookies);
273 }
274
275 if (cookieHeader == null)
276 {
277 throw new ClientException("Failed to create Cookie header for ["
278 + "domain = [" + theDomain + ", path = [" + thePath
279 + ", cookies = [" + theCookies + "]]. Turn on HttpClient "
280 + "logging for more information about the error");
281 }
282
283 return cookieHeader;
284 }
285
286 /**
287 * @return the cookie string which will be added as a HTTP "Cookie" header
288 * or null if no cookie has been set
289 * @param theRequest the request containing all data to pass to the server
290 * redirector.
291 * @param theUrl the URL to connect to
292 * @throws ClientException if an error occurred when creating the cookie
293 * string
294 */
295 public static String getCookieString(WebRequest theRequest, URL theUrl)
296 throws ClientException
297 {
298 // If no Cookies, then exit
299 Vector cookies = theRequest.getCookies();
300
301 if (!cookies.isEmpty())
302 {
303 // transform the Cactus cookies into HttpClient cookies
304 org.apache.commons.httpclient.Cookie[] httpclientCookies =
305 CookieUtil.createHttpClientCookies(theRequest, theUrl);
306
307 // and create the cookie header to send
308 Header cookieHeader = createCookieHeader(
309 CookieUtil.getCookieDomain(theRequest, theUrl.getHost()),
310 CookieUtil.getCookiePath(theRequest, theUrl.getFile()),
311 httpclientCookies);
312
313 return cookieHeader.getValue();
314 }
315
316 return null;
317 }
318 }