blob: 99e4ed6053c6b761cc50802ccbbb484a8300f544 [file] [log] [blame]
Michael E. Rodriguezd8af66f2005-12-08 20:55:55 +00001/*
2 * Copyright 1999,2005 The Apache Software Foundation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17
18package javax.servlet.http;
19
20import javax.servlet.ServletInputStream;
21import java.util.Hashtable;
22import java.util.ResourceBundle;
23import java.util.StringTokenizer;
24import java.io.IOException;
25
26
27/**
28 * Provides a collection of methods that are useful
29 * in writing HTTP servlets.
30 *
31 */
32
33
34public class HttpUtils {
35
36 private static final String LSTRING_FILE =
37 "javax.servlet.http.LocalStrings";
38 private static ResourceBundle lStrings =
39 ResourceBundle.getBundle(LSTRING_FILE);
40
41 static Hashtable nullHashtable = new Hashtable();
42
43
44
45 /**
46 * Constructs an empty <code>HttpUtils</code> object.
47 *
48 */
49
50 public HttpUtils() {}
51
52
53
54
55
56 /**
57 *
58 * Parses a query string passed from the client to the
59 * server and builds a <code>HashTable</code> object
60 * with key-value pairs.
61 * The query string should be in the form of a string
62 * packaged by the GET or POST method, that is, it
63 * should have key-value pairs in the form <i>key=value</i>,
64 * with each pair separated from the next by a & character.
65 *
66 * <p>A key can appear more than once in the query string
67 * with different values. However, the key appears only once in
68 * the hashtable, with its value being
69 * an array of strings containing the multiple values sent
70 * by the query string.
71 *
72 * <p>The keys and values in the hashtable are stored in their
73 * decoded form, so
74 * any + characters are converted to spaces, and characters
75 * sent in hexadecimal notation (like <i>%xx</i>) are
76 * converted to ASCII characters.
77 *
78 * @param s a string containing the query to be parsed
79 *
80 * @return a <code>HashTable</code> object built
81 * from the parsed key-value pairs
82 *
83 * @exception IllegalArgumentException if the query string
84 * is invalid
85 *
86 */
87
88 static public Hashtable parseQueryString(String s) {
89
90 String valArray[] = null;
91
92 if (s == null) {
93 throw new IllegalArgumentException();
94 }
95 Hashtable ht = new Hashtable();
96 StringBuffer sb = new StringBuffer();
97 StringTokenizer st = new StringTokenizer(s, "&");
98 while (st.hasMoreTokens()) {
99 String pair = (String)st.nextToken();
100 int pos = pair.indexOf('=');
101 if (pos == -1) {
102 // XXX
103 // should give more detail about the illegal argument
104 throw new IllegalArgumentException();
105 }
106 String key = parseName(pair.substring(0, pos), sb);
107 String val = parseName(pair.substring(pos+1, pair.length()), sb);
108 if (ht.containsKey(key)) {
109 String oldVals[] = (String []) ht.get(key);
110 valArray = new String[oldVals.length + 1];
111 for (int i = 0; i < oldVals.length; i++)
112 valArray[i] = oldVals[i];
113 valArray[oldVals.length] = val;
114 } else {
115 valArray = new String[1];
116 valArray[0] = val;
117 }
118 ht.put(key, valArray);
119 }
120 return ht;
121 }
122
123
124
125
126 /**
127 *
128 * Parses data from an HTML form that the client sends to
129 * the server using the HTTP POST method and the
130 * <i>application/x-www-form-urlencoded</i> MIME type.
131 *
132 * <p>The data sent by the POST method contains key-value
133 * pairs. A key can appear more than once in the POST data
134 * with different values. However, the key appears only once in
135 * the hashtable, with its value being
136 * an array of strings containing the multiple values sent
137 * by the POST method.
138 *
139 * <p>The keys and values in the hashtable are stored in their
140 * decoded form, so
141 * any + characters are converted to spaces, and characters
142 * sent in hexadecimal notation (like <i>%xx</i>) are
143 * converted to ASCII characters.
144 *
145 *
146 *
147 * @param len an integer specifying the length,
148 * in characters, of the
149 * <code>ServletInputStream</code>
150 * object that is also passed to this
151 * method
152 *
153 * @param in the <code>ServletInputStream</code>
154 * object that contains the data sent
155 * from the client
156 *
157 * @return a <code>HashTable</code> object built
158 * from the parsed key-value pairs
159 *
160 *
161 * @exception IllegalArgumentException if the data
162 * sent by the POST method is invalid
163 *
164 */
165
166
167 static public Hashtable parsePostData(int len,
168 ServletInputStream in)
169 {
170 // XXX
171 // should a length of 0 be an IllegalArgumentException
172
173 if (len <=0)
174 return new Hashtable(); // cheap hack to return an empty hash
175
176 if (in == null) {
177 throw new IllegalArgumentException();
178 }
179
180 //
181 // Make sure we read the entire POSTed body.
182 //
183 byte[] postedBytes = new byte [len];
184 try {
185 int offset = 0;
186
187 do {
188 int inputLen = in.read (postedBytes, offset, len - offset);
189 if (inputLen <= 0) {
190 String msg = lStrings.getString("err.io.short_read");
191 throw new IllegalArgumentException (msg);
192 }
193 offset += inputLen;
194 } while ((len - offset) > 0);
195
196 } catch (IOException e) {
197 throw new IllegalArgumentException(e.getMessage());
198 }
199
200 // XXX we shouldn't assume that the only kind of POST body
201 // is FORM data encoded using ASCII or ISO Latin/1 ... or
202 // that the body should always be treated as FORM data.
203 //
204
205 try {
206 String postedBody = new String(postedBytes, 0, len, "8859_1");
207 return parseQueryString(postedBody);
208 } catch (java.io.UnsupportedEncodingException e) {
209 // XXX function should accept an encoding parameter & throw this
210 // exception. Otherwise throw something expected.
211 throw new IllegalArgumentException(e.getMessage());
212 }
213 }
214
215
216
217
218 /*
219 * Parse a name in the query string.
220 */
221
222 static private String parseName(String s, StringBuffer sb) {
223 sb.setLength(0);
224 for (int i = 0; i < s.length(); i++) {
225 char c = s.charAt(i);
226 switch (c) {
227 case '+':
228 sb.append(' ');
229 break;
230 case '%':
231 try {
232 sb.append((char) Integer.parseInt(s.substring(i+1, i+3),
233 16));
234 i += 2;
235 } catch (NumberFormatException e) {
236 // XXX
237 // need to be more specific about illegal arg
238 throw new IllegalArgumentException();
239 } catch (StringIndexOutOfBoundsException e) {
240 String rest = s.substring(i);
241 sb.append(rest);
242 if (rest.length()==2)
243 i++;
244 }
245
246 break;
247 default:
248 sb.append(c);
249 break;
250 }
251 }
252 return sb.toString();
253 }
254
255
256
257
258 /**
259 *
260 * Reconstructs the URL the client used to make the request,
261 * using information in the <code>HttpServletRequest</code> object.
262 * The returned URL contains a protocol, server name, port
263 * number, and server path, but it does not include query
264 * string parameters.
265 *
266 * <p>Because this method returns a <code>StringBuffer</code>,
267 * not a string, you can modify the URL easily, for example,
268 * to append query parameters.
269 *
270 * <p>This method is useful for creating redirect messages
271 * and for reporting errors.
272 *
273 * @param req a <code>HttpServletRequest</code> object
274 * containing the client's request
275 *
276 * @return a <code>StringBuffer</code> object containing
277 * the reconstructed URL
278 *
279 */
280
281 public static StringBuffer getRequestURL (HttpServletRequest req) {
282 StringBuffer url = new StringBuffer ();
283 String scheme = req.getScheme ();
284 int port = req.getServerPort ();
285 String urlPath = req.getRequestURI();
286
287 //String servletPath = req.getServletPath ();
288 //String pathInfo = req.getPathInfo ();
289
290 url.append (scheme); // http, https
291 url.append ("://");
292 url.append (req.getServerName ());
293 if ((scheme.equals ("http") && port != 80)
294 || (scheme.equals ("https") && port != 443)) {
295 url.append (':');
296 url.append (req.getServerPort ());
297 }
298 //if (servletPath != null)
299 // url.append (servletPath);
300 //if (pathInfo != null)
301 // url.append (pathInfo);
302 url.append(urlPath);
303 return url;
304 }
305}
306
307
308