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