001: /*
002: * Enhydra Java Application Server Project
003: *
004: * The contents of this file are subject to the Enhydra Public License
005: * Version 1.1 (the "License"); you may not use this file except in
006: * compliance with the License. You may obtain a copy of the License on
007: * the Enhydra web site ( http://www.enhydra.org/ ).
008: *
009: * Software distributed under the License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
011: * the License for the specific terms governing rights and limitations
012: * under the License.
013: *
014: * The Initial Developer of the Enhydra Application Server is Lutris
015: * Technologies, Inc. The Enhydra Application Server and portions created
016: * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
017: * All Rights Reserved.
018: *
019: * Contributor(s):
020: *
021: * $Id: HttpUtils.java,v 1.2 2006-06-15 13:47:00 sinisa Exp $
022: */
023:
024: package com.lutris.http;
025:
026: import java.io.IOException;
027: import java.io.InputStream;
028: import java.net.URLDecoder;
029: import java.util.Hashtable;
030: import java.util.StringTokenizer;
031:
032: import com.lutris.appserver.server.httpPresentation.HttpPresentationException;
033: import com.lutris.appserver.server.httpPresentation.HttpPresentationRequest;
034:
035: /**
036: * Utility methods useful to for HTTP.
037: */
038: public class HttpUtils {
039: /**
040: * Adds the pair <b>key</b> and <b>value</b> to <b>table</b>.
041: * If <b>key</b> already exists, then <b>value</b> is added
042: * as the end of an array of existing values, and
043: * the new array is assigned as the value for <b>key</b>.
044: * If <b>key</b> does not yet exist, the <b>value</b> is assigned
045: * as the value for <b>key</b>.
046: *
047: * @param table The table to add to.
048: * @param key The key to assign the value to.
049: * @param value The value to assign
050: */
051: private static void AddKey(Hashtable table, String key, String value) {
052: Object obj = table.get(key);
053: if (obj == null) {
054: // First time "key" appears.
055: table.put(key, value);
056: } else {
057: if (obj instanceof String) {
058: // Second time. Entry becomes an array of String.
059: String[] newarray = new String[2];
060: newarray[0] = obj.toString();
061: newarray[1] = value;
062: table.put(key, newarray);
063: newarray = null;
064: } else if (obj instanceof String[]) {
065: // Third and subsequent times.
066: String[] oldarray = (String[]) obj;
067: String[] newarray = new String[oldarray.length + 1];
068: for (int i = 0; i < oldarray.length; i++) {
069: newarray[i] = oldarray[i];
070: }
071: newarray[oldarray.length] = value;
072: oldarray = null;
073: table.put(key, newarray);
074: newarray = null;
075: } else {
076: // We should never see anything other than String
077: // or String[] in this hash table.
078: throw new Error("Internal Error - "
079: + "Unexpected type in QueryString Hashtable.");
080: }
081: }
082: }
083:
084: /**
085: * Trims the left-hand side of a URL until the character '?'
086: * is reached. This is useful for raw URLs that need to be
087: * parsed as query strings.
088: *
089: * @param s The string to trim.
090: * @return The trimmed string.
091: */
092: public static String trimQueryString(String s) {
093: int pos = s.indexOf("?");
094: if (pos >= 0)
095: return s.substring(pos + 1);
096: return s;
097: }
098:
099: /**
100: * Decodes a query string in the format specified by the MIME
101: * type <code>"application/x-www-form-urlencoded"</code>. Such a
102: * string consists of encoded key/value pairs in the form
103: * <code>key=value</code>. A hash table is returned with each
104: * value indexed by its corresponding key. If a key appears once
105: * in the query string, then the value will be of type
106: * <code>String</code>. If the key appears more than once, then
107: * the value will be an array of type <code>String</code>.
108: *
109: * @param s The string to parse.
110: * @return A Hashtable containing the values indexed by
111: * their corresponding keys.
112: */
113: public static Hashtable parseQueryString(String s) {
114: String qs = s.replace('?', '&');
115: StringTokenizer tok = new StringTokenizer(qs, "&", false);
116: Hashtable table = new Hashtable();
117: String pair, key, value;
118: while (tok.hasMoreTokens()) {
119: pair = tok.nextToken();
120: int i = pair.indexOf("=");
121: if (i >= 0) {
122: key = URLDecoder.decode(pair.substring(0, i));
123: value = URLDecoder.decode(pair.substring(i + 1));
124: AddKey(table, key, value);
125: } else {
126: int j = pair.indexOf(",");
127: if (j >= 0) {
128: // Might be an image map coord. pair.
129: String xstr = pair.substring(0, j).trim();
130: xstr = URLDecoder.decode(xstr);
131: String ystr = pair.substring(j + 1).trim();
132: ystr = URLDecoder.decode(ystr);
133: try {
134: int xcoord = Integer.parseInt(xstr, 10);
135: int ycoord = Integer.parseInt(ystr, 10);
136: // Yes, we have image map coordinates.
137: AddKey(table, "_imap_x", "" + xcoord);
138: AddKey(table, "_imap_y", "" + ycoord);
139: } catch (Throwable t) {
140: AddKey(table, "_garbage", URLDecoder
141: .decode(pair));
142: }
143: } else {
144: AddKey(table, "_garbage", URLDecoder.decode(pair));
145: }
146: }
147:
148: }
149: return table;
150: }
151:
152: /**
153: * Parses FORM data posted by a client using the HTTP POST method
154: * and the <code>application/x-www-form-urlencoded</code> MIME type.
155: *
156: * @param len The maximum number of bytes to read from the input
157: * stream.
158: * @param in The input stream from which to read the form data.
159: * @return A Hashtable containing the values indexed by
160: * their corresponding keys.
161: * @exception IllegalArgumentException If the query string is
162: * not formatted correctly.
163: * @exception IOException If an I/O error occurs while reading the
164: * input stream.
165: */
166: public static Hashtable parsePostData(int len, InputStream in)
167: throws IllegalArgumentException, IOException {
168: byte[] bytes = new byte[len];
169: int n, count = 0;
170: boolean eof = false;
171: while ((count < len)
172: && ((n = in.read(bytes, count, len - count)) > 0))
173: count += n;
174: char[] chars = new char[count];
175: // Convert signed bytes directly to ASCII compatibility Unicode
176: // chars using two's complement byte signed-unsigned conversion.
177: for (n = 0; n < count; n++)
178: chars[n] = (char) ((((int) bytes[n]) + 0x100) & 0xff);
179: bytes = null;
180: return parseQueryString(new String(chars));
181: }
182:
183: /**
184: * Obtains the URL used by the client to make the current request,
185: * without the query parameters. Returns a StringBuffer object that
186: * can be appended to, if the query string must be appended.
187: *
188: * @param req presentation request object from which
189: * to extract the URL.
190: * @return A string buffer object containing the extracted
191: * URL.
192: * @exception HttpPresentationException If an exception is generated
193: * by the <code>req</code> object.
194: */
195: public static StringBuffer getRequestURL(HttpPresentationRequest req)
196: throws HttpPresentationException {
197: StringBuffer buf = new StringBuffer();
198: buf.append("http://");
199: buf.append(req.getServerName());
200: int port = req.getServerPort();
201: if (port != 80)
202: buf.append(":" + port);
203: buf.append(req.getRequestURI());
204: return buf;
205: }
206: }
|