Home | History | Annotate | Download | only in server
      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 
     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 }
    336