View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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      
50      public CookieCutter()
51      {  
52      }
53      
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      
69      public void setCookies(Cookie[] cookies)
70      {
71          _cookies=cookies;
72          _lastCookies=null;
73          _lazyFields=null;
74          _fields=0;
75      }
76      
77      public void reset()
78      {
79          _cookies=null;
80          _fields=0;
81      }
82      
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     
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 }