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