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                                 if (tokenstart>=0)
204                                     value = hdr.substring(tokenstart, tokenend+1);
205                                 else
206                                     value="";
207                                 tokenstart = -1;
208                                 invalue=false;
209                                 break;
210                                 
211                             default:
212                                 if (tokenstart<0)
213                                     tokenstart=i;
214                                 tokenend=i;
215                                 if (i==last)
216                                 {
217                                     value = hdr.substring(tokenstart, tokenend+1);
218                                     break;
219                                 }
220                                 continue;
221                         }
222                     }
223                     else
224                     {
225                         // parse the name
226                         switch (c)
227                         {
228                             case ' ':
229                             case '\t':
230                                 continue;
231                                 
232                             case '"':
233                                 if (tokenstart<0)
234                                 {
235                                     quoted=true;
236                                     tokenstart=i;
237                                 }
238                                 tokenend=i;
239                                 if (i==last)
240                                 {
241                                     name = hdr.substring(tokenstart, tokenend+1);
242                                     value = "";
243                                     break;
244                                 }
245                                 continue;
246 
247                             case ';':
248                                 if (tokenstart>=0)
249                                 {
250                                     name = hdr.substring(tokenstart, tokenend+1);
251                                     value = "";
252                                 }
253                                 tokenstart = -1;
254                                 break;
255 
256                             case '=':
257                                 if (tokenstart>=0)
258                                     name = hdr.substring(tokenstart, tokenend+1);
259                                 tokenstart = -1;
260                                 invalue=true;
261                                 continue;
262                                 
263                             default:
264                                 if (tokenstart<0)
265                                     tokenstart=i;
266                                 tokenend=i;
267                                 if (i==last)
268                                 {
269                                     name = hdr.substring(tokenstart, tokenend+1);
270                                     value = "";
271                                     break;
272                                 }
273                                 continue;
274                         }
275                     }
276                 }
277 
278                 // If after processing the current character we have a value and a name, then it is a cookie
279                 if (value!=null && name!=null)
280                 {
281                     name=QuotedStringTokenizer.unquoteOnly(name);
282                     value=QuotedStringTokenizer.unquoteOnly(value);
283                     
284                     try
285                     {
286                         if (name.startsWith("$"))
287                         {
288                             String lowercaseName = name.toLowerCase(Locale.ENGLISH);
289                             if ("$path".equals(lowercaseName))
290                             {
291                                 if (cookie!=null)
292                                     cookie.setPath(value);
293                             }
294                             else if ("$domain".equals(lowercaseName))
295                             {
296                                 if (cookie!=null)
297                                     cookie.setDomain(value);
298                             }
299                             else if ("$port".equals(lowercaseName))
300                             {
301                                 if (cookie!=null)
302                                     cookie.setComment("$port="+value);
303                             }
304                             else if ("$version".equals(lowercaseName))
305                             {
306                                 version = Integer.parseInt(value);
307                             }
308                         }
309                         else
310                         {
311                             cookie = new Cookie(name, value);
312                             if (version > 0)
313                                 cookie.setVersion(version);
314                             cookies = LazyList.add(cookies, cookie);
315                         }
316                     }
317                     catch (Exception e)
318                     {
319                         LOG.debug(e);
320                     }
321 
322                     name = null;
323                     value = null;
324                 }
325             }
326         }
327 
328         _cookies = (Cookie[]) LazyList.toArray(cookies,Cookie.class);
329         _lastCookies=_cookies;
330     }
331     
332 }