View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 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.ArrayList;
21  import java.util.List;
22  import java.util.Locale;
23  
24  import javax.servlet.http.Cookie;
25  
26  import org.eclipse.jetty.http.QuotedCSV;
27  import org.eclipse.jetty.util.log.Log;
28  import org.eclipse.jetty.util.log.Logger;
29  
30  
31  /* ------------------------------------------------------------ */
32  /** Cookie parser
33   * <p>Optimized stateful cookie parser.  Cookies fields are added with the
34   * {@link #addCookieField(String)} method and parsed on the next subsequent
35   * call to {@link #getCookies()}.
36   * If the added fields are identical to those last added (as strings), then the 
37   * cookies are not re parsed.
38   *
39   */
40  public class CookieCutter
41  {
42      private static final Logger LOG = Log.getLogger(CookieCutter.class);
43  
44      private Cookie[] _cookies;
45      private Cookie[] _lastCookies;
46      private final List<String> _fieldList = new ArrayList<>();
47      int _fields;
48      
49      public CookieCutter()
50      {  
51      }
52      
53      public Cookie[] getCookies()
54      {
55          if (_cookies!=null)
56              return _cookies;
57          
58          if (_lastCookies!=null && _fields==_fieldList.size())
59              _cookies=_lastCookies;
60          else
61              parseFields();
62          _lastCookies=_cookies;
63          return _cookies;
64      }
65      
66      public void setCookies(Cookie[] cookies)
67      {
68          _cookies=cookies;
69          _lastCookies=null;
70          _fieldList.clear();
71          _fields=0;
72      }
73      
74      public void reset()
75      {
76          _cookies=null;
77          _fields=0;
78      }
79      
80      public void addCookieField(String f)
81      {
82          if (f==null)
83              return;
84          f=f.trim();
85          if (f.length()==0)
86              return;
87              
88          if (_fieldList.size()>_fields)
89          {
90              if (f.equals(_fieldList.get(_fields)))
91              {
92                  _fields++;
93                  return;
94              }
95              
96              while (_fieldList.size()>_fields)
97                  _fieldList.remove(_fields);
98          }
99          _cookies=null;
100         _lastCookies=null;
101         _fieldList.add(_fields++,f);
102     }
103     
104     
105     protected void parseFields()
106     {
107         _lastCookies=null;
108         _cookies=null;
109         
110         List<Cookie> cookies = new ArrayList<>();
111 
112         int version = 0;
113 
114         // delete excess fields
115         while (_fieldList.size()>_fields)
116             _fieldList.remove(_fields);
117         
118         // For each cookie field
119         for (String hdr : _fieldList)
120         {
121             // Parse the header
122             String name = null;
123             String value = null;
124 
125             Cookie cookie = null;
126 
127             boolean invalue=false;
128             boolean quoted=false;
129             boolean escaped=false;
130             int tokenstart=-1;
131             int tokenend=-1;
132             for (int i = 0, length = hdr.length(), last=length-1; i < length; i++)
133             {
134                 char c = hdr.charAt(i);
135                 
136                 // Handle quoted values for name or value
137                 if (quoted)
138                 {
139                     if (escaped)
140                     {
141                         escaped=false;
142                         continue;
143                     }
144                     
145                     switch (c)
146                     {
147                         case '"':
148                             tokenend=i;
149                             quoted=false;
150 
151                             // handle quote as last character specially
152                             if (i==last)
153                             {
154                                 if (invalue)
155                                     value = hdr.substring(tokenstart, tokenend+1);
156                                 else
157                                 {
158                                     name = hdr.substring(tokenstart, tokenend+1);
159                                     value = "";
160                                 }
161                             }
162                             break;
163                             
164                         case '\\':
165                             escaped=true;
166                             continue;
167                         default:
168                             continue;
169                     }
170                 }
171                 else
172                 {
173                     // Handle name and value state machines
174                     if (invalue)
175                     {
176                         // parse the value
177                         switch (c)
178                         {
179                             case ' ':
180                             case '\t':
181                                 continue;
182                                 
183                             case '"':
184                                 if (tokenstart<0)
185                                 {
186                                     quoted=true;
187                                     tokenstart=i;
188                                 }
189                                 tokenend=i;
190                                 if (i==last)
191                                 {
192                                     value = hdr.substring(tokenstart, tokenend+1);
193                                     break;
194                                 }
195                                 continue;
196 
197                             case ';':
198                                 if (tokenstart>=0)
199                                     value = hdr.substring(tokenstart, tokenend+1);
200                                 else
201                                     value="";
202                                 tokenstart = -1;
203                                 invalue=false;
204                                 break;
205                                 
206                             default:
207                                 if (tokenstart<0)
208                                     tokenstart=i;
209                                 tokenend=i;
210                                 if (i==last)
211                                 {
212                                     value = hdr.substring(tokenstart, tokenend+1);
213                                     break;
214                                 }
215                                 continue;
216                         }
217                     }
218                     else
219                     {
220                         // parse the name
221                         switch (c)
222                         {
223                             case ' ':
224                             case '\t':
225                                 continue;
226                                 
227                             case '"':
228                                 if (tokenstart<0)
229                                 {
230                                     quoted=true;
231                                     tokenstart=i;
232                                 }
233                                 tokenend=i;
234                                 if (i==last)
235                                 {
236                                     name = hdr.substring(tokenstart, tokenend+1);
237                                     value = "";
238                                     break;
239                                 }
240                                 continue;
241 
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                    
277                     name=QuotedCSV.unquote(name);
278                     value=QuotedCSV.unquote(value);
279                     
280                     try
281                     {
282                         if (name.startsWith("$"))
283                         {
284                             String lowercaseName = name.toLowerCase(Locale.ENGLISH);
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.add(cookie);
311                         }
312                     }
313                     catch (Exception e)
314                     {
315                         LOG.debug(e);
316                     }
317 
318                     name = null;
319                     value = null;
320                 }
321             }
322         }
323 
324         _cookies = (Cookie[]) cookies.toArray(new Cookie[cookies.size()]);
325         _lastCookies=_cookies;
326     }
327     
328 }