001    package org.apache.tapestry.json;
002    
003    /*
004     Copyright (c) 2002 JSON.org
005    
006     Permission is hereby granted, free of charge, to any person obtaining a copy
007     of this software and associated documentation files (the "Software"), to deal
008     in the Software without restriction, including without limitation the rights
009     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010     copies of the Software, and to permit persons to whom the Software is
011     furnished to do so, subject to the following conditions:
012    
013     The above copyright notice and this permission notice shall be included in all
014     copies or substantial portions of the Software.
015    
016     The Software shall be used for Good, not Evil.
017    
018     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
019     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
020     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
021     AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
022     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
023     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
024     SOFTWARE.
025     */
026    
027    import java.text.ParseException;
028    import java.util.Iterator;
029    import java.util.NoSuchElementException;
030    
031    /**
032     * Convert an HTTP header to a JSONObject and back.
033     * 
034     * @author JSON.org
035     * @version 0.1
036     */
037    public final class HTTP
038    {
039    
040        /** Carriage return/line feed. */
041        public static final String CRLF = "\r\n";
042    
043        /* defeat instantiation */
044        private HTTP()
045        {
046        }
047    
048        /**
049         * Convert an HTTP header string into a JSONObject. It can be a request
050         * header or a response header. A request header will contain
051         * 
052         * <pre>
053         * {
054         *     Method: &quot;POST&quot; (for example),
055         *     &quot;Request-URI&quot;: &quot;/&quot; (for example),
056         *     &quot;HTTP-Version&quot;: &quot;HTTP/1.1&quot; (for example)
057         *  }
058         * </pre>
059         * 
060         * A response header will contain
061         * 
062         * <pre>
063         * {
064         *     &quot;HTTP-Version&quot;: &quot;HTTP/1.1&quot; (for example),
065         *     &quot;Status-Code&quot;: &quot;200&quot; (for example),
066         *     &quot;Reason-Phrase&quot;: &quot;OK&quot; (for example)
067         *  }
068         * </pre>
069         * 
070         * In addition, the other parameters in the header will be captured, using
071         * the HTTP field names as JSON names, so that
072         * 
073         * <pre>
074         *     Date: Sun, 26 May 2002 18:06:04 GMT
075         *     Cookie: Q=q2=PPEAsg--; B=677gi6ouf29bn&amp;b=2&amp;f=s
076         *     Cache-Control: no-cache
077         * </pre>
078         * 
079         * become
080         * 
081         * <pre>
082         * {...
083         *     Date: &quot;Sun, 26 May 2002 18:06:04 GMT&quot;,
084         *     Cookie: &quot;Q=q2=PPEAsg--; B=677gi6ouf29bn&amp;b=2&amp;f=s&quot;,
085         *     &quot;Cache-Control&quot;: &quot;no-cache&quot;,
086         *  ...}
087         * </pre>
088         * 
089         * It does no further checking or conversion. It does not parse dates. It
090         * does not do '%' transforms on URLs.
091         * 
092         * @param string
093         *            An HTTP header string.
094         * @return A JSONObject containing the elements and attributes of the XML
095         *         string.
096         * @throws ParseException
097         */
098        public static JSONObject toJSONObject(String string)
099            throws ParseException
100        {
101            JSONObject o = new JSONObject();
102            HTTPTokener x = new HTTPTokener(string);
103            String t;
104    
105            t = x.nextToken();
106            if (t.toUpperCase().startsWith("HTTP"))
107            {
108    
109                // Response
110    
111                o.put("HTTP-Version", t);
112                o.put("Status-Code", x.nextToken());
113                o.put("Reason-Phrase", x.nextTo('\0'));
114                x.next();
115    
116            }
117            else
118            {
119    
120                // Request
121    
122                o.put("Method", t);
123                o.put("Request-URI", x.nextToken());
124                o.put("HTTP-Version", x.nextToken());
125            }
126    
127            // Fields
128    
129            while(x.more())
130            {
131                String name = x.nextTo(':');
132                x.next(':');
133                o.put(name, x.nextTo('\0'));
134                x.next();
135            }
136            return o;
137        }
138    
139        /**
140         * Convert a JSONObject into an HTTP header. A request header must contain
141         * 
142         * <pre>
143         * {
144         *     Method: &quot;POST&quot; (for example),
145         *     &quot;Request-URI&quot;: &quot;/&quot; (for example),
146         *     &quot;HTTP-Version&quot;: &quot;HTTP/1.1&quot; (for example)
147         *  }
148         * </pre>
149         * 
150         * A response header must contain
151         * 
152         * <pre>
153         * {
154         *     &quot;HTTP-Version&quot;: &quot;HTTP/1.1&quot; (for example),
155         *     &quot;Status-Code&quot;: &quot;200&quot; (for example),
156         *     &quot;Reason-Phrase&quot;: &quot;OK&quot; (for example)
157         *  }
158         * </pre>
159         * 
160         * Any other members of the JSONObject will be output as HTTP fields. The
161         * result will end with two CRLF pairs.
162         * 
163         * @param o
164         *            A JSONObject
165         * @return An HTTP header string.
166         * @throws NoSuchElementException
167         *             if the object does not contain enough information.
168         */
169        public static String toString(JSONObject o)
170        {
171            Iterator keys = o.keys();
172            String s;
173            StringBuffer sb = new StringBuffer();
174            if (o.has("Status-Code") && o.has("Reason-Phrase"))
175            {
176                sb.append(o.getString("HTTP-Version"));
177                sb.append(' ');
178                sb.append(o.getString("Status-Code"));
179                sb.append(' ');
180                sb.append(o.getString("Reason-Phrase"));
181            }
182            else if (o.has("Method") && o.has("Request-URI"))
183            {
184                sb.append(o.getString("Method"));
185                sb.append(' ');
186                sb.append('"');
187                sb.append(o.getString("Request-URI"));
188                sb.append('"');
189                sb.append(' ');
190                sb.append(o.getString("HTTP-Version"));
191            }
192            else
193            {
194                throw new NoSuchElementException(
195                        "Not enough material for an HTTP header.");
196            }
197            sb.append(CRLF);
198            while(keys.hasNext())
199            {
200                s = keys.next().toString();
201                if (!s.equals("HTTP-Version") && !s.equals("Status-Code")
202                        && !s.equals("Reason-Phrase") && !s.equals("Method")
203                        && !s.equals("Request-URI") && !o.isNull(s))
204                {
205                    sb.append(s);
206                    sb.append(": ");
207                    sb.append(o.getString(s));
208                    sb.append(CRLF);
209                }
210            }
211            sb.append(CRLF);
212            return sb.toString();
213        }
214    }