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 &amp; 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