Java tutorial
// ======================================================================== // $Id: HttpFields.java,v 1.77 2006/11/22 20:02:15 gregwilkins Exp $ // Copyright 199-2004 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== package net.lightbody.bmp.proxy.jetty.http; import net.lightbody.bmp.proxy.jetty.log.LogFactory; import net.lightbody.bmp.proxy.jetty.util.*; import org.apache.commons.logging.Log; import javax.servlet.http.Cookie; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import java.text.SimpleDateFormat; import java.util.*; /* ------------------------------------------------------------ */ /** HTTP Fields. * A collection of HTTP header and or Trailer fields. * This class is not synchronized and needs to be protected from * concurrent access. * * This class is not synchronized as it is expected that modifications * will only be performed by a single thread. * * @version $Id: HttpFields.java,v 1.77 2006/11/22 20:02:15 gregwilkins Exp $ * @author Greg Wilkins (gregw) */ public class HttpFields { private static Log log = LogFactory.getLog(HttpFields.class); /* ------------------------------------------------------------ */ /** General Fields. */ public final static String __CacheControl = "Cache-Control", __Connection = "Connection", __Date = "Date", __Pragma = "Pragma", __ProxyConnection = "Proxy-Connection", __Trailer = "Trailer", __TransferEncoding = "Transfer-Encoding", __Upgrade = "Upgrade", __Via = "Via", __Warning = "Warning"; /* ------------------------------------------------------------ */ /** Entity Fields. */ public final static String __Allow = "Allow", __ContentEncoding = "Content-Encoding", __ContentLanguage = "Content-Language", __ContentLength = "Content-Length", __ContentLocation = "Content-Location", __ContentMD5 = "Content-MD5", __ContentRange = "Content-Range", __ContentType = "Content-Type", __Expires = "Expires", __LastModified = "Last-Modified"; /* ------------------------------------------------------------ */ /** Request Fields. */ public final static String __Accept = "Accept", __AcceptCharset = "Accept-Charset", __AcceptEncoding = "Accept-Encoding", __AcceptLanguage = "Accept-Language", __Authorization = "Authorization", __Expect = "Expect", __Forwarded = "Forwarded", __From = "From", __Host = "Host", __IfMatch = "If-Match", __IfModifiedSince = "If-Modified-Since", __IfNoneMatch = "If-None-Match", __IfRange = "If-Range", __IfUnmodifiedSince = "If-Unmodified-Since", __KeepAlive = "keep-alive", __MaxForwards = "Max-Forwards", __ProxyAuthorization = "Proxy-Authorization", __Range = "Range", __RequestRange = "Request-Range", __Referer = "Referer", __TE = "TE", __UserAgent = "User-Agent", __XForwardedFor = "X-Forwarded-For"; /* ------------------------------------------------------------ */ /** Response Fields. */ public final static String __AcceptRanges = "Accept-Ranges", __Age = "Age", __ETag = "ETag", __Location = "Location", __ProxyAuthenticate = "Proxy-Authenticate", __RetryAfter = "Retry-After", __Server = "Server", __ServletEngine = "Servlet-Engine", __Vary = "Vary", __WwwAuthenticate = "WWW-Authenticate"; /* ------------------------------------------------------------ */ /** Other Fields. */ public final static String __Cookie = "Cookie", __SetCookie = "Set-Cookie", __SetCookie2 = "Set-Cookie2", __MimeVersion = "MIME-Version", __Identity = "identity", __SoapAction = "SOAPAction"; /* ------------------------------------------------------------ */ /** Private class to hold Field name info */ private static final class FieldInfo { String _name; String _lname; boolean _inlineValues; int _hashCode; static int __hashCode; FieldInfo(String name, boolean inline) { synchronized (FieldInfo.class) { _name = name; _lname = StringUtil.asciiToLowerCase(name); _inlineValues = inline; _hashCode = __hashCode++; if (__hashCode < __maxCacheSize) { FieldInfo oldInfo = (FieldInfo) __info.get(name); if (oldInfo == null) { __info.put(name, this); if (!name.equals(_lname)) __info.put(_lname, this); } else _hashCode = oldInfo._hashCode; } } } public String toString() { return "[" + _name + "," + _hashCode + "," + _inlineValues + "]"; } public int hashCode() { return _hashCode; } public boolean equals(Object o) { if (o == null || !(o instanceof FieldInfo)) return false; FieldInfo fi = (FieldInfo) o; return fi == this || fi._hashCode == _hashCode || fi._name.equals(_name); } } /* ------------------------------------------------------------ */ private static final StringMap __info = new StringMap(true); private static final StringMap __values = new StringMap(true); private static final int __maxCacheSize = 128; /* ------------------------------------------------------------ */ static { // Initialize FieldInfo's with special values. // In order of most frequently used. new FieldInfo(__Host, false); new FieldInfo(__KeepAlive, false); new FieldInfo(__Connection, false); new FieldInfo(__Cookie, false); new FieldInfo(__Accept, false); new FieldInfo(__AcceptLanguage, false); new FieldInfo(__AcceptEncoding, false); new FieldInfo(__AcceptCharset, false); new FieldInfo(__CacheControl, false); new FieldInfo(__SetCookie, false); new FieldInfo(__SetCookie2, false); new FieldInfo(__Date, false); new FieldInfo(__TransferEncoding, true); new FieldInfo(__ContentEncoding, true); new FieldInfo(__ContentLength, false); new FieldInfo(__Expires, false); new FieldInfo(__Expect, false); new FieldInfo(__Referer, false); new FieldInfo(__TE, false); new FieldInfo(__UserAgent, false); new FieldInfo(__IfModifiedSince, false); new FieldInfo(__IfRange, false); new FieldInfo(__IfUnmodifiedSince, false); new FieldInfo(__Location, false); new FieldInfo(__Server, false); new FieldInfo(__ServletEngine, false); new FieldInfo(__AcceptRanges, false); new FieldInfo(__Range, false); new FieldInfo(__RequestRange, false); new FieldInfo(__SoapAction, false); new FieldInfo(__ContentLocation, false); new FieldInfo(__ContentMD5, false); new FieldInfo(__ContentRange, false); new FieldInfo(__ContentType, false); new FieldInfo(__LastModified, false); new FieldInfo(__Authorization, false); new FieldInfo(__From, false); new FieldInfo(__MaxForwards, false); new FieldInfo(__ProxyAuthenticate, false); new FieldInfo(__Age, false); new FieldInfo(__ETag, false); new FieldInfo(__RetryAfter, false); } /* ------------------------------------------------------------ */ private static FieldInfo getFieldInfo(String name) { FieldInfo info = (FieldInfo) __info.get(name); if (info == null) info = new FieldInfo(name, false); return info; } /* ------------------------------------------------------------ */ private static FieldInfo getFieldInfo(char[] name, int offset, int length) { Map.Entry entry = __info.getEntry(name, offset, length); if (entry == null) return new FieldInfo(new String(name, offset, length), false); return (FieldInfo) entry.getValue(); } /* ------------------------------------------------------------ */ /** Fields Values. */ public final static String __Chunked = "chunked"; public final static String __Close = "close"; public final static String __TextHtml = "text/html"; public final static String __MessageHttp = "message/http"; public final static String __WwwFormUrlEncode = "application/x-www-form-urlencoded"; public static final String __ExpectContinue = "100-continue"; static { __values.put(__KeepAlive, __KeepAlive); __values.put(__Chunked, __Chunked); __values.put(__Close, __Close); __values.put(__TextHtml, __TextHtml); __values.put(__MessageHttp, __MessageHttp); __values.put(__WwwFormUrlEncode, __WwwFormUrlEncode); __values.put(__ExpectContinue, __ExpectContinue); __values.put("max-age=0", "max-age=0"); __values.put("no-cache", "no-cache"); __values.put("300", "300"); __values.put("ISO-8859-1, utf-8;q=0.66, *;q=0.66", "ISO-8859-1, utf-8;q=0.66, *;q=0.66"); } /* ------------------------------------------------------------ */ public final static String __separators = ", \t"; /* ------------------------------------------------------------ */ public final static char[] __CRLF = { '\015', '\012' }; public final static char[] __COLON = { ':', ' ' }; /* ------------------------------------------------------------ */ private static String[] DAYS = { "Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; private static String[] MONTHS = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Jan" }; /* ------------------------------------------------------------ */ /** Format HTTP date * "EEE, dd MMM yyyy HH:mm:ss 'GMT'" or * "EEE, dd-MMM-yy HH:mm:ss 'GMT'"for cookies */ public static String formatDate(long date, boolean cookie) { StringBuffer buf = new StringBuffer(32); HttpCal gc = new HttpCal(); gc.setTimeInMillis(date); formatDate(buf, gc, cookie); return buf.toString(); } /* ------------------------------------------------------------ */ /** Format HTTP date * "EEE, dd MMM yyyy HH:mm:ss 'GMT'" or * "EEE, dd-MMM-yy HH:mm:ss 'GMT'"for cookies */ public static String formatDate(Calendar calendar, boolean cookie) { StringBuffer buf = new StringBuffer(32); formatDate(buf, calendar, cookie); return buf.toString(); } /* ------------------------------------------------------------ */ /** Format HTTP date * "EEE, dd MMM yyyy HH:mm:ss 'GMT'" or * "EEE, dd-MMM-yy HH:mm:ss 'GMT'"for cookies */ public static String formatDate(StringBuffer buf, long date, boolean cookie) { HttpCal gc = new HttpCal(); gc.setTimeInMillis(date); formatDate(buf, gc, cookie); return buf.toString(); } /* ------------------------------------------------------------ */ /** Format HTTP date * "EEE, dd MMM yyyy HH:mm:ss 'GMT'" or * "EEE, dd-MMM-yy HH:mm:ss 'GMT'"for cookies */ public static void formatDate(StringBuffer buf, Calendar calendar, boolean cookie) { // "EEE, dd MMM yyyy HH:mm:ss 'GMT'" // "EEE, dd-MMM-yy HH:mm:ss 'GMT'", cookie int day_of_week = calendar.get(Calendar.DAY_OF_WEEK); int day_of_month = calendar.get(Calendar.DAY_OF_MONTH); int month = calendar.get(Calendar.MONTH); int year = calendar.get(Calendar.YEAR); int century = year / 100; year = year % 100; long tm = (calendar instanceof HttpCal) ? (((HttpCal) calendar).getTimeInMillis()) : calendar.getTime().getTime(); int epoch = (int) ((tm / 1000) % (60 * 60 * 24)); int seconds = epoch % 60; epoch = epoch / 60; int minutes = epoch % 60; int hours = epoch / 60; buf.append(DAYS[day_of_week]); buf.append(','); buf.append(' '); StringUtil.append2digits(buf, day_of_month); if (cookie) { buf.append('-'); buf.append(MONTHS[month]); buf.append('-'); StringUtil.append2digits(buf, year); } else { buf.append(' '); buf.append(MONTHS[month]); buf.append(' '); StringUtil.append2digits(buf, century); StringUtil.append2digits(buf, year); } buf.append(' '); StringUtil.append2digits(buf, hours); buf.append(':'); StringUtil.append2digits(buf, minutes); buf.append(':'); StringUtil.append2digits(buf, seconds); buf.append(" GMT"); } /* -------------------------------------------------------------- */ private static TimeZone __GMT = TimeZone.getTimeZone("GMT"); public final static DateCache __dateCache = new DateCache("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US); /* ------------------------------------------------------------ */ private final static String __dateReceiveFmt[] = { "EEE, dd MMM yyyy HH:mm:ss zzz", "EEE, dd-MMM-yy HH:mm:ss zzz", "EEE MMM dd HH:mm:ss yyyy", "EEE, dd MMM yyyy HH:mm:ss zzz", "EEE, dd-MMM-yy HH:mm:ss zzz", "dd MMM yyyy HH:mm:ss", "dd-MMM-yy HH:mm:ss", }; public static SimpleDateFormat __dateReceiveSource[]; public static final ThreadLocal __dateReceiveCache = new ThreadLocal(); static { __GMT.setID("GMT"); __dateCache.setTimeZone(__GMT); __dateReceiveSource = new SimpleDateFormat[__dateReceiveFmt.length]; for (int i = 0; i < __dateReceiveSource.length; i++) { __dateReceiveSource[i] = new SimpleDateFormat(__dateReceiveFmt[i], Locale.US); __dateReceiveSource[i].setTimeZone(__GMT); } } public final static String __01Jan1970 = HttpFields.formatDate(0, false); /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ private static final class Field { FieldInfo _info; String _value; Field _next; Field _prev; int _version; /* ------------------------------------------------------------ */ Field(FieldInfo info, String value, int version) { _info = info; _value = value; _next = null; _prev = null; _version = version; } /* ------------------------------------------------------------ */ Field(FieldInfo info, char[] buf, int offset, int length, int version) { Map.Entry valueEntry = __values.getEntry(buf, offset, length); String value = null; if (valueEntry != null) value = (String) valueEntry.getKey(); else value = new String(buf, offset, length); _info = info; _value = value; _next = null; _prev = null; _version = version; } /* ------------------------------------------------------------ */ public boolean equals(Object o) { return (o instanceof Field) && o == this && _version == ((Field) o)._version; } /* ------------------------------------------------------------ */ public int hashCode() { return _info.hashCode() * _version; } /* ------------------------------------------------------------ */ void clear() { _version = -1; } /* ------------------------------------------------------------ */ void destroy() { _info = null; _value = null; _next = null; _prev = null; _version = -1; } /* ------------------------------------------------------------ */ void reset(String value, int version) { _value = value; _version = version; } /* ------------------------------------------------------------ */ /** Reassign a value to this field. * Checks if the value is the same as that in the char array, if so * then just reuse existing value. */ void reset(char[] buf, int offset, int length, int version) { _version = version; if (!StringUtil.equals(_value, buf, offset, length)) { Map.Entry valueEntry = __values.getEntry(buf, offset, length); String value = null; if (valueEntry != null) value = (String) valueEntry.getKey(); else value = new String(buf, offset, length); _value = value; } } /* ------------------------------------------------------------ */ void write(Writer writer, int version) throws IOException { if (_info == null || _version != version) return; if (_info._inlineValues) { if (_prev != null) return; writer.write(_info._name); writer.write(__COLON); Field f = this; while (true) { writer.write(QuotedStringTokenizer.quote(f._value, ", \t")); f = f._next; if (f == null) break; writer.write(","); } writer.write(__CRLF); } else { writer.write(_info._name); writer.write(__COLON); writer.write(_value); writer.write(__CRLF); } } /* ------------------------------------------------------------ */ String getDisplayName() { return _info._name; } /* ------------------------------------------------------------ */ public String toString() { return ("[" + (_prev == null ? "" : "<-") + getDisplayName() + "=" + _value + (_next == null ? "" : "->") + "]"); } } /* ------------------------------------------------------------ */ private static Float __one = new Float("1.0"); private static Float __zero = new Float("0.0"); private static StringMap __qualities = new StringMap(); static { __qualities.put(null, __one); __qualities.put("1.0", __one); __qualities.put("1", __one); __qualities.put("0.9", new Float("0.9")); __qualities.put("0.8", new Float("0.8")); __qualities.put("0.7", new Float("0.7")); __qualities.put("0.66", new Float("0.66")); __qualities.put("0.6", new Float("0.6")); __qualities.put("0.5", new Float("0.5")); __qualities.put("0.4", new Float("0.4")); __qualities.put("0.33", new Float("0.33")); __qualities.put("0.3", new Float("0.3")); __qualities.put("0.2", new Float("0.2")); __qualities.put("0.1", new Float("0.1")); __qualities.put("0", __zero); __qualities.put("0.0", __zero); } /* -------------------------------------------------------------- */ private ArrayList _fields = new ArrayList(15); private int[] _index = new int[__maxCacheSize]; private int _version; private SimpleDateFormat _dateReceive[]; private StringBuffer _dateBuffer; private HttpCal _calendar; /* ------------------------------------------------------------ */ /** Constructor. */ public HttpFields() { Arrays.fill(_index, -1); } /* ------------------------------------------------------------ */ public int size() { return _fields.size(); } /* -------------------------------------------------------------- */ /** Get enumeration of header _names. * Returns an enumeration of strings representing the header _names * for this request. */ public Enumeration getFieldNames() { return new Enumeration() { int i = 0; Field field = null; public boolean hasMoreElements() { if (field != null) return true; while (i < _fields.size()) { Field f = (Field) _fields.get(i++); if (f != null && f._version == _version && f._prev == null) { field = f; return true; } } return false; } public Object nextElement() throws NoSuchElementException { if (field != null || hasMoreElements()) { String n = field._info._name; field = null; return n; } throw new NoSuchElementException(); } }; } /* ------------------------------------------------------------ */ Field getField(String name) { FieldInfo info = getFieldInfo(name); return getField(info, true); } /* ------------------------------------------------------------ */ Field getField(FieldInfo info, boolean getValid) { int hi = info.hashCode(); if (hi < _index.length) { if (_index[hi] >= 0) { Field field = (Field) (_fields.get(_index[hi])); return (field != null && (!getValid || field._version == _version)) ? field : null; } } else { for (int i = 0; i < _fields.size(); i++) { Field field = (Field) _fields.get(i); if (info.equals(field._info) && (!getValid || field._version == _version)) return field; } } return null; } /* ------------------------------------------------------------ */ public boolean containsKey(String name) { FieldInfo info = getFieldInfo(name); return getField(info, true) != null; } /* -------------------------------------------------------------- */ /** * @return the value of a field, or null if not found. For * multiple fields of the same name, only the first is returned. * @param name the case-insensitive field name */ public String get(String name) { FieldInfo info = getFieldInfo(name); Field field = getField(info, true); if (field != null) return field._value; return null; } /* -------------------------------------------------------------- */ /** Get multi headers * @return Enumeration of the values, or null if no such header. * @param name the case-insensitive field name */ public Enumeration getValues(String name) { FieldInfo info = getFieldInfo(name); final Field field = getField(info, true); if (field != null) { return new Enumeration() { Field f = field; public boolean hasMoreElements() { while (f != null && f._version != _version) f = f._next; return f != null; } public Object nextElement() throws NoSuchElementException { if (f == null) throw new NoSuchElementException(); Field n = f; do f = f._next; while (f != null && f._version != _version); return n._value; } }; } return null; } /* -------------------------------------------------------------- */ /** Get multi field values with separator. * The multiple values can be represented as separate headers of * the same name, or by a single header using the separator(s), or * a combination of both. Separators may be quoted. * @param name the case-insensitive field name * @param separators String of separators. * @return Enumeration of the values, or null if no such header. */ public Enumeration getValues(String name, final String separators) { final Enumeration e = getValues(name); if (e == null) return null; return new Enumeration() { QuotedStringTokenizer tok = null; public boolean hasMoreElements() { if (tok != null && tok.hasMoreElements()) return true; while (e.hasMoreElements()) { String value = (String) e.nextElement(); tok = new QuotedStringTokenizer(value, separators, false, false); if (tok.hasMoreElements()) return true; } tok = null; return false; } public Object nextElement() throws NoSuchElementException { if (!hasMoreElements()) throw new NoSuchElementException(); String next = (String) tok.nextElement(); if (next != null) next = next.trim(); return next; } }; } /* -------------------------------------------------------------- */ /** Set a field. * @param name the name of the field * @param value the value of the field. If null the field is cleared. */ public String put(String name, String value) { if (value == null) return remove(name); FieldInfo info = getFieldInfo(name); Field field = getField(info, false); // Look for value to replace. if (field != null) { String old = (field._version == _version) ? field._value : null; field.reset(value, _version); field = field._next; while (field != null) { field.clear(); field = field._next; } return old; } else { // new value; field = new Field(info, value, _version); int hi = info.hashCode(); if (hi < _index.length) _index[hi] = _fields.size(); _fields.add(field); return null; } } /* -------------------------------------------------------------- */ /** Set a field. * @param name the name of the field * @param list the List value of the field. If null the field is cleared. */ public void put(String name, List list) { if (list == null || list.size() == 0) { remove(name); return; } Object v = list.get(0); if (v != null) put(name, v.toString()); else remove(name); if (list.size() > 1) { java.util.Iterator iter = list.iterator(); iter.next(); while (iter.hasNext()) { v = iter.next(); if (v != null) add(name, v.toString()); } } } /* -------------------------------------------------------------- */ /** Add to or set a field. * If the field is allowed to have multiple values, add will add * multiple headers of the same name. * @param name the name of the field * @param value the value of the field. * @exception IllegalArgumentException If the name is a single * valued field and already has a value. */ public void add(String name, String value) throws IllegalArgumentException { if (value == null) throw new IllegalArgumentException("null value"); FieldInfo info = getFieldInfo(name); Field field = getField(info, false); Field last = null; if (field != null) { while (field != null && field._version == _version) { last = field; field = field._next; } } if (field != null) field.reset(value, _version); else { // create the field field = new Field(info, value, _version); // look for chain to add too if (last != null) { field._prev = last; last._next = field; } else if (info.hashCode() < _index.length) _index[info.hashCode()] = _fields.size(); _fields.add(field); } } /* ------------------------------------------------------------ */ /** Remove a field. * @param name */ public String remove(String name) { String old = null; FieldInfo info = getFieldInfo(name); Field field = getField(info, true); if (field != null) { old = field._value; while (field != null) { field.clear(); field = field._next; } } return old; } /* -------------------------------------------------------------- */ /** Get a header as an integer value. * Returns the value of an integer field or -1 if not found. * The case of the field name is ignored. * @param name the case-insensitive field name * @exception NumberFormatException If bad integer found */ public int getIntField(String name) throws NumberFormatException { String val = valueParameters(get(name), null); if (val != null) return Integer.parseInt(val); return -1; } /* -------------------------------------------------------------- */ /** Get a header as a date value. * Returns the value of a date field, or -1 if not found. * The case of the field name is ignored. * @param name the case-insensitive field name */ public long getDateField(String name) { String val = valueParameters(get(name), null); if (val == null) return -1; if (_dateReceive == null) { _dateReceive = (SimpleDateFormat[]) __dateReceiveCache.get(); if (_dateReceive == null) { _dateReceive = (SimpleDateFormat[]) new SimpleDateFormat[__dateReceiveSource.length]; __dateReceiveCache.set(_dateReceive); } } for (int i = 0; i < _dateReceive.length; i++) { // clone formatter for thread safety if (_dateReceive[i] == null) _dateReceive[i] = (SimpleDateFormat) __dateReceiveSource[i].clone(); try { Date date = (Date) _dateReceive[i].parseObject(val); return date.getTime(); } catch (java.lang.Exception e) { LogSupport.ignore(log, e); } } if (val.endsWith(" GMT")) { val = val.substring(0, val.length() - 4); for (int i = 0; i < _dateReceive.length; i++) { try { Date date = (Date) _dateReceive[i].parseObject(val); return date.getTime(); } catch (java.lang.Exception e) { LogSupport.ignore(log, e); } } } throw new IllegalArgumentException(val); } /* -------------------------------------------------------------- */ /** * Sets the value of an integer field. * @param name the field name * @param value the field integer value */ public void putIntField(String name, int value) { put(name, Integer.toString(value)); } /* -------------------------------------------------------------- */ /** * Sets the value of a date field. * @param name the field name * @param date the field date value */ public void putDateField(String name, Date date) { putDateField(name, date.getTime()); } /* -------------------------------------------------------------- */ /** * Adds the value of a date field. * @param name the field name * @param date the field date value */ public void addDateField(String name, Date date) { addDateField(name, date.getTime()); } /* -------------------------------------------------------------- */ /** * Adds the value of a date field. * @param name the field name * @param date the field date value */ public void addDateField(String name, long date) { if (_dateBuffer == null) { _dateBuffer = new StringBuffer(32); _calendar = new HttpCal(); } _dateBuffer.setLength(0); _calendar.setTimeInMillis(date); formatDate(_dateBuffer, _calendar, false); add(name, _dateBuffer.toString()); } /* -------------------------------------------------------------- */ /** * Sets the value of a date field. * @param name the field name * @param date the field date value */ public void putDateField(String name, long date) { if (_dateBuffer == null) { _dateBuffer = new StringBuffer(32); _calendar = new HttpCal(); } _dateBuffer.setLength(0); _calendar.setTimeInMillis(date); formatDate(_dateBuffer, _calendar, false); put(name, _dateBuffer.toString()); } /* -------------------------------------------------------------- */ /** Read HttpHeaders from inputStream. */ public void read(LineInput in) throws IOException { Field last = null; char[] buf = null; int size = 0; net.lightbody.bmp.proxy.jetty.util.LineInput.LineBuffer line_buffer; synchronized (in) { line: while ((line_buffer = in.readLineBuffer()) != null) { // check space in the lowercase buffer buf = line_buffer.buffer; size = line_buffer.size; if (size == 0) break; // setup loop state machine int i1 = -1; int i2 = -1; int name_l = 0; int i = 0; char c = buf[0]; // Check for continuity line if (c != ' ' && c != '\t') { i2 = 0; // reading name upto : for (i = 1; i < size; i++) { c = buf[i]; if (c == ':') { name_l = i2 + 1; break; } if (c != ' ' && c != '\t') i2 = i; } } // skip whitespace after : or start of continuity line for (i++; i < size; i++) { c = buf[i]; if (c != ' ' && c != '\t') { i1 = i; i2 = i - 1; break; } } // Reverse Parse the "name : value" to last char of value for (i = size; i-- > i1 && i >= 0;) { c = buf[i]; if (c != ' ' && c != '\t') { i2 = i; break; } } // If no name, it is a continuation line if (name_l <= 0) { if (i1 > 0 && last != null) last._value = last._value + ' ' + new String(buf, i1, i2 - i1 + 1); continue; } // create the field. FieldInfo info = getFieldInfo(buf, 0, name_l); Field field = getField(info, false); last = null; if (field != null) { while (field != null && field._version == _version) { last = field; field = field._next; } } if (field != null) { if (i1 >= 0) field.reset(buf, i1, i2 - i1 + 1, _version); else field.reset("", _version); } else { // create the field if (i1 >= 0) field = new Field(info, buf, i1, i2 - i1 + 1, _version); else field = new Field(info, "", _version); // look for chain to add too if (last != null) { field._prev = last; last._next = field; } else if (info.hashCode() < _index.length) _index[info.hashCode()] = _fields.size(); _fields.add(field); } last = field; } } } /* -------------------------------------------------------------- */ /* Write Extra HTTP headers. */ public void write(Writer writer) throws IOException { synchronized (writer) { for (int i = 0; i < _fields.size(); i++) { Field field = (Field) _fields.get(i); if (field != null) field.write(writer, _version); } writer.write(__CRLF); } } /* -------------------------------------------------------------- */ public String toString() { try { StringWriter writer = new StringWriter(); write(writer); return writer.toString(); } catch (Exception e) { } return null; } /* ------------------------------------------------------------ */ /** Clear the header. */ public void clear() { _version++; if (_version > 1000) { _version = 0; for (int i = _fields.size(); i-- > 0;) { Field field = (Field) _fields.get(i); if (field != null) field.clear(); } } } /* ------------------------------------------------------------ */ /** Destroy the header. * Help the garbage collector by null everything that we can. */ public void destroy() { for (int i = _fields.size(); i-- > 0;) { Field field = (Field) _fields.get(i); if (field != null) field.destroy(); } _fields = null; _index = null; _dateBuffer = null; _calendar = null; _dateReceive = null; } /* ------------------------------------------------------------ */ /** Get field value parameters. * Some field values can have parameters. This method separates * the value from the parameters and optionally populates a * map with the paramters. For example:<PRE> * FieldName : Value ; param1=val1 ; param2=val2 * </PRE> * @param value The Field value, possibly with parameteres. * @param parameters A map to populate with the parameters, or null * @return The value. */ public static String valueParameters(String value, Map parameters) { if (value == null) return null; int i = value.indexOf(';'); if (i < 0) return value; if (parameters == null) return value.substring(0, i).trim(); StringTokenizer tok1 = new QuotedStringTokenizer(value.substring(i), ";", false, true); while (tok1.hasMoreTokens()) { String token = tok1.nextToken(); StringTokenizer tok2 = new QuotedStringTokenizer(token, "= "); if (tok2.hasMoreTokens()) { String paramName = tok2.nextToken(); String paramVal = null; if (tok2.hasMoreTokens()) paramVal = tok2.nextToken(); parameters.put(paramName, paramVal); } } return value.substring(0, i).trim(); } /* ------------------------------------------------------------ */ public static Float getQuality(String value) { if (value == null) return __zero; int qe = value.indexOf(";"); if (qe++ < 0 || qe == value.length()) return __one; if (value.charAt(qe++) == 'q') { qe++; Map.Entry entry = __qualities.getEntry(value, qe, value.length() - qe); if (entry != null) return (Float) entry.getValue(); } HashMap params = new HashMap(3); valueParameters(value, params); String qs = (String) params.get("q"); Float q = (Float) __qualities.get(qs); if (q == null) { try { q = new Float(qs); } catch (Exception e) { q = __one; } } return q; } /* ------------------------------------------------------------ */ /** List values in quality order. * @param enm Enumeration of values with quality parameters * @return values in quality order. */ public static List qualityList(Enumeration enm) { if (enm == null || !enm.hasMoreElements()) return Collections.EMPTY_LIST; Object list = null; Object qual = null; // Assume list will be well ordered and just add nonzero while (enm.hasMoreElements()) { String v = enm.nextElement().toString(); Float q = getQuality(v); if (q.floatValue() >= 0.001) { list = LazyList.add(list, v); qual = LazyList.add(qual, q); } } List vl = LazyList.getList(list, false); if (vl.size() < 2) return vl; List ql = LazyList.getList(qual, false); // sort list with swaps Float last = __zero; for (int i = vl.size(); i-- > 0;) { Float q = (Float) ql.get(i); if (last.compareTo(q) > 0) { Object tmp = vl.get(i); vl.set(i, vl.get(i + 1)); vl.set(i + 1, tmp); ql.set(i, ql.get(i + 1)); ql.set(i + 1, q); last = __zero; i = vl.size(); continue; } last = q; } ql.clear(); return vl; } /* ------------------------------------------------------------ */ /** Format a set cookie value * @param cookie The cookie. */ public void addSetCookie(Cookie cookie) { String name = cookie.getName(); String value = cookie.getValue(); int version = cookie.getVersion(); // Check arguments if (name == null || name.length() == 0) throw new IllegalArgumentException("Bad cookie name"); // Format value and params StringBuffer buf = new StringBuffer(128); String name_value_params = null; synchronized (buf) { buf.append(name); buf.append('='); if (value != null && value.length() > 0) { if (version == 0) URI.encodeString(buf, value, "\";, '"); else buf.append(QuotedStringTokenizer.quote(value, "\";, '")); } if (version > 0) { buf.append(";Version="); buf.append(version); String comment = cookie.getComment(); if (comment != null && comment.length() > 0) { buf.append(";Comment="); QuotedStringTokenizer.quote(buf, comment); } } String path = cookie.getPath(); if (path != null && path.length() > 0) { buf.append(";Path="); buf.append(path); } String domain = cookie.getDomain(); if (domain != null && domain.length() > 0) { buf.append(";Domain="); buf.append(domain.toLowerCase());// lowercase for IE } long maxAge = cookie.getMaxAge(); if (maxAge >= 0) { if (version == 0) { buf.append(";Expires="); if (maxAge == 0) buf.append(__01Jan1970); else formatDate(buf, System.currentTimeMillis() + 1000L * maxAge, true); } else { buf.append(";Max-Age="); buf.append(cookie.getMaxAge()); } } else if (version > 0) { buf.append(";Discard"); } if (cookie.getSecure()) { buf.append(";Secure"); } if (cookie instanceof HttpOnlyCookie) buf.append(";HttpOnly"); name_value_params = buf.toString(); } put(__Expires, __01Jan1970); add(__SetCookie, name_value_params); } /* ------------------------------------------------------------ */ /** Add fields from another HttpFields instance. * Single valued fields are replaced, while all others are added. * @param fields */ public void add(HttpFields fields) { if (fields == null) return; Enumeration enm = fields.getFieldNames(); while (enm.hasMoreElements()) { String name = (String) enm.nextElement(); Enumeration values = fields.getValues(name); while (values.hasMoreElements()) add(name, (String) values.nextElement()); } } /* ------------------------------------------------------------ */ /** * return an iterator for field name:value pairs * @return an HttpFields.Iterator */ public Iterator iterator() { return new EntryIterator(); } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ public class Entry { protected int _i; Entry(int i) { _i = i; } public String getKey() { return ((Field) _fields.get(_i)).getDisplayName(); } public String getValue() { return ((Field) _fields.get(_i))._value; } } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ private class EntryIterator implements Iterator { protected int _i = 0; public boolean hasNext() { return (_i < _fields.size()); } public Object next() throws NoSuchElementException { return new Entry(_i++); } public void remove() { throw new UnsupportedOperationException(); } } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /* handle 1.3 protected methods */ private static class HttpCal extends GregorianCalendar { HttpCal() { super(__GMT); } /* ------------------------------------------------------------------------------- */ /** * @see java.util.Calendar#setTimeInMillis(long) */ public void setTimeInMillis(long arg0) { super.setTimeInMillis(arg0); } /* ------------------------------------------------------------------------------- */ /** * @see java.util.Calendar#getTimeInMillis() */ public long getTimeInMillis() { return super.getTimeInMillis(); } } }