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