/* * MimeHeaders.java * * Brazil project web application toolkit, * export version: 2.3 * Copyright (c) 1999-2005 Sun Microsystems, Inc. * * Sun Public License Notice * * The contents of this file are subject to the Sun Public License Version * 1.0 (the "License"). You may not use this file except in compliance with * the License. A copy of the License is included as the file "license.terms", * and also available at http://www.sun.com/ * * The Original Code is from: * Brazil project web application toolkit release 2.3. * The Initial Developer of the Original Code is: cstevens. * Portions created by cstevens are Copyright (C) Sun Microsystems, Inc. * All Rights Reserved. * * Contributor(s): cstevens, suhler. * * Version: 2.4 * Created by cstevens on 99/09/15 * Last modified by suhler on 05/12/08 13:03:48 * * Version Histories: * * 2.4 05/12/08-13:03:48 (suhler) * add limits on request size to prevent DOS attacks * * 2.3 05/05/25-12:56:31 (suhler) * added the ability to read new headers, replacing old ones * * 2.2 04/11/30-15:19:46 (suhler) * fixed sccs version string * * 2.1 02/10/01-16:37:04 (suhler) * version change * * 1.12 99/11/16-14:48:57 (cstevens) * Bug used to cause continuation line to be added to the _first_ occurence of * the key, not the most recent occurence (namely, the previous line). * * 1.11 99/10/26-18:56:28 (cstevens) * Change MimeHeaders so it uses "put" instead of "set", to be compatible with * names chosen by Hashtable and StringMap. * * 1.10 99/10/14-12:59:44 (cstevens) * Documentation. * * 1.9 99/10/07-13:04:16 (cstevens) * docs * * 1.8 99/10/04-17:16:44 (cstevens) * * 1.7 99/10/04-16:14:27 (cstevens) * Merged changes between child workspace "/home/cstevens/ws/brazil/naws" and * parent workspace "/export/ws/brazil/naws". * * 1.5.1.1 99/10/04-16:03:47 (cstevens) * Documentation for LexML and StringMap. * * 1.6 99/10/01-11:23:07 (suhler) * Merged changes between child workspace "/home/suhler/brazil/naws" and * parent workspace "/net/mack.eng/export/ws/brazil/naws". * * 1.4.1.1 99/10/01-11:21:10 (suhler) * added toString() * * 1.5 99/09/30-10:45:53 (cstevens) * Easier for debugging. * * 1.4 99/09/15-15:56:46 (cstevens) * efficiency * * 1.3 99/09/15-14:51:57 (cstevens) * import *; * * 1.2 99/09/15-14:41:41 (cstevens) * Rewritign http server to make it easier to proxy requests. * * 1.2 99/09/15-14:31:37 (Codemgr) * SunPro Code Manager data about conflicts, renames, etc... * Name history : 1 0 util/http/MimeHeaders.java * * 1.1 99/09/15-14:31:36 (cstevens) * date and time created 99/09/15 14:31:36 by cstevens * */ package sunlabs.brazil.util.http; import sunlabs.brazil.util.StringMap; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; /** * This class is build on top of the StringMap class and * provides added methods that are of help when manipulating MIME headers. * By creating an instance of this class, the user can conveniently read, * write, and modify MIME headers. * * @author Colin Stevens (colin.stevens@sun.com) * @version 2.4 */ public class MimeHeaders extends StringMap { /* * Place arbitrary limits on header size to mitigate DOS attacts. */ public static final int MAX_LINE=1024; public static final int MAX_LINES=1024; /** * Creates a new, empty MimeHeaders object. */ public MimeHeaders() { } /** * Creates a new MimeHeaders object and then initializes * it by reading MIME headers from the specified input stream. * * @param in * The input stream to read. */ public MimeHeaders(HttpInputStream in) throws IOException { read(in); } /** * Reads MIME headers from the specified input stream. This method * reads up to and consumes the blank line that marks the end of the * MIME headers. It also stops reading if it reaches the end of * the input stream. *

* The MIME headers read from the input stream are stored in this * MimeHeaders object. All headers read are added * to the existing headers; the new headers do not replace the * existing ones. The order of the headers in this object will * reflect the order of the headers from the input stream, but * space characters surrounding the keys and values are not preserved. *

* In a set of MIME headers, the given key may appear multiple times * (that is, on multiple lines, not necessarily consecutively). * In that case, that key will appear multiple times in this * MimeHeaders object also. The HTTP spec says that * if a given key appears multiple times in a set of MIME headers, the * values can be concatenated together with commas * between them. However, in practice, it appears that some browsers * and HTTP servers get confused when encountering such collapsed * MIME headers, for instance, the Yahoo mail reader program. *

* MIME headers also support the idea of continuation lines, where * a key (and optionally its value) is followed on subsequent line(s) by * another value without a key. The HTTP spec says that in this case * the values can be concatenated together with space characters * between them. In practice, joining continuation lines together * does not seem to confuse any browsers or HTTP servers. This method * joins continuation lines together by putting the space-equivalent * characters "\r\n\t" between the values so it can be easily parsed * with StringTokenizer and also easily written to * an output stream in a format that actually preserves its formatting * as a continuation line. * * @param in * The input stream to read from. * * @throws IOException * if the input stream throws an IOException while being * read. */ public void read(HttpInputStream in) throws IOException { read(in, false); } /** * Reads MIME headers from the specified input stream. * Same as (@link #read(HttpInputStream in)), only existing keys may * be replaced or augmented. * * @param in * The input stream to read from. * @param shouldReplace * If true, existing keys are replaced instead of * augmented. * * @throws IOException * if the input stream throws an IOException while being * read. * @see #read(HttpInputStream in) */ public void read(HttpInputStream in, boolean shouldReplace) throws IOException { int count=0; while (count++ < MAX_LINES) { String line = in.readLine(MAX_LINE); if ((line == null) || (line.length() == 0)) { break; } if (count >= MAX_LINES) { throw new IOException("Too many headers in HTTP request"); } if (Character.isSpaceChar(line.charAt(0)) == false) { int index = line.indexOf(':'); if (index >= 0) { String key = line.substring(0, index).trim(); String value = line.substring(index + 1).trim(); if (shouldReplace) { put(key, value); } else { add(key, value); } } } else if (size() > 0) { String value = get(size() - 1); put(size() - 1, value + "\r\n\t" + line.trim()); } } } /** * Writes this MimeHeaders object to the given output * stream. This method does not write a blank line after * the headers are written. * * @param out * The output stream. */ public void print(OutputStream out) { print(new PrintStream(out)); } /** * Writes this MimeHeaders object to the given output * stream. This method does not write a blank line after * the headers are written. * * @param out * The output stream. */ public void print(PrintStream out) { int length = size(); for (int i = 0; i < length; i++) { String key = getKey(i); String value = get(i); out.print(key + ": " + value + "\r\n"); } } /** * Maps the given case-insensitive key to the specified value if the * key does not already exist in this MimeHeaders object. *

* Often, when dealing with MIME headers, the user will want to set * a header only if that header is not already set. * * @param key * The new key. May not be null. * * @param value * The new value. May be null. */ public void putIfNotPresent(String key, String value) { if (get(key) == null) { put(key, value); } } /** * Maps the given case-insensitive key to the specified value in this * MimeHeaders object, replacing the old value. *

* This is convenience method that automatically converts the * integer value to a string before calling the underlying * put method. * * @param key * The new key. May not be null. * * @param value * The new value. */ public void put(String key, int value) { put(key, Integer.toString(value)); } /** * Adds a mapping for the given case-insensitive key to the specified * value in this MimeHeaders object. It leaves any existing * key-value mapping alone. *

* This is convenience method that automatically converts the * integer value to a string before calling the underlying * add method. * * @param key * The new key. May not be null. * * @param value * The new value. */ public void add(String key, int value) { add(key, Integer.toString(value)); } /** * Copies the contents of this MimeHeaders object, * {@link #add adding} all the other's keys and values to the other. * * @param other * The MimeHeaders object to copy to. */ public void copyTo(MimeHeaders other) { int length = size(); for (int i = 0; i < length; i++) { other.add(getKey(i), get(i)); } } }