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