// ======================================================================== // Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // You may elect to redistribute this code under either of these licenses. // ======================================================================== package org.eclipse.jetty.server; import javax.servlet.http.Cookie; import org.eclipse.jetty.util.LazyList; import org.eclipse.jetty.util.QuotedStringTokenizer; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; /* ------------------------------------------------------------ */ /** Cookie parser * <p>Optimized stateful cookie parser. Cookies fields are added with the * {@link #addCookieField(String)} method and parsed on the next subsequent * call to {@link #getCookies()}. * If the added fields are identical to those last added (as strings), then the * cookies are not re parsed. * * */ public class CookieCutter { private static final Logger LOG = Log.getLogger(CookieCutter.class); private Cookie[] _cookies; private Cookie[] _lastCookies; Object _lazyFields; int _fields; public CookieCutter() { } public Cookie[] getCookies() { if (_cookies!=null) return _cookies; if (_lastCookies!=null && _lazyFields!=null && _fields==LazyList.size(_lazyFields)) _cookies=_lastCookies; else parseFields(); _lastCookies=_cookies; return _cookies; } public void setCookies(Cookie[] cookies) { _cookies=cookies; _lastCookies=null; _lazyFields=null; _fields=0; } public void reset() { _cookies=null; _fields=0; } public void addCookieField(String f) { if (f==null) return; f=f.trim(); if (f.length()==0) return; if (LazyList.size(_lazyFields)>_fields) { if (f.equals(LazyList.get(_lazyFields,_fields))) { _fields++; return; } while (LazyList.size(_lazyFields)>_fields) _lazyFields=LazyList.remove(_lazyFields,_fields); } _cookies=null; _lastCookies=null; _lazyFields=LazyList.add(_lazyFields,_fields++,f); } protected void parseFields() { _lastCookies=null; _cookies=null; Object cookies = null; int version = 0; // delete excess fields while (LazyList.size(_lazyFields)>_fields) _lazyFields=LazyList.remove(_lazyFields,_fields); // For each cookie field for (int f=0;f<_fields;f++) { String hdr = LazyList.get(_lazyFields,f); // Parse the header String name = null; String value = null; Cookie cookie = null; boolean invalue=false; boolean quoted=false; boolean escaped=false; int tokenstart=-1; int tokenend=-1; for (int i = 0, length = hdr.length(), last=length-1; i < length; i++) { char c = hdr.charAt(i); // Handle quoted values for name or value if (quoted) { if (escaped) { escaped=false; continue; } switch (c) { case '"': tokenend=i; quoted=false; // handle quote as last character specially if (i==last) { if (invalue) value = hdr.substring(tokenstart, tokenend+1); else { name = hdr.substring(tokenstart, tokenend+1); value = ""; } } break; case '\\': escaped=true; continue; default: continue; } } else { // Handle name and value state machines if (invalue) { // parse the value switch (c) { case ' ': case '\t': continue; case '"': if (tokenstart<0) { quoted=true; tokenstart=i; } tokenend=i; if (i==last) { value = hdr.substring(tokenstart, tokenend+1); break; } continue; case ';': case ',': if (tokenstart>=0) value = hdr.substring(tokenstart, tokenend+1); else value=""; tokenstart = -1; invalue=false; break; default: if (tokenstart<0) tokenstart=i; tokenend=i; if (i==last) { value = hdr.substring(tokenstart, tokenend+1); break; } continue; } } else { // parse the name switch (c) { case ' ': case '\t': continue; case '"': if (tokenstart<0) { quoted=true; tokenstart=i; } tokenend=i; if (i==last) { name = hdr.substring(tokenstart, tokenend+1); value = ""; break; } continue; case ';': case ',': if (tokenstart>=0) { name = hdr.substring(tokenstart, tokenend+1); value = ""; } tokenstart = -1; break; case '=': if (tokenstart>=0) name = hdr.substring(tokenstart, tokenend+1); tokenstart = -1; invalue=true; continue; default: if (tokenstart<0) tokenstart=i; tokenend=i; if (i==last) { name = hdr.substring(tokenstart, tokenend+1); value = ""; break; } continue; } } } // If after processing the current character we have a value and a name, then it is a cookie if (value!=null && name!=null) { // TODO handle unquoting during parsing! But quoting is uncommon name=QuotedStringTokenizer.unquote(name); value=QuotedStringTokenizer.unquote(value); try { if (name.startsWith("$")) { String lowercaseName = name.toLowerCase(); if ("$path".equals(lowercaseName)) { if (cookie!=null) cookie.setPath(value); } else if ("$domain".equals(lowercaseName)) { if (cookie!=null) cookie.setDomain(value); } else if ("$port".equals(lowercaseName)) { if (cookie!=null) cookie.setComment("$port="+value); } else if ("$version".equals(lowercaseName)) { version = Integer.parseInt(value); } } else { cookie = new Cookie(name, value); if (version > 0) cookie.setVersion(version); cookies = LazyList.add(cookies, cookie); } } catch (Exception e) { LOG.warn(e.toString()); LOG.debug(e); } name = null; value = null; } } } _cookies = (Cookie[]) LazyList.toArray(cookies,Cookie.class); _lastCookies=_cookies; } }