Mercurial > hg > blitz_condensed
diff src/com/go/trove/net/HttpHeaderMap.java @ 0:3dc0c5604566
Initial checkin of blitz 2.0 fcs - no installer yet.
author | Dan Creswell <dan.creswell@gmail.com> |
---|---|
date | Sat, 21 Mar 2009 11:00:06 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/go/trove/net/HttpHeaderMap.java Sat Mar 21 11:00:06 2009 +0000 @@ -0,0 +1,541 @@ +/* ==================================================================== + * Trove - Copyright (c) 1997-2000 Walt Disney Internet Group + * ==================================================================== + * The Tea Software License, Version 1.1 + * + * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes software developed by the + * Walt Disney Internet Group (http://opensource.go.com/)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must + * not be used to endorse or promote products derived from this + * software without prior written permission. For written + * permission, please contact opensource@dig.com. + * + * 5. Products derived from this software may not be called "Tea", + * "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet", + * "Kettle", "Trove" or "BeanDoc" appear in their name, without prior + * written permission of the Walt Disney Internet Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * For more information about Tea, please see http://opensource.go.com/. + */ + +package com.go.trove.net; + +import java.util.*; +import java.text.*; +import java.io.*; +import com.go.trove.io.*; + +/****************************************************************************** + * + * @author Brian S O'Neill + * @version + * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/07/16 <!-- $--> + */ +public class HttpHeaderMap implements Map, Serializable { + private final static TimeZone GMT_ZONE; + private final static DateFormat DATE_PARSER_1; + private final static DateFormat DATE_PARSER_2; + private final static String[] DAYS; + private final static String[] MONTHS; + + static { + GMT_ZONE = TimeZone.getTimeZone("GMT"); + + DATE_PARSER_1 = + new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss", Locale.US); + DATE_PARSER_1.setTimeZone(GMT_ZONE); + DATE_PARSER_1.setLenient(true); + + DATE_PARSER_2 = + new SimpleDateFormat("EEEE, dd-MMM-yy HH:mm:ss", Locale.US); + DATE_PARSER_2.setTimeZone(GMT_ZONE); + DATE_PARSER_2.setLenient(true); + + DateFormatSymbols symbols = new DateFormatSymbols(Locale.US); + DAYS = symbols.getShortWeekdays(); + MONTHS = symbols.getShortMonths(); + } + + private static void appendDate(CharToByteBuffer buffer, Date d) + throws IOException + { + Calendar c = new GregorianCalendar(GMT_ZONE, Locale.US); + c.setTime(d); + + buffer.append(DAYS[c.get(Calendar.DAY_OF_WEEK)]); + buffer.append(", "); + append2Digit(buffer, c.get(Calendar.DAY_OF_MONTH)); + buffer.append(' '); + buffer.append(MONTHS[c.get(Calendar.MONTH)]); + buffer.append(' '); + append4Digit(buffer, c.get(Calendar.YEAR)); + buffer.append(' '); + append2Digit(buffer, c.get(Calendar.HOUR_OF_DAY)); + buffer.append(':'); + append2Digit(buffer, c.get(Calendar.MINUTE)); + buffer.append(':'); + append2Digit(buffer, c.get(Calendar.SECOND)); + buffer.append(" GMT"); + } + + private static void append2Digit(CharToByteBuffer buffer, int val) + throws IOException + { + buffer.append((char)(val / 10 + '0')); + buffer.append((char)(val % 10 + '0')); + } + + private static void append4Digit(CharToByteBuffer buffer, int val) + throws IOException + { + if (val < 1000 || val > 9999) { + buffer.append(Integer.toString(val)); + } + else { + buffer.append((char)(val / 1000 + '0')); + buffer.append((char)(val / 100 % 10 + '0')); + buffer.append((char)(val / 10 % 10 + '0')); + buffer.append((char)(val % 10 + '0')); + } + } + + private Map mMap; + + public HttpHeaderMap() { + mMap = new TreeMap(String.CASE_INSENSITIVE_ORDER); + } + + /** + * Read and parse headers from the given InputStream until a blank line is + * reached. Except for Cookies, all other headers that contain multiple + * fields delimited by commas are parsed into multiple headers. + * + * @param in stream to read from + */ + public void readFrom(InputStream in) throws IOException { + readFrom(in, new byte[80]); + } + + /** + * Read and parse headers from the given InputStream until a blank line is + * reached. Except for Cookies, all other headers that contain multiple + * fields delimited by commas are parsed into multiple headers. + * + * @param in stream to read from + * @param buffer temporary buffer to use + */ + public void readFrom(InputStream in, byte[] buffer) throws IOException { + String header; + while ((header = HttpUtils.readLine(in, buffer, 4000)) != null) { + if (header.length() == 0) { + break; + } + processHeaderLine(header); + } + } + + /** + * Read and parse headers from the given InputStream until a blank line is + * reached. Except for Cookies, all other headers that contain multiple + * fields delimited by commas are parsed into multiple headers. + * + * @param in stream to read from + * @param buffer temporary buffer to use + */ + public void readFrom(InputStream in, char[] buffer) throws IOException { + String header; + while ((header = HttpUtils.readLine(in, buffer, 4000)) != null) { + if (header.length() == 0) { + break; + } + processHeaderLine(header); + } + } + + private void processHeaderLine(String header) { + int index = header.indexOf(':'); + if (index < 0) { + return; + } + + String name = header.substring(0, index); + + String value; + int length = header.length(); + parseValue: { + do { + if (++index >= length) { + value = ""; + break parseValue; + } + } while (header.charAt(index) == ' '); + + value = header.substring(index); + } + + if ("Cookie".equalsIgnoreCase(name) || + "Set-Cookie".equalsIgnoreCase(name) || + (value.indexOf(',') == 3 && + (value.startsWith("Mon") || + value.startsWith("Tue") || + value.startsWith("Wed") || + value.startsWith("Thu") || + value.startsWith("Fri") || + value.startsWith("Sat") || + value.startsWith("Sun")))) { + + add(name, value.trim()); + } + else { + // Parse up header by commas unless its a Cookie or value + // is a date. + while ((index = value.indexOf(',')) >= 0) { + add(name, value.substring(0, index).trim()); + value = value.substring(index + 1); + } + + add(name, value.trim()); + } + } + + public void writeTo(OutputStream out) throws IOException { + CharToByteBuffer buffer = new FastCharToByteBuffer + (new DefaultByteBuffer(), "8859_1"); + buffer = new InternedCharToByteBuffer(buffer); + appendTo(buffer); + buffer.writeTo(out); + } + + public void appendTo(CharToByteBuffer buffer) throws IOException { + Iterator it = mMap.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = (Map.Entry)it.next(); + Object key = entry.getKey(); + Object value = entry.getValue(); + + if (key == null || value == null) { + continue; + } + + String strKey = key.toString(); + + if (value instanceof List) { + Iterator values = ((List)value).iterator(); + if ("Set-Cookie".equalsIgnoreCase(strKey)) { + while (values.hasNext()) { + buffer.append(strKey); + buffer.append(": "); + value = values.next(); + if (value instanceof Date) { + appendDate(buffer, (Date)value); + } + else { + buffer.append(value.toString()); + } + buffer.append("\r\n"); + } + } + else { + // Write multiple headers together except Cookies. + int count = 0; + while (values.hasNext()) { + value = values.next(); + + if (count < 0) { + buffer.append("\r\n"); + count = 0; + } + if (count++ == 0) { + buffer.append(strKey); + buffer.append(": "); + } + + if (value instanceof Date) { + // Comma in date, so must isolate header. + if (count > 1) { + buffer.append("\r\n"); + buffer.append(strKey); + buffer.append(": "); + } + appendDate(buffer, (Date)value); + count = -1; + } + else { + String strVal = value.toString(); + if (strVal.indexOf(',') < 0) { + if (count > 1) { + buffer.append(','); + } + buffer.append(strVal); + } + else { + // Comma in value, so must isolate header. + if (count > 1) { + buffer.append("\r\n"); + buffer.append(strKey); + buffer.append(": "); + } + buffer.append(strVal); + count = -1; + } + } + } + buffer.append("\r\n"); + } + } + else { + buffer.append(strKey); + buffer.append(": "); + if (value instanceof Date) { + appendDate(buffer, (Date)value); + } + else { + buffer.append(value.toString()); + } + buffer.append("\r\n"); + } + } + } + + public int size() { + return mMap.size(); + } + + public boolean isEmpty() { + return mMap.isEmpty(); + } + + public boolean containsKey(Object key) { + return mMap.containsKey(key); + } + + public boolean containsValue(Object value) { + Iterator it = mMap.values().iterator(); + while (it.hasNext()) { + Object obj = it.next(); + if (obj instanceof List) { + Iterator it2 = ((List)obj).iterator(); + while (it2.hasNext()) { + obj = it2.next(); + return (value == null) ? obj == null : value.equals(obj); + } + } + else { + return (value == null) ? obj == null : value.equals(obj); + } + } + return false; + } + + /** + * Returns the first value associated with the given key. + */ + public Object get(Object key) { + Object value = mMap.get(key); + if (value instanceof List) { + return ((List)value).get(0); + } + else { + return value; + } + } + + public String getString(Object key) { + Object obj = get(key); + if (obj instanceof String) { + return (String)obj; + } + else if (obj != null) { + return obj.toString(); + } + return null; + } + + public Integer getInteger(Object key) { + Object obj = get(key); + if (obj instanceof Integer) { + return (Integer)obj; + } + else if (obj != null) { + try { + return new Integer(obj.toString()); + } + catch (NumberFormatException e) { + } + } + return null; + } + + public Date getDate(Object key) { + Object obj = get(key); + if (obj instanceof Date) { + return (Date)obj; + } + else if (obj == null) { + return null; + } + + String val = obj.toString(); + + // Trim after a possible ';' to separate the date + // from other optional data. + int index = val.indexOf(';'); + if (index >= 0) { + val = val.substring(0, index); + } + + Date date; + + try { + synchronized (DATE_PARSER_1) { + date = DATE_PARSER_1.parse(val); + } + } + catch (ParseException e) { + try { + synchronized (DATE_PARSER_2) { + date = DATE_PARSER_2.parse(val); + } + } + catch (ParseException e2) { + return null; + } + } + + return date; + } + + + /** + * Returns all the values associated with the given key. Changes to the + * returned list will be reflected in this map. + */ + public List getAll(Object key) { + Object value = mMap.get(key); + if (value instanceof List) { + return ((List)value); + } + else { + List list = new ArrayList(); + if (value != null || mMap.containsKey(key)) { + list.add(value); + } + mMap.put(key, list); + return list; + } + } + + /** + * May return a list if the key previously mapped to multiple values. + */ + public Object put(Object key, Object value) { + if (value instanceof List) { + return mMap.put(key, new ArrayList((List)value)); + } + else { + return mMap.put(key, value); + } + } + + /** + * Add more than one value associated with the given key. + */ + public void add(Object key, Object value) { + Object existing = mMap.get(key); + if (existing instanceof List) { + if (value instanceof List) { + ((List)existing).addAll((List)value); + } + else { + ((List)existing).add(value); + } + } + else if (existing == null && !mMap.containsKey(key)) { + if (value instanceof List) { + mMap.put(key, new ArrayList((List)value)); + } + else { + mMap.put(key, value); + } + } + else { + List list = new ArrayList(); + list.add(existing); + if (value instanceof List) { + list.addAll((List)value); + } + else { + list.add(value); + } + mMap.put(key, list); + } + } + + public Object remove(Object key) { + return mMap.remove(key); + } + + public void putAll(Map map) { + mMap.putAll(map); + } + + public void clear() { + mMap.clear(); + } + + public Set keySet() { + return mMap.keySet(); + } + + public Collection values() { + return mMap.values(); + } + + public Set entrySet() { + return mMap.entrySet(); + } + + public boolean equals(Object obj) { + return mMap.equals(obj); + } + + public int hashCode() { + return mMap.hashCode(); + } + + public String toString() { + return super.toString() + ":" + mMap.toString(); + } +}