0001: // ========================================================================
0002: // Copyright 2004-2005 Mort Bay Consulting Pty. Ltd.
0003: // ------------------------------------------------------------------------
0004: // Licensed under the Apache License, Version 2.0 (the "License");
0005: // you may not use this file except in compliance with the License.
0006: // You may obtain a copy of the License at
0007: // http://www.apache.org/licenses/LICENSE-2.0
0008: // Unless required by applicable law or agreed to in writing, software
0009: // distributed under the License is distributed on an "AS IS" BASIS,
0010: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0011: // See the License for the specific language governing permissions and
0012: // limitations under the License.
0013: // ========================================================================
0014:
0015: package org.mortbay.jetty;
0016:
0017: import java.io.IOException;
0018: import java.io.UnsupportedEncodingException;
0019: import java.io.Writer;
0020: import java.net.URLEncoder;
0021: import java.text.SimpleDateFormat;
0022: import java.util.ArrayList;
0023: import java.util.Calendar;
0024: import java.util.Collections;
0025: import java.util.Date;
0026: import java.util.Enumeration;
0027: import java.util.GregorianCalendar;
0028: import java.util.HashMap;
0029: import java.util.Iterator;
0030: import java.util.List;
0031: import java.util.Locale;
0032: import java.util.Map;
0033: import java.util.NoSuchElementException;
0034: import java.util.StringTokenizer;
0035: import java.util.TimeZone;
0036:
0037: import javax.servlet.http.Cookie;
0038:
0039: import org.mortbay.io.Buffer;
0040: import org.mortbay.io.BufferCache;
0041: import org.mortbay.io.BufferUtil;
0042: import org.mortbay.io.ByteArrayBuffer;
0043: import org.mortbay.io.View;
0044: import org.mortbay.util.DateCache;
0045: import org.mortbay.util.LazyList;
0046: import org.mortbay.util.QuotedStringTokenizer;
0047: import org.mortbay.util.StringMap;
0048: import org.mortbay.util.StringUtil;
0049:
0050: /* ------------------------------------------------------------ */
0051: /**
0052: * HTTP Fields. A collection of HTTP header and or Trailer fields. This class is not synchronized
0053: * and needs to be protected from concurrent access.
0054: *
0055: * This class is not synchronized as it is expected that modifications will only be performed by a
0056: * single thread.
0057: *
0058: * @author Greg Wilkins (gregw)
0059: */
0060: public class HttpFields {
0061: /* ------------------------------------------------------------ */
0062: public final static String __separators = ", \t";
0063:
0064: /* ------------------------------------------------------------ */
0065: private static String[] DAYS = { "Sat", "Sun", "Mon", "Tue", "Wed",
0066: "Thu", "Fri", "Sat" };
0067: private static String[] MONTHS = { "Jan", "Feb", "Mar", "Apr",
0068: "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
0069: "Jan" };
0070:
0071: /* ------------------------------------------------------------ */
0072: /**
0073: * Format HTTP date "EEE, dd MMM yyyy HH:mm:ss 'GMT'" or "EEE, dd-MMM-yy HH:mm:ss 'GMT'"for
0074: * cookies
0075: */
0076: public static String formatDate(long date, boolean cookie) {
0077: StringBuffer buf = new StringBuffer(32);
0078: GregorianCalendar gc = new GregorianCalendar(__GMT);
0079: gc.setTimeInMillis(date);
0080: formatDate(buf, gc, cookie);
0081: return buf.toString();
0082: }
0083:
0084: /* ------------------------------------------------------------ */
0085: /**
0086: * Format HTTP date "EEE, dd MMM yyyy HH:mm:ss 'GMT'" or "EEE, dd-MMM-yy HH:mm:ss 'GMT'"for
0087: * cookies
0088: */
0089: public static String formatDate(Calendar calendar, boolean cookie) {
0090: StringBuffer buf = new StringBuffer(32);
0091: formatDate(buf, calendar, cookie);
0092: return buf.toString();
0093: }
0094:
0095: /* ------------------------------------------------------------ */
0096: /**
0097: * Format HTTP date "EEE, dd MMM yyyy HH:mm:ss 'GMT'" or "EEE, dd-MMM-yy HH:mm:ss 'GMT'"for
0098: * cookies
0099: */
0100: public static String formatDate(StringBuffer buf, long date,
0101: boolean cookie) {
0102: GregorianCalendar gc = new GregorianCalendar(__GMT);
0103: gc.setTimeInMillis(date);
0104: formatDate(buf, gc, cookie);
0105: return buf.toString();
0106: }
0107:
0108: /* ------------------------------------------------------------ */
0109: /**
0110: * Format HTTP date "EEE, dd MMM yyyy HH:mm:ss 'GMT'" or "EEE, dd-MMM-yy HH:mm:ss 'GMT'"for
0111: * cookies
0112: */
0113: public static void formatDate(StringBuffer buf, Calendar calendar,
0114: boolean cookie) {
0115: // "EEE, dd MMM yyyy HH:mm:ss 'GMT'"
0116: // "EEE, dd-MMM-yy HH:mm:ss 'GMT'", cookie
0117:
0118: int day_of_week = calendar.get(Calendar.DAY_OF_WEEK);
0119: int day_of_month = calendar.get(Calendar.DAY_OF_MONTH);
0120: int month = calendar.get(Calendar.MONTH);
0121: int year = calendar.get(Calendar.YEAR);
0122: int century = year / 100;
0123: year = year % 100;
0124:
0125: int epoch = (int) ((calendar.getTimeInMillis() / 1000) % (60 * 60 * 24));
0126: int seconds = epoch % 60;
0127: epoch = epoch / 60;
0128: int minutes = epoch % 60;
0129: int hours = epoch / 60;
0130:
0131: buf.append(DAYS[day_of_week]);
0132: buf.append(',');
0133: buf.append(' ');
0134: StringUtil.append2digits(buf, day_of_month);
0135:
0136: if (cookie) {
0137: buf.append('-');
0138: buf.append(MONTHS[month]);
0139: buf.append('-');
0140: StringUtil.append2digits(buf, year);
0141: } else {
0142: buf.append(' ');
0143: buf.append(MONTHS[month]);
0144: buf.append(' ');
0145: StringUtil.append2digits(buf, century);
0146: StringUtil.append2digits(buf, year);
0147: }
0148: buf.append(' ');
0149: StringUtil.append2digits(buf, hours);
0150: buf.append(':');
0151: StringUtil.append2digits(buf, minutes);
0152: buf.append(':');
0153: StringUtil.append2digits(buf, seconds);
0154: buf.append(" GMT");
0155: }
0156:
0157: /* -------------------------------------------------------------- */
0158: private static TimeZone __GMT = TimeZone.getTimeZone("GMT");
0159: public final static DateCache __dateCache = new DateCache(
0160: "EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
0161:
0162: /* ------------------------------------------------------------ */
0163: private final static String __dateReceiveFmt[] = {
0164: "EEE, dd MMM yyyy HH:mm:ss zzz", "EEE, dd-MMM-yy HH:mm:ss",
0165: "EEE MMM dd HH:mm:ss yyyy",
0166:
0167: "EEE, dd MMM yyyy HH:mm:ss",
0168: "EEE dd MMM yyyy HH:mm:ss zzz", "EEE dd MMM yyyy HH:mm:ss",
0169: "EEE MMM dd yyyy HH:mm:ss zzz", "EEE MMM dd yyyy HH:mm:ss",
0170: "EEE MMM-dd-yyyy HH:mm:ss zzz", "EEE MMM-dd-yyyy HH:mm:ss",
0171: "dd MMM yyyy HH:mm:ss zzz", "dd MMM yyyy HH:mm:ss",
0172: "dd-MMM-yy HH:mm:ss zzz", "dd-MMM-yy HH:mm:ss",
0173: "MMM dd HH:mm:ss yyyy zzz", "MMM dd HH:mm:ss yyyy",
0174: "EEE MMM dd HH:mm:ss yyyy zzz",
0175: "EEE, MMM dd HH:mm:ss yyyy zzz",
0176: "EEE, MMM dd HH:mm:ss yyyy", "EEE, dd-MMM-yy HH:mm:ss zzz",
0177: "EEE dd-MMM-yy HH:mm:ss zzz", "EEE dd-MMM-yy HH:mm:ss", };
0178: private static int __dateReceiveInit = 3;
0179: private static SimpleDateFormat __dateReceive[];
0180: static {
0181: __GMT.setID("GMT");
0182: __dateCache.setTimeZone(__GMT);
0183: __dateReceive = new SimpleDateFormat[__dateReceiveFmt.length];
0184: // Initialize only the standard formats here.
0185: for (int i = 0; i < __dateReceiveInit; i++) {
0186: __dateReceive[i] = new SimpleDateFormat(
0187: __dateReceiveFmt[i], Locale.US);
0188: __dateReceive[i].setTimeZone(__GMT);
0189: }
0190: }
0191: public final static String __01Jan1970 = formatDate(0, false);
0192: public final static Buffer __01Jan1970_BUFFER = new ByteArrayBuffer(
0193: __01Jan1970);
0194:
0195: /* -------------------------------------------------------------- */
0196: protected ArrayList _fields = new ArrayList(20);
0197: protected int _revision;
0198: protected HashMap _bufferMap = new HashMap(32);
0199: protected SimpleDateFormat _dateReceive[] = new SimpleDateFormat[__dateReceive.length];
0200: private StringBuffer _dateBuffer;
0201: private Calendar _calendar;
0202:
0203: /* ------------------------------------------------------------ */
0204: /**
0205: * Constructor.
0206: */
0207: public HttpFields() {
0208: }
0209:
0210: /* -------------------------------------------------------------- */
0211: /**
0212: * Get enumeration of header _names. Returns an enumeration of strings representing the header
0213: * _names for this request.
0214: */
0215: public Enumeration getFieldNames() {
0216: return new Enumeration() {
0217: int i = 0;
0218: Field field = null;
0219:
0220: public boolean hasMoreElements() {
0221: if (field != null)
0222: return true;
0223: while (i < _fields.size()) {
0224: Field f = (Field) _fields.get(i++);
0225: if (f != null && f._prev == null
0226: && f._revision == _revision) {
0227: field = f;
0228: return true;
0229: }
0230: }
0231: return false;
0232: }
0233:
0234: public Object nextElement() throws NoSuchElementException {
0235: if (field != null || hasMoreElements()) {
0236: String n = field._name.toString();
0237: field = null;
0238: return n;
0239: }
0240: throw new NoSuchElementException();
0241: }
0242: };
0243: }
0244:
0245: /* -------------------------------------------------------------- */
0246: /**
0247: * Get enumeration of Fields Returns an enumeration of Fields for this request.
0248: */
0249: public Iterator getFields() {
0250: return new Iterator() {
0251: int i = 0;
0252: Field field = null;
0253:
0254: public boolean hasNext() {
0255: if (field != null)
0256: return true;
0257: while (i < _fields.size()) {
0258: Field f = (Field) _fields.get(i++);
0259: if (f != null && f._revision == _revision) {
0260: field = f;
0261: return true;
0262: }
0263: }
0264: return false;
0265: }
0266:
0267: public Object next() {
0268: if (field != null || hasNext()) {
0269: final Field f = field;
0270: field = null;
0271: return f;
0272: }
0273: throw new NoSuchElementException();
0274: }
0275:
0276: public void remove() {
0277: throw new UnsupportedOperationException();
0278: }
0279: };
0280: }
0281:
0282: /* ------------------------------------------------------------ */
0283: private Field getField(String name) {
0284: return (Field) _bufferMap.get(HttpHeaders.CACHE.lookup(name));
0285: }
0286:
0287: /* ------------------------------------------------------------ */
0288: private Field getField(Buffer name) {
0289: return (Field) _bufferMap.get(name);
0290: }
0291:
0292: /* ------------------------------------------------------------ */
0293: public boolean containsKey(Buffer name) {
0294: Field f = getField(name);
0295: return (f != null && f._revision == _revision);
0296: }
0297:
0298: /* ------------------------------------------------------------ */
0299: public boolean containsKey(String name) {
0300: Field f = getField(name);
0301: return (f != null && f._revision == _revision);
0302: }
0303:
0304: /* -------------------------------------------------------------- */
0305: /**
0306: * @return the value of a field, or null if not found. For multiple fields of the same name,
0307: * only the first is returned.
0308: * @param name the case-insensitive field name
0309: */
0310: public String getStringField(String name) {
0311: // TODO - really reuse strings from previous requests!
0312: Field field = getField(name);
0313: if (field != null && field._revision == _revision)
0314: return field.getValue();
0315: return null;
0316: }
0317:
0318: /* -------------------------------------------------------------- */
0319: /**
0320: * @return the value of a field, or null if not found. For multiple fields of the same name,
0321: * only the first is returned.
0322: * @param name the case-insensitive field name
0323: */
0324: public String getStringField(Buffer name) {
0325: // TODO - really reuse strings from previous requests!
0326: Field field = getField(name);
0327: if (field != null && field._revision == _revision)
0328: return field._value.toString();
0329: return null;
0330: }
0331:
0332: /* -------------------------------------------------------------- */
0333: /**
0334: * @return the value of a field, or null if not found. For multiple fields of the same name,
0335: * only the first is returned.
0336: * @param name the case-insensitive field name
0337: */
0338: public Buffer get(Buffer name) {
0339: Field field = getField(name);
0340: if (field != null && field._revision == _revision)
0341: return field._value;
0342: return null;
0343: }
0344:
0345: /* -------------------------------------------------------------- */
0346: /**
0347: * Get multi headers
0348: *
0349: * @return Enumeration of the values, or null if no such header.
0350: * @param name the case-insensitive field name
0351: */
0352: public Enumeration getValues(String name) {
0353: final Field field = getField(name);
0354: if (field == null)
0355: return null;
0356:
0357: return new Enumeration() {
0358: Field f = field;
0359:
0360: public boolean hasMoreElements() {
0361: while (f != null && f._revision != _revision)
0362: f = f._next;
0363: return f != null;
0364: }
0365:
0366: public Object nextElement() throws NoSuchElementException {
0367: if (f == null)
0368: throw new NoSuchElementException();
0369: Field n = f;
0370: do
0371: f = f._next;
0372: while (f != null && f._revision != _revision);
0373: return n.getValue();
0374: }
0375: };
0376: }
0377:
0378: /* -------------------------------------------------------------- */
0379: /**
0380: * Get multi headers
0381: *
0382: * @return Enumeration of the value Strings, or null if no such header.
0383: * @param name the case-insensitive field name
0384: */
0385: public Enumeration getValues(Buffer name) {
0386: final Field field = getField(name);
0387:
0388: if (field == null)
0389: return null;
0390:
0391: return new Enumeration() {
0392: Field f = field;
0393:
0394: public boolean hasMoreElements() {
0395: while (f != null && f._revision != _revision)
0396: f = f._next;
0397: return f != null;
0398: }
0399:
0400: public Object nextElement() throws NoSuchElementException {
0401: if (f == null)
0402: throw new NoSuchElementException();
0403: Field n = f;
0404: f = f._next;
0405: while (f != null && f._revision != _revision)
0406: f = f._next;
0407: return n.getValue();
0408: }
0409: };
0410: }
0411:
0412: /* -------------------------------------------------------------- */
0413: /**
0414: * Get multi field values with separator. The multiple values can be represented as separate
0415: * headers of the same name, or by a single header using the separator(s), or a combination of
0416: * both. Separators may be quoted.
0417: *
0418: * @param name the case-insensitive field name
0419: * @param separators String of separators.
0420: * @return Enumeration of the values, or null if no such header.
0421: */
0422: public Enumeration getValues(String name, final String separators) {
0423: final Enumeration e = getValues(name);
0424: if (e == null)
0425: return null;
0426: return new Enumeration() {
0427: QuotedStringTokenizer tok = null;
0428:
0429: public boolean hasMoreElements() {
0430: if (tok != null && tok.hasMoreElements())
0431: return true;
0432: while (e.hasMoreElements()) {
0433: String value = (String) e.nextElement();
0434: tok = new QuotedStringTokenizer(value, separators,
0435: false, false);
0436: if (tok.hasMoreElements())
0437: return true;
0438: }
0439: tok = null;
0440: return false;
0441: }
0442:
0443: public Object nextElement() throws NoSuchElementException {
0444: if (!hasMoreElements())
0445: throw new NoSuchElementException();
0446: String next = (String) tok.nextElement();
0447: if (next != null)
0448: next = next.trim();
0449: return next;
0450: }
0451: };
0452: }
0453:
0454: /* -------------------------------------------------------------- */
0455: /**
0456: * Set a field.
0457: *
0458: * @param name the name of the field
0459: * @param value the value of the field. If null the field is cleared.
0460: */
0461: public void put(String name, String value) {
0462: Buffer n = HttpHeaders.CACHE.lookup(name);
0463: Buffer v = null;
0464: if (value != null)
0465: v = HttpHeaderValues.CACHE.lookup(value);
0466: put(n, v, -1);
0467: }
0468:
0469: /* -------------------------------------------------------------- */
0470: /**
0471: * Set a field.
0472: *
0473: * @param name the name of the field
0474: * @param value the value of the field. If null the field is cleared.
0475: */
0476: public void put(Buffer name, String value) {
0477: Buffer v = HttpHeaderValues.CACHE.lookup(value);
0478: put(name, v, -1);
0479: }
0480:
0481: /* -------------------------------------------------------------- */
0482: /**
0483: * Set a field.
0484: *
0485: * @param name the name of the field
0486: * @param value the value of the field. If null the field is cleared.
0487: */
0488: public void put(Buffer name, Buffer value) {
0489: put(name, value, -1);
0490: }
0491:
0492: /* -------------------------------------------------------------- */
0493: /**
0494: * Set a field.
0495: *
0496: * @param name the name of the field
0497: * @param value the value of the field. If null the field is cleared.
0498: */
0499: private void put(Buffer name, Buffer value, long numValue) {
0500: if (value == null) {
0501: remove(name);
0502: return;
0503: }
0504:
0505: if (!(name instanceof BufferCache.CachedBuffer))
0506: name = HttpHeaders.CACHE.lookup(name);
0507:
0508: Field field = (Field) _bufferMap.get(name);
0509:
0510: // Look for value to replace.
0511: if (field != null) {
0512: field.reset(value, numValue, _revision);
0513: field = field._next;
0514: while (field != null) {
0515: field.clear();
0516: field = field._next;
0517: }
0518: return;
0519: } else {
0520: // new value;
0521: field = new Field(name, value, numValue, _revision);
0522: _fields.add(field);
0523: _bufferMap.put(field.getNameBuffer(), field);
0524: }
0525: }
0526:
0527: /* -------------------------------------------------------------- */
0528: /**
0529: * Set a field.
0530: *
0531: * @param name the name of the field
0532: * @param list the List value of the field. If null the field is cleared.
0533: */
0534: public void put(String name, List list) {
0535: if (list == null || list.size() == 0) {
0536: remove(name);
0537: return;
0538: }
0539: Buffer n = HttpHeaders.CACHE.lookup(name);
0540:
0541: Object v = list.get(0);
0542: if (v != null)
0543: put(n, HttpHeaderValues.CACHE.lookup(v.toString()));
0544: else
0545: remove(n);
0546:
0547: if (list.size() > 1) {
0548: java.util.Iterator iter = list.iterator();
0549: iter.next();
0550: while (iter.hasNext()) {
0551: v = iter.next();
0552: if (v != null)
0553: put(n, HttpHeaderValues.CACHE.lookup(v.toString()));
0554: }
0555: }
0556: }
0557:
0558: /* -------------------------------------------------------------- */
0559: /**
0560: * Add to or set a field. If the field is allowed to have multiple values, add will add multiple
0561: * headers of the same name.
0562: *
0563: * @param name the name of the field
0564: * @param value the value of the field.
0565: * @exception IllegalArgumentException If the name is a single valued field and already has a
0566: * value.
0567: */
0568: public void add(String name, String value)
0569: throws IllegalArgumentException {
0570: Buffer n = HttpHeaders.CACHE.lookup(name);
0571: Buffer v = HttpHeaderValues.CACHE.lookup(value);
0572: add(n, v, -1);
0573: }
0574:
0575: /* -------------------------------------------------------------- */
0576: /**
0577: * Add to or set a field. If the field is allowed to have multiple values, add will add multiple
0578: * headers of the same name.
0579: *
0580: * @param name the name of the field
0581: * @param value the value of the field.
0582: * @exception IllegalArgumentException If the name is a single valued field and already has a
0583: * value.
0584: */
0585: public void add(Buffer name, Buffer value)
0586: throws IllegalArgumentException {
0587: add(name, value, -1);
0588: }
0589:
0590: /* -------------------------------------------------------------- */
0591: /**
0592: * Add to or set a field. If the field is allowed to have multiple values, add will add multiple
0593: * headers of the same name.
0594: *
0595: * @param name the name of the field
0596: * @param value the value of the field.
0597: * @exception IllegalArgumentException If the name is a single valued field and already has a
0598: * value.
0599: */
0600: private void add(Buffer name, Buffer value, long numValue)
0601: throws IllegalArgumentException {
0602: if (value == null)
0603: throw new IllegalArgumentException("null value");
0604:
0605: if (!(name instanceof BufferCache.CachedBuffer))
0606: name = HttpHeaders.CACHE.lookup(name);
0607:
0608: Field field = (Field) _bufferMap.get(name);
0609: Field last = null;
0610: if (field != null) {
0611: while (field != null && field._revision == _revision) {
0612: last = field;
0613: field = field._next;
0614: }
0615: }
0616:
0617: if (field != null)
0618: field.reset(value, numValue, _revision);
0619: else {
0620: // create the field
0621: field = new Field(name, value, numValue, _revision);
0622:
0623: // look for chain to add too
0624: if (last != null) {
0625: field._prev = last;
0626: last._next = field;
0627: } else
0628: _bufferMap.put(field.getNameBuffer(), field);
0629:
0630: _fields.add(field);
0631: }
0632: }
0633:
0634: /* ------------------------------------------------------------ */
0635: /**
0636: * Remove a field.
0637: *
0638: * @param name
0639: */
0640: public void remove(String name) {
0641: remove(HttpHeaders.CACHE.lookup(name));
0642: }
0643:
0644: /* ------------------------------------------------------------ */
0645: /**
0646: * Remove a field.
0647: *
0648: * @param name
0649: */
0650: public void remove(Buffer name) {
0651: Field field = (Field) _bufferMap.get(name);
0652:
0653: if (field != null) {
0654: while (field != null) {
0655: field.clear();
0656: field = field._next;
0657: }
0658: }
0659: }
0660:
0661: /* -------------------------------------------------------------- */
0662: /**
0663: * Get a header as an long value. Returns the value of an integer field or -1 if not found. The
0664: * case of the field name is ignored.
0665: *
0666: * @param name the case-insensitive field name
0667: * @exception NumberFormatException If bad long found
0668: */
0669: public long getLongField(String name) throws NumberFormatException {
0670: Field field = getField(name);
0671: if (field != null && field._revision == _revision)
0672: return field.getLongValue();
0673:
0674: return -1L;
0675: }
0676:
0677: /* -------------------------------------------------------------- */
0678: /**
0679: * Get a header as an long value. Returns the value of an integer field or -1 if not found. The
0680: * case of the field name is ignored.
0681: *
0682: * @param name the case-insensitive field name
0683: * @exception NumberFormatException If bad long found
0684: */
0685: public long getLongField(Buffer name) throws NumberFormatException {
0686: Field field = getField(name);
0687: if (field != null && field._revision == _revision)
0688: return field.getLongValue();
0689: return -1L;
0690: }
0691:
0692: /* -------------------------------------------------------------- */
0693: /**
0694: * Get a header as a date value. Returns the value of a date field, or -1 if not found. The case
0695: * of the field name is ignored.
0696: *
0697: * @param name the case-insensitive field name
0698: */
0699: public long getDateField(String name) {
0700: Field field = getField(name);
0701: if (field == null || field._revision != _revision)
0702: return -1;
0703:
0704: if (field._numValue != -1)
0705: return field._numValue;
0706:
0707: String val = valueParameters(field._value.toString(), null);
0708: if (val == null)
0709: return -1;
0710:
0711: for (int i = 0; i < __dateReceiveInit; i++) {
0712: if (_dateReceive[i] == null)
0713: _dateReceive[i] = (SimpleDateFormat) __dateReceive[i]
0714: .clone();
0715:
0716: try {
0717: Date date = (Date) _dateReceive[i].parseObject(val);
0718: return field._numValue = date.getTime();
0719: } catch (java.lang.Exception e) {
0720: }
0721: }
0722: if (val.endsWith(" GMT")) {
0723: val = val.substring(0, val.length() - 4);
0724: for (int i = 0; i < __dateReceiveInit; i++) {
0725: try {
0726: Date date = (Date) _dateReceive[i].parseObject(val);
0727: return field._numValue = date.getTime();
0728: } catch (java.lang.Exception e) {
0729: }
0730: }
0731: }
0732:
0733: // The standard formats did not work. So we will lock the common format array
0734: // and look at lazily creating the non-standard formats
0735: synchronized (__dateReceive) {
0736: for (int i = __dateReceiveInit; i < _dateReceive.length; i++) {
0737: if (_dateReceive[i] == null) {
0738: if (__dateReceive[i] == null) {
0739: __dateReceive[i] = new SimpleDateFormat(
0740: __dateReceiveFmt[i], Locale.US);
0741: __dateReceive[i].setTimeZone(__GMT);
0742: }
0743: _dateReceive[i] = (SimpleDateFormat) __dateReceive[i]
0744: .clone();
0745: }
0746:
0747: try {
0748: Date date = (Date) _dateReceive[i].parseObject(val);
0749: return field._numValue = date.getTime();
0750: } catch (java.lang.Exception e) {
0751: }
0752: }
0753: if (val.endsWith(" GMT")) {
0754: val = val.substring(0, val.length() - 4);
0755: for (int i = 0; i < _dateReceive.length; i++) {
0756: try {
0757: Date date = (Date) _dateReceive[i]
0758: .parseObject(val);
0759: return field._numValue = date.getTime();
0760: } catch (java.lang.Exception e) {
0761: }
0762: }
0763: }
0764: }
0765:
0766: throw new IllegalArgumentException("Cannot convert date: "
0767: + val);
0768: }
0769:
0770: /* -------------------------------------------------------------- */
0771: /**
0772: * Sets the value of an long field.
0773: *
0774: * @param name the field name
0775: * @param value the field long value
0776: */
0777: public void putLongField(Buffer name, long value) {
0778: Buffer v = BufferUtil.toBuffer(value);
0779: put(name, v, value);
0780: }
0781:
0782: /* -------------------------------------------------------------- */
0783: /**
0784: * Sets the value of an long field.
0785: *
0786: * @param name the field name
0787: * @param value the field long value
0788: */
0789: public void putLongField(String name, long value) {
0790: Buffer n = HttpHeaders.CACHE.lookup(name);
0791: Buffer v = BufferUtil.toBuffer(value);
0792: put(n, v, value);
0793: }
0794:
0795: /* -------------------------------------------------------------- */
0796: /**
0797: * Sets the value of an long field.
0798: *
0799: * @param name the field name
0800: * @param value the field long value
0801: */
0802: public void addLongField(String name, long value) {
0803: Buffer n = HttpHeaders.CACHE.lookup(name);
0804: Buffer v = BufferUtil.toBuffer(value);
0805: add(n, v, value);
0806: }
0807:
0808: /* -------------------------------------------------------------- */
0809: /**
0810: * Sets the value of an long field.
0811: *
0812: * @param name the field name
0813: * @param value the field long value
0814: */
0815: public void addLongField(Buffer name, long value) {
0816: Buffer v = BufferUtil.toBuffer(value);
0817: add(name, v, value);
0818: }
0819:
0820: /* -------------------------------------------------------------- */
0821: /**
0822: * Sets the value of a date field.
0823: *
0824: * @param name the field name
0825: * @param date the field date value
0826: */
0827: public void putDateField(Buffer name, long date) {
0828: if (_dateBuffer == null) {
0829: _dateBuffer = new StringBuffer(32);
0830: _calendar = new GregorianCalendar(__GMT);
0831: }
0832: _dateBuffer.setLength(0);
0833: _calendar.setTimeInMillis(date);
0834: formatDate(_dateBuffer, _calendar, false);
0835: Buffer v = new ByteArrayBuffer(_dateBuffer.toString());
0836: put(name, v, date);
0837: }
0838:
0839: /* -------------------------------------------------------------- */
0840: /**
0841: * Sets the value of a date field.
0842: *
0843: * @param name the field name
0844: * @param date the field date value
0845: */
0846: public void putDateField(String name, long date) {
0847: Buffer n = HttpHeaders.CACHE.lookup(name);
0848: putDateField(n, date);
0849: }
0850:
0851: /* -------------------------------------------------------------- */
0852: /**
0853: * Sets the value of a date field.
0854: *
0855: * @param name the field name
0856: * @param date the field date value
0857: */
0858: public void addDateField(String name, long date) {
0859: if (_dateBuffer == null) {
0860: _dateBuffer = new StringBuffer(32);
0861: _calendar = new GregorianCalendar(__GMT);
0862: }
0863: _dateBuffer.setLength(0);
0864: _calendar.setTimeInMillis(date);
0865: formatDate(_dateBuffer, _calendar, false);
0866: Buffer n = HttpHeaders.CACHE.lookup(name);
0867: Buffer v = new ByteArrayBuffer(_dateBuffer.toString());
0868: add(n, v, date);
0869: }
0870:
0871: /* ------------------------------------------------------------ */
0872: /**
0873: * Format a set cookie value
0874: *
0875: * @param cookie The cookie.
0876: * @param cookie2 If true, use the alternate cookie 2 header
0877: */
0878: public void addSetCookie(Cookie cookie) {
0879: String name = cookie.getName();
0880: String value = cookie.getValue();
0881: int version = cookie.getVersion();
0882:
0883: // Check arguments
0884: if (name == null || name.length() == 0)
0885: throw new IllegalArgumentException("Bad cookie name");
0886:
0887: // Format value and params
0888: StringBuffer buf = new StringBuffer(128);
0889: String name_value_params = null;
0890: synchronized (buf) {
0891: buf.append(name);
0892: buf.append('=');
0893: if (value != null && value.length() > 0) {
0894: if (version == 0) {
0895: try {
0896: buf.append(URLEncoder.encode(value,
0897: StringUtil.__ISO_8859_1));
0898: } catch (UnsupportedEncodingException e) {
0899: e.printStackTrace();
0900: }
0901: } else
0902: QuotedStringTokenizer.quote(buf, value);
0903: }
0904:
0905: if (version > 0) {
0906: buf.append(";version=");
0907: buf.append(version);
0908: String comment = cookie.getComment();
0909: if (comment != null && comment.length() > 0) {
0910: buf.append(";Comment=");
0911: QuotedStringTokenizer.quote(buf, comment);
0912: }
0913: }
0914: String path = cookie.getPath();
0915: if (path != null && path.length() > 0) {
0916: buf.append(";path=");
0917: buf.append(path);
0918: }
0919: String domain = cookie.getDomain();
0920: if (domain != null && domain.length() > 0) {
0921: buf.append(";domain=");
0922: buf.append(domain.toLowerCase());// lowercase for IE
0923: }
0924:
0925: long maxAge = cookie.getMaxAge();
0926: if (maxAge >= 0) {
0927: if (version == 0) {
0928: buf.append(";expires=");
0929: if (maxAge == 0)
0930: buf.append(__01Jan1970);
0931: else
0932: formatDate(buf, System.currentTimeMillis()
0933: + 1000L * maxAge, true);
0934: } else {
0935: buf.append(";max-age=");
0936: buf.append(maxAge);
0937: }
0938: } else if (version > 0) {
0939: buf.append(";discard");
0940: }
0941:
0942: if (cookie.getSecure()) {
0943: buf.append(";Secure");
0944: }
0945: if (cookie instanceof HttpOnlyCookie)
0946: buf.append(";HttpOnly");
0947:
0948: // TODO - straight to Buffer?
0949: name_value_params = buf.toString();
0950: }
0951: put(HttpHeaders.EXPIRES_BUFFER, __01Jan1970_BUFFER);
0952: add(HttpHeaders.SET_COOKIE_BUFFER, new ByteArrayBuffer(
0953: name_value_params));
0954: }
0955:
0956: /* -------------------------------------------------------------- */
0957: public void write(Writer writer) throws IOException {
0958: synchronized (writer) {
0959: for (int i = 0; i < _fields.size(); i++) {
0960: Field field = (Field) _fields.get(i);
0961: if (field != null && field._revision == _revision)
0962: field.write(writer);
0963: }
0964: writer.write(StringUtil.CRLF);
0965: }
0966: }
0967:
0968: /* -------------------------------------------------------------- */
0969: public void put(Buffer buffer) throws IOException {
0970: for (int i = 0; i < _fields.size(); i++) {
0971: Field field = (Field) _fields.get(i);
0972: if (field != null && field._revision == _revision)
0973: field.put(buffer);
0974: }
0975: BufferUtil.putCRLF(buffer);
0976: }
0977:
0978: /* -------------------------------------------------------------- */
0979: public String toString() {
0980: try {
0981: ByteArrayBuffer buffer = new ByteArrayBuffer(4096);
0982: put(buffer);
0983: return buffer.toString();
0984: } catch (Exception e) {
0985: e.printStackTrace();
0986: }
0987:
0988: return null;
0989: }
0990:
0991: /* ------------------------------------------------------------ */
0992: /**
0993: * Clear the header.
0994: */
0995: public void clear() {
0996: _revision++;
0997: if (_revision > 1000000) {
0998: _revision = 0;
0999: for (int i = _fields.size(); i-- > 0;) {
1000: Field field = (Field) _fields.get(i);
1001: if (field != null)
1002: field.clear();
1003: }
1004: }
1005: }
1006:
1007: /* ------------------------------------------------------------ */
1008: /**
1009: * Destroy the header. Help the garbage collector by null everything that we can.
1010: */
1011: public void destroy() {
1012: if (_fields != null) {
1013: for (int i = _fields.size(); i-- > 0;) {
1014: Field field = (Field) _fields.get(i);
1015: if (field != null)
1016: field.destroy();
1017: }
1018: }
1019: _fields = null;
1020: _dateBuffer = null;
1021: _calendar = null;
1022: _dateReceive = null;
1023: }
1024:
1025: /* ------------------------------------------------------------ */
1026: /**
1027: * Add fields from another HttpFields instance. Single valued fields are replaced, while all
1028: * others are added.
1029: *
1030: * @param fields
1031: */
1032: public void add(HttpFields fields) {
1033: if (fields == null)
1034: return;
1035:
1036: Enumeration e = fields.getFieldNames();
1037: while (e.hasMoreElements()) {
1038: String name = (String) e.nextElement();
1039: Enumeration values = fields.getValues(name);
1040: while (values.hasMoreElements())
1041: add(name, (String) values.nextElement());
1042: }
1043: }
1044:
1045: /* ------------------------------------------------------------ */
1046: /**
1047: * Get field value parameters. Some field values can have parameters. This method separates the
1048: * value from the parameters and optionally populates a map with the paramters. For example:
1049: *
1050: * <PRE>
1051: *
1052: * FieldName : Value ; param1=val1 ; param2=val2
1053: *
1054: * </PRE>
1055: *
1056: * @param value The Field value, possibly with parameteres.
1057: * @param parameters A map to populate with the parameters, or null
1058: * @return The value.
1059: */
1060: public static String valueParameters(String value, Map parameters) {
1061: if (value == null)
1062: return null;
1063:
1064: int i = value.indexOf(';');
1065: if (i < 0)
1066: return value;
1067: if (parameters == null)
1068: return value.substring(0, i).trim();
1069:
1070: StringTokenizer tok1 = new QuotedStringTokenizer(value
1071: .substring(i), ";", false, true);
1072: while (tok1.hasMoreTokens()) {
1073: String token = tok1.nextToken();
1074: StringTokenizer tok2 = new QuotedStringTokenizer(token,
1075: "= ");
1076: if (tok2.hasMoreTokens()) {
1077: String paramName = tok2.nextToken();
1078: String paramVal = null;
1079: if (tok2.hasMoreTokens())
1080: paramVal = tok2.nextToken();
1081: parameters.put(paramName, paramVal);
1082: }
1083: }
1084:
1085: return value.substring(0, i).trim();
1086: }
1087:
1088: /* ------------------------------------------------------------ */
1089: private static Float __one = new Float("1.0");
1090: private static Float __zero = new Float("0.0");
1091: private static StringMap __qualities = new StringMap();
1092: static {
1093: __qualities.put(null, __one);
1094: __qualities.put("1.0", __one);
1095: __qualities.put("1", __one);
1096: __qualities.put("0.9", new Float("0.9"));
1097: __qualities.put("0.8", new Float("0.8"));
1098: __qualities.put("0.7", new Float("0.7"));
1099: __qualities.put("0.66", new Float("0.66"));
1100: __qualities.put("0.6", new Float("0.6"));
1101: __qualities.put("0.5", new Float("0.5"));
1102: __qualities.put("0.4", new Float("0.4"));
1103: __qualities.put("0.33", new Float("0.33"));
1104: __qualities.put("0.3", new Float("0.3"));
1105: __qualities.put("0.2", new Float("0.2"));
1106: __qualities.put("0.1", new Float("0.1"));
1107: __qualities.put("0", __zero);
1108: __qualities.put("0.0", __zero);
1109: }
1110:
1111: /* ------------------------------------------------------------ */
1112: public static Float getQuality(String value) {
1113: if (value == null)
1114: return __zero;
1115:
1116: int qe = value.indexOf(";");
1117: if (qe++ < 0 || qe == value.length())
1118: return __one;
1119:
1120: if (value.charAt(qe++) == 'q') {
1121: qe++;
1122: Map.Entry entry = __qualities.getEntry(value, qe, value
1123: .length()
1124: - qe);
1125: if (entry != null)
1126: return (Float) entry.getValue();
1127: }
1128:
1129: HashMap params = new HashMap(3);
1130: valueParameters(value, params);
1131: String qs = (String) params.get("q");
1132: Float q = (Float) __qualities.get(qs);
1133: if (q == null) {
1134: try {
1135: q = new Float(qs);
1136: } catch (Exception e) {
1137: q = __one;
1138: }
1139: }
1140: return q;
1141: }
1142:
1143: /* ------------------------------------------------------------ */
1144: /**
1145: * List values in quality order.
1146: *
1147: * @param enum Enumeration of values with quality parameters
1148: * @return values in quality order.
1149: */
1150: public static List qualityList(Enumeration e) {
1151: if (e == null || !e.hasMoreElements())
1152: return Collections.EMPTY_LIST;
1153:
1154: Object list = null;
1155: Object qual = null;
1156:
1157: // Assume list will be well ordered and just add nonzero
1158: while (e.hasMoreElements()) {
1159: String v = e.nextElement().toString();
1160: Float q = getQuality(v);
1161:
1162: if (q.floatValue() >= 0.001) {
1163: list = LazyList.add(list, v);
1164: qual = LazyList.add(qual, q);
1165: }
1166: }
1167:
1168: List vl = LazyList.getList(list, false);
1169: if (vl.size() < 2)
1170: return vl;
1171:
1172: List ql = LazyList.getList(qual, false);
1173:
1174: // sort list with swaps
1175: Float last = __zero;
1176: for (int i = vl.size(); i-- > 0;) {
1177: Float q = (Float) ql.get(i);
1178: if (last.compareTo(q) > 0) {
1179: Object tmp = vl.get(i);
1180: vl.set(i, vl.get(i + 1));
1181: vl.set(i + 1, tmp);
1182: ql.set(i, ql.get(i + 1));
1183: ql.set(i + 1, q);
1184: last = __zero;
1185: i = vl.size();
1186: continue;
1187: }
1188: last = q;
1189: }
1190: ql.clear();
1191: return vl;
1192: }
1193:
1194: /* ------------------------------------------------------------ */
1195: /* ------------------------------------------------------------ */
1196: /* ------------------------------------------------------------ */
1197: public static final class Field {
1198: private Buffer _name;
1199: private Buffer _value;
1200: private String _stringValue;
1201: private long _numValue;
1202: private Field _next;
1203: private Field _prev;
1204: private int _revision;
1205:
1206: /* ------------------------------------------------------------ */
1207: private Field(Buffer name, Buffer value, long numValue,
1208: int revision) {
1209: _name = name.asImmutableBuffer();
1210: _value = value.isImmutable() ? value : new View(value);
1211: _next = null;
1212: _prev = null;
1213: _revision = revision;
1214: _numValue = numValue;
1215: _stringValue = null;
1216: }
1217:
1218: /* ------------------------------------------------------------ */
1219: private void clear() {
1220: _revision = -1;
1221: }
1222:
1223: /* ------------------------------------------------------------ */
1224: private void destroy() {
1225: _name = null;
1226: _value = null;
1227: _next = null;
1228: _prev = null;
1229: _stringValue = null;
1230: }
1231:
1232: /* ------------------------------------------------------------ */
1233: /**
1234: * Reassign a value to this field. Checks if the string value is the same as that in the char
1235: * array, if so then just reuse existing value.
1236: */
1237: private void reset(Buffer value, long numValue, int revision) {
1238: _revision = revision;
1239: if (_value == null) {
1240: _value = value.isImmutable() ? value : new View(value);
1241: _numValue = numValue;
1242: _stringValue = null;
1243: } else if (value.isImmutable()) {
1244: _value = value;
1245: _numValue = numValue;
1246: _stringValue = null;
1247: } else {
1248: if (_value instanceof View)
1249: ((View) _value).update(value);
1250: else
1251: _value = new View(value);
1252: _numValue = numValue;
1253:
1254: // check to see if string value is still valid.
1255: if (_stringValue != null) {
1256: if (_stringValue.length() != value.length())
1257: _stringValue = null;
1258: else {
1259: for (int i = value.length(); i-- > 0;) {
1260: if (value.peek(value.getIndex() + i) != _stringValue
1261: .charAt(i)) {
1262: _stringValue = null;
1263: break;
1264: }
1265: }
1266: }
1267: }
1268: }
1269: }
1270:
1271: /* ------------------------------------------------------------ */
1272: public void write(Writer writer) throws IOException {
1273: writer.write(_name.toString());
1274: writer.write(":");
1275: writer.write(_value.toString());
1276: writer.write(StringUtil.CRLF);
1277: }
1278:
1279: /* ------------------------------------------------------------ */
1280: public void put(Buffer buffer) throws IOException {
1281: buffer.put(_name);
1282: buffer.put((byte) ':');
1283: buffer.put((byte) ' ');
1284: buffer.put(_value);
1285: BufferUtil.putCRLF(buffer);
1286: }
1287:
1288: /* ------------------------------------------------------------ */
1289: public String getName() {
1290: return _name.toString();
1291: }
1292:
1293: /* ------------------------------------------------------------ */
1294: Buffer getNameBuffer() {
1295: return _name;
1296: }
1297:
1298: /* ------------------------------------------------------------ */
1299: public int getNameOrdinal() {
1300: return HttpHeaders.CACHE.getOrdinal(_name);
1301: }
1302:
1303: /* ------------------------------------------------------------ */
1304: public String getValue() {
1305: if (_stringValue == null)
1306: _stringValue = _value.toString();
1307: return _stringValue;
1308: }
1309:
1310: /* ------------------------------------------------------------ */
1311: public Buffer getValueBuffer() {
1312: return _value;
1313: }
1314:
1315: /* ------------------------------------------------------------ */
1316: public int getValueOrdinal() {
1317: return HttpHeaderValues.CACHE.getOrdinal(_value);
1318: }
1319:
1320: /* ------------------------------------------------------------ */
1321: public int getIntValue() {
1322: return (int) getLongValue();
1323: }
1324:
1325: /* ------------------------------------------------------------ */
1326: public long getLongValue() {
1327: if (_numValue == -1)
1328: _numValue = BufferUtil.toLong(_value);
1329: return _numValue;
1330: }
1331:
1332: /* ------------------------------------------------------------ */
1333: public String toString() {
1334: return ("[" + (_prev == null ? "" : "<-") + getName() + "="
1335: + _revision + "=" + _value
1336: + (_next == null ? "" : "->") + "]");
1337: }
1338: }
1339:
1340: }
|