001 /**
002 *
003 * Copyright 2003-2004 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 //
019 // This source code implements specifications defined by the Java
020 // Community Process. In order to remain compliant with the specification
021 // DO NOT add / change / or delete method signatures!
022 //
023
024 package javax.servlet.http;
025
026 import java.io.IOException;
027 import java.util.Hashtable;
028 import java.util.ResourceBundle;
029 import java.util.StringTokenizer;
030 import javax.servlet.ServletInputStream;
031
032 /**
033 * @deprecated As of Java(tm) Servlet API 2.3. These methods were only useful
034 * with the default encoding and have been moved to the request interfaces.
035 *
036 * @version $Rev: 46019 $ $Date: 2004-09-14 02:56:06 -0700 (Tue, 14 Sep 2004) $
037 */
038 public class HttpUtils {
039 private static final String LSTRING_FILE = "javax.servlet.http.LocalStrings";
040 private static ResourceBundle lStrings = ResourceBundle.getBundle(LSTRING_FILE);
041
042 /**
043 * Constructs an empty <code>HttpUtils</code> object.
044 */
045 public HttpUtils() {
046 }
047
048 /**
049 * Parses a query string passed from the client to the server and builds a
050 * <code>HashTable</code> object with key-value pairs. The query string
051 * should be in the form of a string packaged by the GET or POST method,
052 * that is, it should have key-value pairs in the form <i>key=value</i>,
053 * with each pair separated from the next by a & character.
054 *
055 * <p>A key can appear more than once in the query string with different
056 * values. However, the key appears only once in the hashtable, with its
057 * value being an array of strings containing the multiple values sent
058 * by the query string.
059 *
060 * <p>The keys and values in the hashtable are stored in their decoded
061 * form, so any + characters are converted to spaces, and characters
062 * sent in hexadecimal notation (like <i>%xx</i>) are converted to ASCII
063 * characters.
064 *
065 * @param s a string containing the query to be parsed
066 *
067 * @return a <code>HashTable</code> object built from the parsed key-value
068 * pairs
069 *
070 * @exception IllegalArgumentException if the query string is invalid
071 */
072 static public Hashtable parseQueryString(String s) {
073
074 String valArray[] = null;
075
076 if (s == null) {
077 throw new IllegalArgumentException();
078 }
079 Hashtable ht = new Hashtable();
080 StringBuffer sb = new StringBuffer();
081 StringTokenizer st = new StringTokenizer(s, "&");
082 while (st.hasMoreTokens()) {
083 String pair = (String) st.nextToken();
084 int pos = pair.indexOf('=');
085 if (pos == -1) {
086 // XXX
087 // should give more detail about the illegal argument
088 throw new IllegalArgumentException();
089 }
090 String key = parseName(pair.substring(0, pos), sb);
091 String val = parseName(pair.substring(pos + 1, pair.length()), sb);
092 if (ht.containsKey(key)) {
093 String oldVals[] = (String[]) ht.get(key);
094 valArray = new String[oldVals.length + 1];
095 for (int i = 0; i < oldVals.length; i++)
096 valArray[i] = oldVals[i];
097 valArray[oldVals.length] = val;
098 } else {
099 valArray = new String[1];
100 valArray[0] = val;
101 }
102 ht.put(key, valArray);
103 }
104 return ht;
105 }
106
107 /**
108 * Parses data from an HTML form that the client sends to the server using
109 * the HTTP POST method and the <i>application/x-www-form-urlencoded</i>
110 * MIME type.
111 *
112 * <p>The data sent by the POST method contains key-value pairs. A key can
113 * appear more than once in the POST data with different values. However,
114 * the key appears only once in the hashtable, with its value being an
115 * array of strings containing the multiple values sent by the POST method.
116 *
117 * <p>The keys and values in the hashtable are stored in their decoded
118 * form, so any + characters are converted to spaces, and characters sent
119 * in hexadecimal notation (like <i>%xx</i>) are converted to ASCII
120 * characters.
121 *
122 * @param len an integer specifying the length, in characters, of the
123 * <code>ServletInputStream</code> object that is also passed to this
124 * method
125 *
126 * @param in the <code>ServletInputStream</code> object that contains the
127 * data sent from the client
128 *
129 * @return a <code>HashTable</code> object built from the parsed key-value
130 * pairs
131 *
132 * @exception IllegalArgumentException if the data sent by the POST
133 * method is invalid
134 */
135 static public Hashtable parsePostData(int len, ServletInputStream in) {
136 // XXX
137 // should a length of 0 be an IllegalArgumentException
138
139 if (len <= 0) {
140 return new Hashtable(); // cheap hack to return an empty hash
141 }
142
143 if (in == null) {
144 throw new IllegalArgumentException();
145 }
146
147 //
148 // Make sure we read the entire POSTed body.
149 //
150 byte[] postedBytes = new byte[len];
151 try {
152 int offset = 0;
153
154 do {
155 int inputLen = in.read(postedBytes, offset, len - offset);
156 if (inputLen <= 0) {
157 String msg = lStrings.getString("err.io.short_read");
158 throw new IllegalArgumentException(msg);
159 }
160 offset += inputLen;
161 } while ((len - offset) > 0);
162
163 } catch (IOException e) {
164 throw new IllegalArgumentException(e.getMessage());
165 }
166
167 // XXX we shouldn't assume that the only kind of POST body
168 // is FORM data encoded using ASCII or ISO Latin/1 ... or
169 // that the body should always be treated as FORM data.
170 //
171
172 try {
173 String postedBody = new String(postedBytes, 0, len, "8859_1");
174 return parseQueryString(postedBody);
175 } catch (java.io.UnsupportedEncodingException e) {
176 // XXX function should accept an encoding parameter & throw this
177 // exception. Otherwise throw something expected.
178 throw new IllegalArgumentException(e.getMessage());
179 }
180 }
181
182 /**
183 * Parse a name in the query string.
184 */
185 static private String parseName(String s, StringBuffer sb) {
186 sb.setLength(0);
187 for (int i = 0; i < s.length(); i++) {
188 char c = s.charAt(i);
189 switch (c) {
190 case '+':
191 sb.append(' ');
192 break;
193 case '%':
194 try {
195 sb.append((char) Integer.parseInt(s.substring(i + 1, i + 3),
196 16));
197 i += 2;
198 } catch (NumberFormatException e) {
199 // XXX
200 // need to be more specific about illegal arg
201 throw new IllegalArgumentException();
202 } catch (StringIndexOutOfBoundsException e) {
203 String rest = s.substring(i);
204 sb.append(rest);
205 if (rest.length() == 2)
206 i++;
207 }
208
209 break;
210 default:
211 sb.append(c);
212 break;
213 }
214 }
215 return sb.toString();
216 }
217
218 /**
219 * Reconstructs the URL the client used to make the request, using
220 * information in the <code>HttpServletRequest</code> object. The returned
221 * URL contains a protocol, server name, port number, and server path, but
222 * it does not include query string parameters.
223 *
224 * <p>Because this method returns a <code>StringBuffer</code>, not a
225 * string, you can modify the URL easily, for example, to append query
226 * parameters.
227 *
228 * <p>This method is useful for creating redirect messages and for
229 * reporting errors.
230 *
231 * @param req a <code>HttpServletRequest</code> object containing the
232 * client's request
233 *
234 * @return a <code>StringBuffer</code> object containing the reconstructed
235 * URL
236 */
237 public static StringBuffer getRequestURL(HttpServletRequest req) {
238 StringBuffer url = new StringBuffer();
239 String scheme = req.getScheme();
240 int port = req.getServerPort();
241 String urlPath = req.getRequestURI();
242
243 //String servletPath = req.getServletPath ();
244 //String pathInfo = req.getPathInfo ();
245
246 url.append(scheme); // http, https
247 url.append("://");
248 url.append(req.getServerName());
249 if ((scheme.equals("http") && port != 80)
250 || (scheme.equals("https") && port != 443)) {
251 url.append(':');
252 url.append(req.getServerPort());
253 }
254 //if (servletPath != null)
255 // url.append (servletPath);
256 //if (pathInfo != null)
257 // url.append (pathInfo);
258 url.append(urlPath);
259 return url;
260 }
261 }
262
263
264