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