1 //
2 //  ========================================================================
3 //  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4 //  ------------------------------------------------------------------------
5 //  All rights reserved. This program and the accompanying materials
6 //  are made available under the terms of the Eclipse Public License v1.0
7 //  and Apache License v2.0 which accompanies this distribution.
8 //
9 //      The Eclipse Public License is available at
10 //      http://www.eclipse.org/legal/epl-v10.html
11 //
12 //      The Apache License v2.0 is available at
13 //      http://www.opensource.org/licenses/apache2.0.php
14 //
15 //  You may elect to redistribute this code under either of these licenses.
16 //  ========================================================================
17 //
18 
19 package org.eclipse.jetty.server;
20 import java.util.Locale;
21 
22 import javax.servlet.http.Cookie;
23 
24 import org.eclipse.jetty.util.LazyList;
25 import org.eclipse.jetty.util.QuotedStringTokenizer;
26 import org.eclipse.jetty.util.log.Log;
27 import org.eclipse.jetty.util.log.Logger;
28 
29 
30 /* ------------------------------------------------------------ */
31 /** Cookie parser
32  * <p>Optimized stateful cookie parser.  Cookies fields are added with the
33  * {@link #addCookieField(String)} method and parsed on the next subsequent
34  * call to {@link #getCookies()}.
35  * If the added fields are identical to those last added (as strings), then the
36  * cookies are not re parsed.
37  *
38  *
39  */
40 public class CookieCutter
41 {
42     private static final Logger LOG = Log.getLogger(CookieCutter.class);
43 
44 
45     private Cookie[] _cookies;
46     private Cookie[] _lastCookies;
47     Object _lazyFields;
48     int _fields;
49 
CookieCutter()50     public CookieCutter()
51     {
52     }
53 
getCookies()54     public Cookie[] getCookies()
55     {
56         if (_cookies!=null)
57             return _cookies;
58 
59         if (_lastCookies!=null &&
60             _lazyFields!=null &&
61             _fields==LazyList.size(_lazyFields))
62             _cookies=_lastCookies;
63         else
64             parseFields();
65         _lastCookies=_cookies;
66         return _cookies;
67     }
68 
setCookies(Cookie[] cookies)69     public void setCookies(Cookie[] cookies)
70     {
71         _cookies=cookies;
72         _lastCookies=null;
73         _lazyFields=null;
74         _fields=0;
75     }
76 
reset()77     public void reset()
78     {
79         _cookies=null;
80         _fields=0;
81     }
82 
addCookieField(String f)83     public void addCookieField(String f)
84     {
85         if (f==null)
86             return;
87         f=f.trim();
88         if (f.length()==0)
89             return;
90 
91         if (LazyList.size(_lazyFields)>_fields)
92         {
93             if (f.equals(LazyList.get(_lazyFields,_fields)))
94             {
95                 _fields++;
96                 return;
97             }
98 
99             while (LazyList.size(_lazyFields)>_fields)
100                 _lazyFields=LazyList.remove(_lazyFields,_fields);
101         }
102         _cookies=null;
103         _lastCookies=null;
104         _lazyFields=LazyList.add(_lazyFields,_fields++,f);
105     }
106 
107 
parseFields()108     protected void parseFields()
109     {
110         _lastCookies=null;
111         _cookies=null;
112 
113         Object cookies = null;
114 
115         int version = 0;
116 
117         // delete excess fields
118         while (LazyList.size(_lazyFields)>_fields)
119             _lazyFields=LazyList.remove(_lazyFields,_fields);
120 
121         // For each cookie field
122         for (int f=0;f<_fields;f++)
123         {
124             String hdr = LazyList.get(_lazyFields,f);
125 
126             // Parse the header
127             String name = null;
128             String value = null;
129 
130             Cookie cookie = null;
131 
132             boolean invalue=false;
133             boolean quoted=false;
134             boolean escaped=false;
135             int tokenstart=-1;
136             int tokenend=-1;
137             for (int i = 0, length = hdr.length(), last=length-1; i < length; i++)
138             {
139                 char c = hdr.charAt(i);
140 
141                 // Handle quoted values for name or value
142                 if (quoted)
143                 {
144                     if (escaped)
145                     {
146                         escaped=false;
147                         continue;
148                     }
149 
150                     switch (c)
151                     {
152                         case '"':
153                             tokenend=i;
154                             quoted=false;
155 
156                             // handle quote as last character specially
157                             if (i==last)
158                             {
159                                 if (invalue)
160                                     value = hdr.substring(tokenstart, tokenend+1);
161                                 else
162                                 {
163                                     name = hdr.substring(tokenstart, tokenend+1);
164                                     value = "";
165                                 }
166                             }
167                             break;
168 
169                         case '\\':
170                             escaped=true;
171                             continue;
172                         default:
173                             continue;
174                     }
175                 }
176                 else
177                 {
178                     // Handle name and value state machines
179                     if (invalue)
180                     {
181                         // parse the value
182                         switch (c)
183                         {
184                             case ' ':
185                             case '\t':
186                                 continue;
187 
188                             case '"':
189                                 if (tokenstart<0)
190                                 {
191                                     quoted=true;
192                                     tokenstart=i;
193                                 }
194                                 tokenend=i;
195                                 if (i==last)
196                                 {
197                                     value = hdr.substring(tokenstart, tokenend+1);
198                                     break;
199                                 }
200                                 continue;
201 
202                             case ';':
203                             // case ',':
204                                 if (tokenstart>=0)
205                                     value = hdr.substring(tokenstart, tokenend+1);
206                                 else
207                                     value="";
208                                 tokenstart = -1;
209                                 invalue=false;
210                                 break;
211 
212                             default:
213                                 if (tokenstart<0)
214                                     tokenstart=i;
215                                 tokenend=i;
216                                 if (i==last)
217                                 {
218                                     value = hdr.substring(tokenstart, tokenend+1);
219                                     break;
220                                 }
221                                 continue;
222                         }
223                     }
224                     else
225                     {
226                         // parse the name
227                         switch (c)
228                         {
229                             case ' ':
230                             case '\t':
231                                 continue;
232 
233                             case '"':
234                                 if (tokenstart<0)
235                                 {
236                                     quoted=true;
237                                     tokenstart=i;
238                                 }
239                                 tokenend=i;
240                                 if (i==last)
241                                 {
242                                     name = hdr.substring(tokenstart, tokenend+1);
243                                     value = "";
244                                     break;
245                                 }
246                                 continue;
247 
248                             case ';':
249                             // case ',':
250                                 if (tokenstart>=0)
251                                 {
252                                     name = hdr.substring(tokenstart, tokenend+1);
253                                     value = "";
254                                 }
255                                 tokenstart = -1;
256                                 break;
257 
258                             case '=':
259                                 if (tokenstart>=0)
260                                     name = hdr.substring(tokenstart, tokenend+1);
261                                 tokenstart = -1;
262                                 invalue=true;
263                                 continue;
264 
265                             default:
266                                 if (tokenstart<0)
267                                     tokenstart=i;
268                                 tokenend=i;
269                                 if (i==last)
270                                 {
271                                     name = hdr.substring(tokenstart, tokenend+1);
272                                     value = "";
273                                     break;
274                                 }
275                                 continue;
276                         }
277                     }
278                 }
279 
280                 // If after processing the current character we have a value and a name, then it is a cookie
281                 if (value!=null && name!=null)
282                 {
283                     // TODO handle unquoting during parsing!  But quoting is uncommon
284                     name=QuotedStringTokenizer.unquoteOnly(name);
285                     value=QuotedStringTokenizer.unquoteOnly(value);
286 
287                     try
288                     {
289                         if (name.startsWith("$"))
290                         {
291                             String lowercaseName = name.toLowerCase(Locale.ENGLISH);
292                             if ("$path".equals(lowercaseName))
293                             {
294                                 if (cookie!=null)
295                                     cookie.setPath(value);
296                             }
297                             else if ("$domain".equals(lowercaseName))
298                             {
299                                 if (cookie!=null)
300                                     cookie.setDomain(value);
301                             }
302                             else if ("$port".equals(lowercaseName))
303                             {
304                                 if (cookie!=null)
305                                     cookie.setComment("$port="+value);
306                             }
307                             else if ("$version".equals(lowercaseName))
308                             {
309                                 version = Integer.parseInt(value);
310                             }
311                         }
312                         else
313                         {
314                             cookie = new Cookie(name, value);
315                             if (version > 0)
316                                 cookie.setVersion(version);
317                             cookies = LazyList.add(cookies, cookie);
318                         }
319                     }
320                     catch (Exception e)
321                     {
322                         LOG.debug(e);
323                     }
324 
325                     name = null;
326                     value = null;
327                 }
328             }
329         }
330 
331         _cookies = (Cookie[]) LazyList.toArray(cookies,Cookie.class);
332         _lastCookies=_cookies;
333     }
334 
335 }
336