View Javadoc

1   /**
2    *
3    * Copyright 2003-2004 The Apache Software Foundation
4    *
5    *  Licensed under the Apache License, Version 2.0 (the "License");
6    *  you may not use this file except in compliance with the License.
7    *  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  
18  //
19  // This source code implements specifications defined by the Java
20  // Community Process. In order to remain compliant with the specification
21  // DO NOT add / change / or delete method signatures!
22  //
23  
24  package javax.servlet.http;
25  
26  import java.io.IOException;
27  import java.util.Hashtable;
28  import java.util.ResourceBundle;
29  import java.util.StringTokenizer;
30  import javax.servlet.ServletInputStream;
31  
32  /**
33   * @deprecated As of Java(tm) Servlet API 2.3. These methods were only useful
34   * with the default encoding and have been moved to the request interfaces.
35   *
36   * @version $Rev: 46019 $ $Date: 2004-09-14 02:56:06 -0700 (Tue, 14 Sep 2004) $
37   */
38  public class HttpUtils {
39      private static final String LSTRING_FILE = "javax.servlet.http.LocalStrings";
40      private static ResourceBundle lStrings = ResourceBundle.getBundle(LSTRING_FILE);
41  
42      /**
43       * Constructs an empty <code>HttpUtils</code> object.
44       */
45      public HttpUtils() {
46      }
47  
48      /**
49       * Parses a query string passed from the client to the server and builds a
50       * <code>HashTable</code> object with key-value pairs. The query string
51       * should be in the form of a string packaged by the GET or POST method,
52       * that is, it should have key-value pairs in the form <i>key=value</i>,
53       * with each pair separated from the next by a &amp; character.
54       *
55       * <p>A key can appear more than once in the query string with different
56       * values. However, the key appears only once in the hashtable, with its
57       * value being an array of strings containing the multiple values sent
58       * by the query string.
59       *
60       * <p>The keys and values in the hashtable are stored in their decoded
61       * form, so any + characters are converted to spaces, and characters
62       * sent in hexadecimal notation (like <i>%xx</i>) are converted to ASCII
63       * characters.
64       *
65       * @param s a string containing the query to be parsed
66       *
67       * @return a <code>HashTable</code> object built from the parsed key-value
68       * pairs
69       *
70       * @exception IllegalArgumentException if the query string is invalid
71       */
72      static public Hashtable parseQueryString(String s) {
73  
74          String valArray[] = null;
75  
76          if (s == null) {
77              throw new IllegalArgumentException();
78          }
79          Hashtable ht = new Hashtable();
80          StringBuffer sb = new StringBuffer();
81          StringTokenizer st = new StringTokenizer(s, "&");
82          while (st.hasMoreTokens()) {
83              String pair = (String) st.nextToken();
84              int pos = pair.indexOf('=');
85              if (pos == -1) {
86                  // XXX
87                  // should give more detail about the illegal argument
88                  throw new IllegalArgumentException();
89              }
90              String key = parseName(pair.substring(0, pos), sb);
91              String val = parseName(pair.substring(pos + 1, pair.length()), sb);
92              if (ht.containsKey(key)) {
93                  String oldVals[] = (String[]) ht.get(key);
94                  valArray = new String[oldVals.length + 1];
95                  for (int i = 0; i < oldVals.length; i++)
96                      valArray[i] = oldVals[i];
97                  valArray[oldVals.length] = val;
98              } else {
99                  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