View Javadoc

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