View Javadoc

    1   // ========================================================================
    2   // Copyright 2004-2005 Mort Bay Consulting Pty. Ltd.
    3   // ------------------------------------------------------------------------
    4   // Licensed under the Apache License, Version 2.0 (the "License");
    5   // you may not use this file except in compliance with the License.
    6   // You may obtain a copy of the License at
    7   // http://www.apache.org/licenses/LICENSE-2.0
    8   // Unless required by applicable law or agreed to in writing, software
    9   // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  // ========================================================================
    14
    15  package org.mortbay.jetty;
    16
    17  import java.io.IOException;
    18  import java.text.SimpleDateFormat;
    19  import java.util.ArrayList;
    20  import java.util.Calendar;
    21  import java.util.Collections;
    22  import java.util.Date;
    23  import java.util.Enumeration;
    24  import java.util.GregorianCalendar;
    25  import java.util.HashMap;
    26  import java.util.Iterator;
    27  import java.util.List;
    28  import java.util.Locale;
    29  import java.util.Map;
    30  import java.util.NoSuchElementException;
    31  import java.util.StringTokenizer;
    32  import java.util.TimeZone;
    33
    34  import javax.servlet.http.Cookie;
    35
    36  import org.mortbay.io.Buffer;
    37  import org.mortbay.io.BufferCache;
    38  import org.mortbay.io.BufferDateCache;
    39  import org.mortbay.io.BufferUtil;
    40  import org.mortbay.io.ByteArrayBuffer;
    41  import org.mortbay.io.View;
    42  import org.mortbay.io.BufferCache.CachedBuffer;
    43  import org.mortbay.util.LazyList;
    44  import org.mortbay.util.QuotedStringTokenizer;
    45  import org.mortbay.util.StringMap;
    46  import org.mortbay.util.StringUtil;
    47  import org.mortbay.util.URIUtil;
    48
    49  /* ------------------------------------------------------------ */
    50  /**
    51   * HTTP Fields. A collection of HTTP header and or Trailer fields. This class is not synchronized
    52   * and needs to be protected from concurrent access.
    53   *
    54   * This class is not synchronized as it is expected that modifications will only be performed by a
    55   * single thread.
    56   *
    57   * @author Greg Wilkins (gregw)
    58   */
    59  public class HttpFields
    60  {
    61      /* ------------------------------------------------------------ */
    62      public final static String __separators = ", \t";
    63
    64      /* ------------------------------------------------------------ */
    65      private static String[] DAYS =
    66      { "Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
    67      private static String[] MONTHS =
    68      { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Jan"};
    69
    70      /* ------------------------------------------------------------ */
    71      /**
    72       * Format HTTP date "EEE, dd MMM yyyy HH:mm:ss 'GMT'" or "EEE, dd-MMM-yy HH:mm:ss 'GMT'"for
    73       * cookies
    74       */
    75      public static String formatDate(long date, boolean cookie)
    76      {
    77          StringBuffer buf = new StringBuffer(32);
    78          GregorianCalendar gc = new GregorianCalendar(__GMT);
    79          gc.setTimeInMillis(date);
    80          formatDate(buf, gc, cookie);
    81          return buf.toString();
    82      }
    83
    84      /* ------------------------------------------------------------ */
    85      /**
    86       * Format HTTP date "EEE, dd MMM yyyy HH:mm:ss 'GMT'" or "EEE, dd-MMM-yy HH:mm:ss 'GMT'"for
    87       * cookies
    88       */
    89      public static String formatDate(Calendar calendar, boolean cookie)
    90      {
    91          StringBuffer buf = new StringBuffer(32);
    92          formatDate(buf, calendar, cookie);
    93          return buf.toString();
    94      }
    95
    96      /* ------------------------------------------------------------ */
    97      /**
    98       * Format HTTP date "EEE, dd MMM yyyy HH:mm:ss 'GMT'" or "EEE, dd-MMM-yy HH:mm:ss 'GMT'"for
    99       * cookies
    100      */
    101     public static String formatDate(StringBuffer buf, long date, boolean cookie)
    102     {
    103         GregorianCalendar gc = new GregorianCalendar(__GMT);
    104         gc.setTimeInMillis(date);
    105         formatDate(buf, gc, cookie);
    106         return buf.toString();
    107     }
    108
    109     /* ------------------------------------------------------------ */
    110     /**
    111      * Format HTTP date "EEE, dd MMM yyyy HH:mm:ss 'GMT'" or "EEE, dd-MMM-yy HH:mm:ss 'GMT'"for
    112      * cookies
    113      */
    114     public static void formatDate(StringBuffer buf, Calendar calendar, boolean cookie)
    115     {
    116         // "EEE, dd MMM yyyy HH:mm:ss 'GMT'"
    117         // "EEE, dd-MMM-yy HH:mm:ss 'GMT'", cookie
    118
    119         int day_of_week = calendar.get(Calendar.DAY_OF_WEEK);
    120         int day_of_month = calendar.get(Calendar.DAY_OF_MONTH);
    121         int month = calendar.get(Calendar.MONTH);
    122         int year = calendar.get(Calendar.YEAR);
    123         int century = year / 100;
    124         year = year % 100;
    125
    126         int epoch = (int) ((calendar.getTimeInMillis() / 1000) % (60 * 60 * 24));
    127         int seconds = epoch % 60;
    128         epoch = epoch / 60;
    129         int minutes = epoch % 60;
    130         int hours = epoch / 60;
    131
    132         buf.append(DAYS[day_of_week]);
    133         buf.append(',');
    134         buf.append(' ');
    135         StringUtil.append2digits(buf, day_of_month);
    136
    137         if (cookie)
    138         {
    139             buf.append('-');
    140             buf.append(MONTHS[month]);
    141             buf.append('-');
    142             StringUtil.append2digits(buf, year);
    143         }
    144         else
    145         {
    146             buf.append(' ');
    147             buf.append(MONTHS[month]);
    148             buf.append(' ');
    149             StringUtil.append2digits(buf, century);
    150             StringUtil.append2digits(buf, year);
    151         }
    152         buf.append(' ');
    153         StringUtil.append2digits(buf, hours);
    154         buf.append(':');
    155         StringUtil.append2digits(buf, minutes);
    156         buf.append(':');
    157         StringUtil.append2digits(buf, seconds);
    158         buf.append(" GMT");
    159     }
    160
    161     /* -------------------------------------------------------------- */
    162     private static TimeZone __GMT = TimeZone.getTimeZone("GMT");
    163     public final static BufferDateCache __dateCache = new BufferDateCache("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
    164
    165     /* ------------------------------------------------------------ */
    166     private final static String __dateReceiveFmt[] =
    167     {   "EEE, dd MMM yyyy HH:mm:ss zzz",
    168         "EEE, dd-MMM-yy HH:mm:ss",
    169         "EEE MMM dd HH:mm:ss yyyy",
    170
    171         "EEE, dd MMM yyyy HH:mm:ss", "EEE dd MMM yyyy HH:mm:ss zzz",
    172         "EEE dd MMM yyyy HH:mm:ss", "EEE MMM dd yyyy HH:mm:ss zzz", "EEE MMM dd yyyy HH:mm:ss",
    173         "EEE MMM-dd-yyyy HH:mm:ss zzz", "EEE MMM-dd-yyyy HH:mm:ss", "dd MMM yyyy HH:mm:ss zzz",
    174         "dd MMM yyyy HH:mm:ss", "dd-MMM-yy HH:mm:ss zzz", "dd-MMM-yy HH:mm:ss", "MMM dd HH:mm:ss yyyy zzz",
    175         "MMM dd HH:mm:ss yyyy", "EEE MMM dd HH:mm:ss yyyy zzz",
    176         "EEE, MMM dd HH:mm:ss yyyy zzz", "EEE, MMM dd HH:mm:ss yyyy", "EEE, dd-MMM-yy HH:mm:ss zzz",
    177         "EEE dd-MMM-yy HH:mm:ss zzz", "EEE dd-MMM-yy HH:mm:ss",
    178       };
    179     private  static int __dateReceiveInit=3;
    180     private  static SimpleDateFormat __dateReceive[];
    181     static
    182     {
    183         __GMT.setID("GMT");
    184         __dateCache.setTimeZone(__GMT);
    185         __dateReceive = new SimpleDateFormat[__dateReceiveFmt.length];
    186         // Initialize only the standard formats here.
    187         for (int i = 0; i < __dateReceiveInit; i++)
    188         {
    189             __dateReceive[i] = new SimpleDateFormat(__dateReceiveFmt[i], Locale.US);
    190             __dateReceive[i].setTimeZone(__GMT);
    191         }
    192     }
    193     public final static String __01Jan1970 = formatDate(0, false);
    194     public final static Buffer __01Jan1970_BUFFER = new ByteArrayBuffer(__01Jan1970);
    195
    196     /* -------------------------------------------------------------- */
    197     protected ArrayList _fields = new ArrayList(20);
    198     protected int _revision;
    199     protected HashMap _bufferMap = new HashMap(32);
    200     protected SimpleDateFormat _dateReceive[] = new SimpleDateFormat[__dateReceive.length];
    201     private StringBuffer _dateBuffer;
    202     private Calendar _calendar;
    203
    204     /* ------------------------------------------------------------ */
    205     /**
    206      * Constructor.
    207      */
    208     public HttpFields()
    209     {
    210     }
    211
    212     /* -------------------------------------------------------------- */
    213     /**
    214      * Get enumeration of header _names. Returns an enumeration of strings representing the header
    215      * _names for this request.
    216      */
    217     public Enumeration getFieldNames()
    218     {
    219         final int revision=_revision;
    220         return new Enumeration()
    221         {
    222             int i = 0;
    223             Field field = null;
    224
    225             public boolean hasMoreElements()
    226             {
    227                 if (field != null) return true;
    228                 while (i < _fields.size())
    229                 {
    230                     Field f = (Field) _fields.get(i++);
    231                     if (f != null && f._prev == null && f._revision == revision)
    232                     {
    233                         field = f;
    234                         return true;
    235                     }
    236                 }
    237                 return false;
    238             }
    239
    240             public Object nextElement() throws NoSuchElementException
    241             {
    242                 if (field != null || hasMoreElements())
    243                 {
    244                     String n = BufferUtil.to8859_1_String(field._name);
    245                     field = null;
    246                     return n;
    247                 }
    248                 throw new NoSuchElementException();
    249             }
    250         };
    251     }
    252
    253     /* -------------------------------------------------------------- */
    254     /**
    255      * Get enumeration of Fields Returns an enumeration of Fields for this request.
    256      */
    257     public Iterator getFields()
    258     {
    259         final int revision=_revision;
    260         return new Iterator()
    261         {
    262             int i = 0;
    263             Field field = null;
    264
    265             public boolean hasNext()
    266             {
    267                 if (field != null) return true;
    268                 while (i < _fields.size())
    269                 {
    270                     Field f = (Field) _fields.get(i++);
    271                     if (f != null && f._revision == revision)
    272                     {
    273                         field = f;
    274                         return true;
    275                     }
    276                 }
    277                 return false;
    278             }
    279
    280             public Object next()
    281             {
    282                 if (field != null || hasNext())
    283                 {
    284                     final Field f = field;
    285                     field = null;
    286                     return f;
    287                 }
    288                 throw new NoSuchElementException();
    289             }
    290
    291             public void remove()
    292             {
    293                 throw new UnsupportedOperationException();
    294             }
    295         };
    296     }
    297
    298     /* ------------------------------------------------------------ */
    299     private Field getField(String name)
    300     {
    301         return (Field) _bufferMap.get(HttpHeaders.CACHE.lookup(name));
    302     }
    303
    304     /* ------------------------------------------------------------ */
    305     private Field getField(Buffer name)
    306     {
    307         return (Field) _bufferMap.get(name);
    308     }
    309
    310     /* ------------------------------------------------------------ */
    311     public boolean containsKey(Buffer name)
    312     {
    313         Field f = getField(name);
    314         return (f != null && f._revision == _revision);
    315     }
    316
    317     /* ------------------------------------------------------------ */
    318     public boolean containsKey(String name)
    319     {
    320         Field f = getField(name);
    321         return (f != null && f._revision == _revision);
    322     }
    323
    324     /* -------------------------------------------------------------- */
    325     /**
    326      * @return the value of a field, or null if not found. For multiple fields of the same name,
    327      *         only the first is returned.
    328      * @param name the case-insensitive field name
    329      */
    330     public String getStringField(String name)
    331     {
    332         // TODO - really reuse strings from previous requests!
    333         Field field = getField(name);
    334         if (field != null && field._revision == _revision) return field.getValue();
    335         return null;
    336     }
    337
    338     /* -------------------------------------------------------------- */
    339     /**
    340      * @return the value of a field, or null if not found. For multiple fields of the same name,
    341      *         only the first is returned.
    342      * @param name the case-insensitive field name
    343      */
    344     public String getStringField(Buffer name)
    345     {
    346         // TODO - really reuse strings from previous requests!
    347         Field field = getField(name);
    348         if (field != null && field._revision == _revision)
    349             return BufferUtil.to8859_1_String(field._value);
    350         return null;
    351     }
    352
    353     /* -------------------------------------------------------------- */
    354     /**
    355      * @return the value of a field, or null if not found. For multiple fields of the same name,
    356      *         only the first is returned.
    357      * @param name the case-insensitive field name
    358      */
    359     public Buffer get(Buffer name)
    360     {
    361         Field field = getField(name);
    362         if (field != null && field._revision == _revision)
    363             return field._value;
    364         return null;
    365     }
    366
    367     /* -------------------------------------------------------------- */
    368     /**
    369      * Get multi headers
    370      *
    371      * @return Enumeration of the values, or null if no such header.
    372      * @param name the case-insensitive field name
    373      */
    374     public Enumeration getValues(String name)
    375     {
    376         final Field field = getField(name);
    377         if (field == null)
    378             return null;
    379         final int revision=_revision;
    380
    381         return new Enumeration()
    382         {
    383             Field f = field;
    384
    385             public boolean hasMoreElements()
    386             {
    387                 while (f != null && f._revision != revision)
    388                     f = f._next;
    389                 return f != null;
    390             }
    391
    392             public Object nextElement() throws NoSuchElementException
    393             {
    394                 if (f == null) throw new NoSuchElementException();
    395                 Field n = f;
    396                 do
    397                     f = f._next;
    398                 while (f != null && f._revision != revision);
    399                 return n.getValue();
    400             }
    401         };
    402     }
    403
    404     /* -------------------------------------------------------------- */
    405     /**
    406      * Get multi headers
    407      *
    408      * @return Enumeration of the value Strings, or null if no such header.
    409      * @param name the case-insensitive field name
    410      */
    411     public Enumeration getValues(Buffer name)
    412     {
    413         final Field field = getField(name);
    414         if (field == null)
    415             return null;
    416         final int revision=_revision;
    417
    418         return new Enumeration()
    419         {
    420             Field f = field;
    421
    422             public boolean hasMoreElements()
    423             {
    424                 while (f != null && f._revision != revision)
    425                     f = f._next;
    426                 return f != null;
    427             }
    428
    429             public Object nextElement() throws NoSuchElementException
    430             {
    431                 if (f == null) throw new NoSuchElementException();
    432                 Field n = f;
    433                 f = f._next;
    434                 while (f != null && f._revision != revision)
    435                     f = f._next;
    436                 return n.getValue();
    437             }
    438         };
    439     }
    440
    441     /* -------------------------------------------------------------- */
    442     /**
    443      * Get multi field values with separator. The multiple values can be represented as separate
    444      * headers of the same name, or by a single header using the separator(s), or a combination of
    445      * both. Separators may be quoted.
    446      *
    447      * @param name the case-insensitive field name
    448      * @param separators String of separators.
    449      * @return Enumeration of the values, or null if no such header.
    450      */
    451     public Enumeration getValues(String name, final String separators)
    452     {
    453         final Enumeration e = getValues(name);
    454         if (e == null)
    455             return null;
    456         return new Enumeration()
    457         {
    458             QuotedStringTokenizer tok = null;
    459
    460             public boolean hasMoreElements()
    461             {
    462                 if (tok != null && tok.hasMoreElements()) return true;
    463                 while (e.hasMoreElements())
    464                 {
    465                     String value = (String) e.nextElement();
    466                     tok = new QuotedStringTokenizer(value, separators, false, false);
    467                     if (tok.hasMoreElements()) return true;
    468                 }
    469                 tok = null;
    470                 return false;
    471             }
    472
    473             public Object nextElement() throws NoSuchElementException
    474             {
    475                 if (!hasMoreElements()) throw new NoSuchElementException();
    476                 String next = (String) tok.nextElement();
    477                 if (next != null) next = next.trim();
    478                 return next;
    479             }
    480         };
    481     }
    482
    483     /* -------------------------------------------------------------- */
    484     /**
    485      * Set a field.
    486      *
    487      * @param name the name of the field
    488      * @param value the value of the field. If null the field is cleared.
    489      */
    490     public void put(String name, String value)
    491     {
    492         Buffer n = HttpHeaders.CACHE.lookup(name);
    493         Buffer v = null;
    494         if (value != null)
    495             v = HttpHeaderValues.CACHE.lookup(value);
    496         put(n, v, -1);
    497     }
    498
    499     /* -------------------------------------------------------------- */
    500     /**
    501      * Set a field.
    502      *
    503      * @param name the name of the field
    504      * @param value the value of the field. If null the field is cleared.
    505      */
    506     public void put(Buffer name, String value)
    507     {
    508         Buffer v = HttpHeaderValues.CACHE.lookup(value);
    509         put(name, v, -1);
    510     }
    511
    512     /* -------------------------------------------------------------- */
    513     /**
    514      * Set a field.
    515      *
    516      * @param name the name of the field
    517      * @param value the value of the field. If null the field is cleared.
    518      */
    519     public void put(Buffer name, Buffer value)
    520     {
    521         put(name, value, -1);
    522     }
    523
    524     /* -------------------------------------------------------------- */
    525     /**
    526      * Set a field.
    527      *
    528      * @param name the name of the field
    529      * @param value the value of the field. If null the field is cleared.
    530      * @param numValue the numeric value of the field (must match value) or -1
    531      */
    532     public void put(Buffer name, Buffer value, long numValue)
    533     {
    534         if (value == null)
    535         {
    536             remove(name);
    537             return;
    538         }
    539
    540         if (!(name instanceof BufferCache.CachedBuffer)) name = HttpHeaders.CACHE.lookup(name);
    541
    542         Field field = (Field) _bufferMap.get(name);
    543
    544         // Look for value to replace.
    545         if (field != null)
    546         {
    547             field.reset(value, numValue, _revision);
    548             field = field._next;
    549             while (field != null)
    550             {
    551                 field.clear();
    552                 field = field._next;
    553             }
    554             return;
    555         }
    556         else
    557         {
    558             // new value;
    559             field = new Field(name, value, numValue, _revision);
    560             _fields.add(field);
    561             _bufferMap.put(field.getNameBuffer(), field);
    562         }
    563     }
    564
    565     /* -------------------------------------------------------------- */
    566     /**
    567      * Set a field.
    568      *
    569      * @param name the name of the field
    570      * @param list the List value of the field. If null the field is cleared.
    571      */
    572     public void put(String name, List list)
    573     {
    574         if (list == null || list.size() == 0)
    575         {
    576             remove(name);
    577             return;
    578         }
    579         Buffer n = HttpHeaders.CACHE.lookup(name);
    580
    581         Object v = list.get(0);
    582         if (v != null)
    583             put(n, HttpHeaderValues.CACHE.lookup(v.toString()));
    584         else
    585             remove(n);
    586
    587         if (list.size() > 1)
    588         {
    589             java.util.Iterator iter = list.iterator();
    590             iter.next();
    591             while (iter.hasNext())
    592             {
    593                 v = iter.next();
    594                 if (v != null) put(n, HttpHeaderValues.CACHE.lookup(v.toString()));
    595             }
    596         }
    597     }
    598
    599     /* -------------------------------------------------------------- */
    600     /**
    601      * Add to or set a field. If the field is allowed to have multiple values, add will add multiple
    602      * headers of the same name.
    603      *
    604      * @param name the name of the field
    605      * @param value the value of the field.
    606      * @exception IllegalArgumentException If the name is a single valued field and already has a
    607      *                value.
    608      */
    609     public void add(String name, String value) throws IllegalArgumentException
    610     {
    611         Buffer n = HttpHeaders.CACHE.lookup(name);
    612         Buffer v = HttpHeaderValues.CACHE.lookup(value);
    613         add(n, v, -1);
    614     }
    615
    616     /* -------------------------------------------------------------- */
    617     /**
    618      * Add to or set a field. If the field is allowed to have multiple values, add will add multiple
    619      * headers of the same name.
    620      *
    621      * @param name the name of the field
    622      * @param value the value of the field.
    623      * @exception IllegalArgumentException If the name is a single valued field and already has a
    624      *                value.
    625      */
    626     public void add(Buffer name, Buffer value) throws IllegalArgumentException
    627     {
    628         add(name, value, -1);
    629     }
    630
    631     /* -------------------------------------------------------------- */
    632     /**
    633      * Add to or set a field. If the field is allowed to have multiple values, add will add multiple
    634      * headers of the same name.
    635      *
    636      * @param name the name of the field
    637      * @param value the value of the field.
    638      * @exception IllegalArgumentException If the name is a single valued field and already has a
    639      *                value.
    640      */
    641     private void add(Buffer name, Buffer value, long numValue) throws IllegalArgumentException
    642     {
    643         if (value == null) throw new IllegalArgumentException("null value");
    644
    645         if (!(name instanceof BufferCache.CachedBuffer)) name = HttpHeaders.CACHE.lookup(name);
    646
    647         Field field = (Field) _bufferMap.get(name);
    648         Field last = null;
    649         if (field != null)
    650         {
    651             while (field != null && field._revision == _revision)
    652             {
    653                 last = field;
    654                 field = field._next;
    655             }
    656         }
    657
    658         if (field != null)
    659             field.reset(value, numValue, _revision);
    660         else
    661         {
    662             // create the field
    663             field = new Field(name, value, numValue, _revision);
    664
    665             // look for chain to add too
    666             if (last != null)
    667             {
    668                 field._prev = last;
    669                 last._next = field;
    670             }
    671             else
    672                 _bufferMap.put(field.getNameBuffer(), field);
    673
    674             _fields.add(field);
    675         }
    676     }
    677
    678     /* ------------------------------------------------------------ */
    679     /**
    680      * Remove a field.
    681      *
    682      * @param name
    683      */
    684     public void remove(String name)
    685     {
    686         remove(HttpHeaders.CACHE.lookup(name));
    687     }
    688
    689     /* ------------------------------------------------------------ */
    690     /**
    691      * Remove a field.
    692      *
    693      * @param name
    694      */
    695     public void remove(Buffer name)
    696     {
    697         Field field = (Field) _bufferMap.get(name);
    698
    699         if (field != null)
    700         {
    701             while (field != null)
    702             {
    703                 field.clear();
    704                 field = field._next;
    705             }
    706         }
    707     }
    708
    709     /* -------------------------------------------------------------- */
    710     /**
    711      * Get a header as an long value. Returns the value of an integer field or -1 if not found. The
    712      * case of the field name is ignored.
    713      *
    714      * @param name the case-insensitive field name
    715      * @exception NumberFormatException If bad long found
    716      */
    717     public long getLongField(String name) throws NumberFormatException
    718     {
    719         Field field = getField(name);
    720         if (field != null && field._revision == _revision) return field.getLongValue();
    721
    722         return -1L;
    723     }
    724
    725     /* -------------------------------------------------------------- */
    726     /**
    727      * Get a header as an long value. Returns the value of an integer field or -1 if not found. The
    728      * case of the field name is ignored.
    729      *
    730      * @param name the case-insensitive field name
    731      * @exception NumberFormatException If bad long found
    732      */
    733     public long getLongField(Buffer name) throws NumberFormatException
    734     {
    735         Field field = getField(name);
    736         if (field != null && field._revision == _revision) return field.getLongValue();
    737         return -1L;
    738     }
    739
    740     /* -------------------------------------------------------------- */
    741     /**
    742      * Get a header as a date value. Returns the value of a date field, or -1 if not found. The case
    743      * of the field name is ignored.
    744      *
    745      * @param name the case-insensitive field name
    746      */
    747     public long getDateField(String name)
    748     {
    749         Field field = getField(name);
    750         if (field == null || field._revision != _revision) return -1;
    751
    752         if (field._numValue != -1) return field._numValue;
    753
    754         String val = valueParameters(BufferUtil.to8859_1_String(field._value), null);
    755         if (val == null) return -1;
    756
    757
    758
    759         for (int i = 0; i < __dateReceiveInit; i++)
    760         {
    761             if (_dateReceive[i] == null) _dateReceive[i] = (SimpleDateFormat) __dateReceive[i].clone();
    762
    763             try
    764             {
    765                 Date date = (Date) _dateReceive[i].parseObject(val);
    766                 return field._numValue = date.getTime();
    767             }
    768             catch (java.lang.Exception e)
    769             {
    770             }
    771         }
    772         if (val.endsWith(" GMT"))
    773         {
    774             val = val.substring(0, val.length() - 4);
    775             for (int i = 0; i < __dateReceiveInit; i++)
    776             {
    777                 try
    778                 {
    779                     Date date = (Date) _dateReceive[i].parseObject(val);
    780                     return field._numValue = date.getTime();
    781                 }
    782                 catch (java.lang.Exception e)
    783                 {
    784                 }
    785             }
    786         }
    787
    788         // The standard formats did not work.  So we will lock the common format array
    789         // and look at lazily creating the non-standard formats
    790         synchronized (__dateReceive)
    791         {
    792             for (int i = __dateReceiveInit; i < _dateReceive.length; i++)
    793             {
    794                 if (_dateReceive[i] == null)
    795                 {
    796                     if (__dateReceive[i]==null)
    797                     {
    798                         __dateReceive[i] = new SimpleDateFormat(__dateReceiveFmt[i], Locale.US);
    799                         __dateReceive[i].setTimeZone(__GMT);
    800                     }
    801                     _dateReceive[i] = (SimpleDateFormat) __dateReceive[i].clone();
    802                 }
    803
    804                 try
    805                 {
    806                     Date date = (Date) _dateReceive[i].parseObject(val);
    807                     return field._numValue = date.getTime();
    808                 }
    809                 catch (java.lang.Exception e)
    810                 {
    811                 }
    812             }
    813             if (val.endsWith(" GMT"))
    814             {
    815                 val = val.substring(0, val.length() - 4);
    816                 for (int i = 0; i < _dateReceive.length; i++)
    817                 {
    818                     try
    819                     {
    820                         Date date = (Date) _dateReceive[i].parseObject(val);
    821                         return field._numValue = date.getTime();
    822                     }
    823                     catch (java.lang.Exception e)
    824                     {
    825                     }
    826                 }
    827             }
    828         }
    829
    830
    831         throw new IllegalArgumentException("Cannot convert date: " + val);
    832     }
    833
    834     /* -------------------------------------------------------------- */
    835     /**
    836      * Sets the value of an long field.
    837      *
    838      * @param name the field name
    839      * @param value the field long value
    840      */
    841     public void putLongField(Buffer name, long value)
    842     {
    843         Buffer v = BufferUtil.toBuffer(value);
    844         put(name, v, value);
    845     }
    846
    847     /* -------------------------------------------------------------- */
    848     /**
    849      * Sets the value of an long field.
    850      *
    851      * @param name the field name
    852      * @param value the field long value
    853      */
    854     public void putLongField(String name, long value)
    855     {
    856         Buffer n = HttpHeaders.CACHE.lookup(name);
    857         Buffer v = BufferUtil.toBuffer(value);
    858         put(n, v, value);
    859     }
    860
    861     /* -------------------------------------------------------------- */
    862     /**
    863      * Sets the value of an long field.
    864      *
    865      * @param name the field name
    866      * @param value the field long value
    867      */
    868     public void addLongField(String name, long value)
    869     {
    870         Buffer n = HttpHeaders.CACHE.lookup(name);
    871         Buffer v = BufferUtil.toBuffer(value);
    872         add(n, v, value);
    873     }
    874
    875     /* -------------------------------------------------------------- */
    876     /**
    877      * Sets the value of an long field.
    878      *
    879      * @param name the field name
    880      * @param value the field long value
    881      */
    882     public void addLongField(Buffer name, long value)
    883     {
    884         Buffer v = BufferUtil.toBuffer(value);
    885         add(name, v, value);
    886     }
    887
    888     /* -------------------------------------------------------------- */
    889     /**
    890      * Sets the value of a date field.
    891      *
    892      * @param name the field name
    893      * @param date the field date value
    894      */
    895     public void putDateField(Buffer name, long date)
    896     {
    897         if (_dateBuffer == null)
    898         {
    899             _dateBuffer = new StringBuffer(32);
    900             _calendar = new GregorianCalendar(__GMT);
    901         }
    902         _dateBuffer.setLength(0);
    903         _calendar.setTimeInMillis(date);
    904         formatDate(_dateBuffer, _calendar, false);
    905         Buffer v = new ByteArrayBuffer(_dateBuffer.toString());
    906         put(name, v, date);
    907     }
    908
    909     /* -------------------------------------------------------------- */
    910     /**
    911      * Sets the value of a date field.
    912      *
    913      * @param name the field name
    914      * @param date the field date value
    915      */
    916     public void putDateField(String name, long date)
    917     {
    918         Buffer n = HttpHeaders.CACHE.lookup(name);
    919         putDateField(n,date);
    920     }
    921
    922     /* -------------------------------------------------------------- */
    923     /**
    924      * Sets the value of a date field.
    925      *
    926      * @param name the field name
    927      * @param date the field date value
    928      */
    929     public void addDateField(String name, long date)
    930     {
    931         if (_dateBuffer == null)
    932         {
    933             _dateBuffer = new StringBuffer(32);
    934             _calendar = new GregorianCalendar(__GMT);
    935         }
    936         _dateBuffer.setLength(0);
    937         _calendar.setTimeInMillis(date);
    938         formatDate(_dateBuffer, _calendar, false);
    939         Buffer n = HttpHeaders.CACHE.lookup(name);
    940         Buffer v = new ByteArrayBuffer(_dateBuffer.toString());
    941         add(n, v, date);
    942     }
    943
    944     /* ------------------------------------------------------------ */
    945     /**
    946      * Format a set cookie value
    947      *
    948      * @param cookie The cookie.
    949      * @param cookie2 If true, use the alternate cookie 2 header
    950      */
    951     public void addSetCookie(Cookie cookie)
    952     {
    953         String name = cookie.getName();
    954         String value = cookie.getValue();
    955         int version = cookie.getVersion();
    956
    957         // Check arguments
    958         if (name == null || name.length() == 0) throw new IllegalArgumentException("Bad cookie name");
    959
    960         // Format value and params
    961         StringBuffer buf = new StringBuffer(128);
    962         String name_value_params = null;
    963         synchronized (buf)
    964         {
    965             QuotedStringTokenizer.quoteIfNeeded(buf, name);
    966             buf.append('=');
    967             if (value != null && value.length() > 0)
    968                 QuotedStringTokenizer.quoteIfNeeded(buf, value);
    969
    970             if (version > 0)
    971             {
    972                 buf.append(";Version=");
    973                 buf.append(version);
    974                 String comment = cookie.getComment();
    975                 if (comment != null && comment.length() > 0)
    976                 {
    977                     buf.append(";Comment=");
    978                     QuotedStringTokenizer.quoteIfNeeded(buf, comment);
    979                 }
    980             }
    981             String path = cookie.getPath();
    982             if (path != null && path.length() > 0)
    983             {
    984                 buf.append(";Path=");
    985                 buf.append(URIUtil.encodePath(path));
    986             }
    987             String domain = cookie.getDomain();
    988             if (domain != null && domain.length() > 0)
    989             {
    990                 buf.append(";Domain=");
    991                 buf.append(domain.toLowerCase());// lowercase for IE
    992             }
    993
    994             long maxAge = cookie.getMaxAge();
    995             if (maxAge >= 0)
    996             {
    997                 if (version == 0)
    998                 {
    999                     buf.append(";Expires=");
    1000                     if (maxAge == 0)
    1001                         buf.append(__01Jan1970);
    1002                     else
    1003                         formatDate(buf, System.currentTimeMillis() + 1000L * maxAge, true);
    1004                 }
    1005                 else
    1006                 {
    1007                     buf.append(";Max-Age=");
    1008                     buf.append(maxAge);
    1009                 }
    1010             }
    1011             else if (version > 0)
    1012             {
    1013                 buf.append(";Discard");
    1014             }
    1015
    1016             if (cookie.getSecure())
    1017             {
    1018                 buf.append(";Secure");
    1019             }
    1020             if (cookie instanceof HttpOnlyCookie)
    1021                 buf.append(";HttpOnly");
    1022
    1023             // TODO - straight to Buffer?
    1024             name_value_params = buf.toString();
    1025         }
    1026         put(HttpHeaders.EXPIRES_BUFFER, __01Jan1970_BUFFER);
    1027         add(HttpHeaders.SET_COOKIE_BUFFER, new ByteArrayBuffer(name_value_params));
    1028     }
    1029
    1030     /* -------------------------------------------------------------- */
    1031     public void put(Buffer buffer) throws IOException
    1032     {
    1033         for (int i = 0; i < _fields.size(); i++)
    1034         {
    1035             Field field = (Field) _fields.get(i);
    1036             if (field != null && field._revision == _revision) field.put(buffer);
    1037         }
    1038         BufferUtil.putCRLF(buffer);
    1039     }
    1040
    1041     /* -------------------------------------------------------------- */
    1042     public String toString()
    1043     {
    1044         try
    1045         {
    1046             ByteArrayBuffer buffer = new ByteArrayBuffer(4096);
    1047             put(buffer);
    1048             return BufferUtil.to8859_1_String(buffer);
    1049         }
    1050         catch (Exception e)
    1051         {
    1052             e.printStackTrace();
    1053         }
    1054
    1055         return null;
    1056     }
    1057
    1058     /* ------------------------------------------------------------ */
    1059     /**
    1060      * Clear the header.
    1061      */
    1062     public void clear()
    1063     {
    1064         _revision++;
    1065         if (_revision > 1000000)
    1066         {
    1067             _revision = 0;
    1068             for (int i = _fields.size(); i-- > 0;)
    1069             {
    1070                 Field field = (Field) _fields.get(i);
    1071                 if (field != null) field.clear();
    1072             }
    1073         }
    1074     }
    1075
    1076     /* ------------------------------------------------------------ */
    1077     /**
    1078      * Destroy the header. Help the garbage collector by null everything that we can.
    1079      */
    1080     public void destroy()
    1081     {
    1082         if (_fields != null)
    1083         {
    1084             for (int i = _fields.size(); i-- > 0;)
    1085             {
    1086                 Field field = (Field) _fields.get(i);
    1087                 if (field != null) field.destroy();
    1088             }
    1089         }
    1090         _fields = null;
    1091         _dateBuffer = null;
    1092         _calendar = null;
    1093         _dateReceive = null;
    1094     }
    1095
    1096     /* ------------------------------------------------------------ */
    1097     /**
    1098      * Add fields from another HttpFields instance. Single valued fields are replaced, while all
    1099      * others are added.
    1100      *
    1101      * @param fields
    1102      */
    1103     public void add(HttpFields fields)
    1104     {
    1105         if (fields == null) return;
    1106
    1107         Enumeration e = fields.getFieldNames();
    1108         while (e.hasMoreElements())
    1109         {
    1110             String name = (String) e.nextElement();
    1111             Enumeration values = fields.getValues(name);
    1112             while (values.hasMoreElements())
    1113                 add(name, (String) values.nextElement());
    1114         }
    1115     }
    1116
    1117     /* ------------------------------------------------------------ */
    1118     /**
    1119      * Get field value parameters. Some field values can have parameters. This method separates the
    1120      * value from the parameters and optionally populates a map with the paramters. For example:
    1121      *
    1122      * <PRE>
    1123      *
    1124      * FieldName : Value ; param1=val1 ; param2=val2
    1125      *
    1126      * </PRE>
    1127      *
    1128      * @param value The Field value, possibly with parameteres.
    1129      * @param parameters A map to populate with the parameters, or null
    1130      * @return The value.
    1131      */
    1132     public static String valueParameters(String value, Map parameters)
    1133     {
    1134         if (value == null) return null;
    1135
    1136         int i = value.indexOf(';');
    1137         if (i < 0) return value;
    1138         if (parameters == null) return value.substring(0, i).trim();
    1139
    1140         StringTokenizer tok1 = new QuotedStringTokenizer(value.substring(i), ";", false, true);
    1141         while (tok1.hasMoreTokens())
    1142         {
    1143             String token = tok1.nextToken();
    1144             StringTokenizer tok2 = new QuotedStringTokenizer(token, "= ");
    1145             if (tok2.hasMoreTokens())
    1146             {
    1147                 String paramName = tok2.nextToken();
    1148                 String paramVal = null;
    1149                 if (tok2.hasMoreTokens()) paramVal = tok2.nextToken();
    1150                 parameters.put(paramName, paramVal);
    1151             }
    1152         }
    1153
    1154         return value.substring(0, i).trim();
    1155     }
    1156
    1157     /* ------------------------------------------------------------ */
    1158     private static Float __one = new Float("1.0");
    1159     private static Float __zero = new Float("0.0");
    1160     private static StringMap __qualities = new StringMap();
    1161     static
    1162     {
    1163         __qualities.put(null, __one);
    1164         __qualities.put("1.0", __one);
    1165         __qualities.put("1", __one);
    1166         __qualities.put("0.9", new Float("0.9"));
    1167         __qualities.put("0.8", new Float("0.8"));
    1168         __qualities.put("0.7", new Float("0.7"));
    1169         __qualities.put("0.66", new Float("0.66"));
    1170         __qualities.put("0.6", new Float("0.6"));
    1171         __qualities.put("0.5", new Float("0.5"));
    1172         __qualities.put("0.4", new Float("0.4"));
    1173         __qualities.put("0.33", new Float("0.33"));
    1174         __qualities.put("0.3", new Float("0.3"));
    1175         __qualities.put("0.2", new Float("0.2"));
    1176         __qualities.put("0.1", new Float("0.1"));
    1177         __qualities.put("0", __zero);
    1178         __qualities.put("0.0", __zero);
    1179     }
    1180
    1181     /* ------------------------------------------------------------ */
    1182     public static Float getQuality(String value)
    1183     {
    1184         if (value == null) return __zero;
    1185
    1186         int qe = value.indexOf(";");
    1187         if (qe++ < 0 || qe == value.length()) return __one;
    1188
    1189         if (value.charAt(qe++) == 'q')
    1190         {
    1191             qe++;
    1192             Map.Entry entry = __qualities.getEntry(value, qe, value.length() - qe);
    1193             if (entry != null) return (Float) entry.getValue();
    1194         }
    1195
    1196         HashMap params = new HashMap(3);
    1197         valueParameters(value, params);
    1198         String qs = (String) params.get("q");
    1199         Float q = (Float) __qualities.get(qs);
    1200         if (q == null)
    1201         {
    1202             try
    1203             {
    1204                 q = new Float(qs);
    1205             }
    1206             catch (Exception e)
    1207             {
    1208                 q = __one;
    1209             }
    1210         }
    1211         return q;
    1212     }
    1213
    1214     /* ------------------------------------------------------------ */
    1215     /**
    1216      * List values in quality order.
    1217      *
    1218      * @param enum Enumeration of values with quality parameters
    1219      * @return values in quality order.
    1220      */
    1221     public static List qualityList(Enumeration e)
    1222     {
    1223         if (e == null || !e.hasMoreElements()) return Collections.EMPTY_LIST;
    1224
    1225         Object list = null;
    1226         Object qual = null;
    1227
    1228         // Assume list will be well ordered and just add nonzero
    1229         while (e.hasMoreElements())
    1230         {
    1231             String v = e.nextElement().toString();
    1232             Float q = getQuality(v);
    1233
    1234             if (q.floatValue() >= 0.001)
    1235             {
    1236                 list = LazyList.add(list, v);
    1237                 qual = LazyList.add(qual, q);
    1238             }
    1239         }
    1240
    1241         List vl = LazyList.getList(list, false);
    1242         if (vl.size() < 2) return vl;
    1243
    1244         List ql = LazyList.getList(qual, false);
    1245
    1246         // sort list with swaps
    1247         Float last = __zero;
    1248         for (int i = vl.size(); i-- > 0;)
    1249         {
    1250             Float q = (Float) ql.get(i);
    1251             if (last.compareTo(q) > 0)
    1252             {
    1253                 Object tmp = vl.get(i);
    1254                 vl.set(i, vl.get(i + 1));
    1255                 vl.set(i + 1, tmp);
    1256                 ql.set(i, ql.get(i + 1));
    1257                 ql.set(i + 1, q);
    1258                 last = __zero;
    1259                 i = vl.size();
    1260                 continue;
    1261             }
    1262             last = q;
    1263         }
    1264         ql.clear();
    1265         return vl;
    1266     }
    1267
    1268     /* ------------------------------------------------------------ */
    1269     /* ------------------------------------------------------------ */
    1270     /* ------------------------------------------------------------ */
    1271     public static final class Field
    1272     {
    1273         private Buffer _name;
    1274         private Buffer _value;
    1275         private String _stringValue;
    1276         private long _numValue;
    1277         private Field _next;
    1278         private Field _prev;
    1279         private int _revision;
    1280
    1281         /* ------------------------------------------------------------ */
    1282         private Field(Buffer name, Buffer value, long numValue, int revision)
    1283         {
    1284             _name = name.asImmutableBuffer();
    1285             _value = value.isImmutable() ? value : new View(value);
    1286             _next = null;
    1287             _prev = null;
    1288             _revision = revision;
    1289             _numValue = numValue;
    1290             _stringValue=null;
    1291         }
    1292
    1293         /* ------------------------------------------------------------ */
    1294         private void clear()
    1295         {
    1296             _revision = -1;
    1297         }
    1298
    1299         /* ------------------------------------------------------------ */
    1300         private void destroy()
    1301         {
    1302             _name = null;
    1303             _value = null;
    1304             _next = null;
    1305             _prev = null;
    1306             _stringValue=null;
    1307         }
    1308
    1309         /* ------------------------------------------------------------ */
    1310         /**
    1311          * Reassign a value to this field. Checks if the string value is the same as that in the char
    1312          * array, if so then just reuse existing value.
    1313          */
    1314         private void reset(Buffer value, long numValue, int revision)
    1315         {
    1316             _revision = revision;
    1317             if (_value == null)
    1318             {
    1319                 _value = value.isImmutable() ? value : new View(value);
    1320                 _numValue = numValue;
    1321                 _stringValue=null;
    1322             }
    1323             else if (value.isImmutable())
    1324             {
    1325                 _value = value;
    1326                 _numValue = numValue;
    1327                 _stringValue=null;
    1328             }
    1329             else
    1330             {
    1331                 if (_value instanceof View)
    1332                     ((View) _value).update(value);
    1333                 else
    1334                     _value = new View(value);
    1335                 _numValue = numValue;
    1336
    1337                 // check to see if string value is still valid.
    1338                 if (_stringValue!=null)
    1339                 {
    1340                     if (_stringValue.length()!=value.length())
    1341                         _stringValue=null;
    1342                     else
    1343                     {
    1344                         for (int i=value.length();i-->0;)
    1345                         {
    1346                             if (value.peek(value.getIndex()+i)!=_stringValue.charAt(i))
    1347                             {
    1348                                 _stringValue=null;
    1349                                 break;
    1350                             }
    1351                         }
    1352                     }
    1353                 }
    1354             }
    1355         }
    1356
    1357
    1358
    1359         /* ------------------------------------------------------------ */
    1360         public void put(Buffer buffer) throws IOException
    1361         {
    1362             int o=(_name instanceof CachedBuffer)?((CachedBuffer)_name).getOrdinal():-1;
    1363             if (o>=0)
    1364                 buffer.put(_name);
    1365             else
    1366             {
    1367                 int s=_name.getIndex();
    1368                 int e=_name.putIndex();
    1369                 while (s<e)
    1370                 {
    1371                     byte b=_name.peek(s++);
    1372                     switch(b)
    1373                     {
    1374                         case '\r':
    1375                         case '\n':
    1376                         case ':' :
    1377                             continue;
    1378                         default:
    1379                             buffer.put(b);
    1380                     }
    1381                 }
    1382             }
    1383
    1384             buffer.put((byte) ':');
    1385             buffer.put((byte) ' ');
    1386
    1387             o=(_value instanceof CachedBuffer)?((CachedBuffer)_value).getOrdinal():-1;
    1388             if (o>=0 || _numValue>=0)
    1389                 buffer.put(_value);
    1390             else
    1391             {
    1392                 int s=_value.getIndex();
    1393                 int e=_value.putIndex();
    1394                 while (s<e)
    1395                 {
    1396                     byte b=_value.peek(s++);
    1397                     switch(b)
    1398                     {
    1399                         case '\r':
    1400                         case '\n':
    1401                             continue;
    1402                         default:
    1403                             buffer.put(b);
    1404                     }
    1405                 }
    1406             }
    1407
    1408             BufferUtil.putCRLF(buffer);
    1409         }
    1410
    1411         /* ------------------------------------------------------------ */
    1412         public String getName()
    1413         {
    1414             return BufferUtil.to8859_1_String(_name);
    1415         }
    1416
    1417         /* ------------------------------------------------------------ */
    1418         Buffer getNameBuffer()
    1419         {
    1420             return _name;
    1421         }
    1422
    1423         /* ------------------------------------------------------------ */
    1424         public int getNameOrdinal()
    1425         {
    1426             return HttpHeaders.CACHE.getOrdinal(_name);
    1427         }
    1428
    1429         /* ------------------------------------------------------------ */
    1430         public String getValue()
    1431         {
    1432             if (_stringValue==null)
    1433                 _stringValue=BufferUtil.to8859_1_String(_value);
    1434             return _stringValue;
    1435         }
    1436
    1437         /* ------------------------------------------------------------ */
    1438         public Buffer getValueBuffer()
    1439         {
    1440             return _value;
    1441         }
    1442
    1443         /* ------------------------------------------------------------ */
    1444         public int getValueOrdinal()
    1445         {
    1446             return HttpHeaderValues.CACHE.getOrdinal(_value);
    1447         }
    1448
    1449         /* ------------------------------------------------------------ */
    1450         public int getIntValue()
    1451         {
    1452             return (int) getLongValue();
    1453         }
    1454
    1455         /* ------------------------------------------------------------ */
    1456         public long getLongValue()
    1457         {
    1458             if (_numValue == -1) _numValue = BufferUtil.toLong(_value);
    1459             return _numValue;
    1460         }
    1461
    1462         /* ------------------------------------------------------------ */
    1463         public String toString()
    1464         {
    1465             return ("[" + (_prev == null ? "" : "<-") + getName() + "="+_revision+"=" + _value + (_next == null ? "" : "->") + "]");
    1466         }
    1467     }
    1468
    1469 }