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