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