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