KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > portal > generic > HttpUtil


1 /*
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in
15  * the documentation and/or other materials provided with the
16  * distribution.
17  *
18  * 3. The end-user documentation included with the redistribution, if
19  * any, must include the following acknowlegement:
20  * "This product includes software developed by the
21  * Caucho Technology (http://www.caucho.com/)."
22  * Alternately, this acknowlegement may appear in the software itself,
23  * if and wherever such third-party acknowlegements normally appear.
24  *
25  * 4. The names "Hessian", "Resin", and "Caucho" must not be used to
26  * endorse or promote products derived from this software without prior
27  * written permission. For written permission, please contact
28  * info@caucho.com.
29  *
30  * 5. Products derived from this software may not be called "Resin"
31  * nor may "Resin" appear in their names without prior written
32  * permission of Caucho Technology.
33  *
34  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
35  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
36  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
37  * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS
38  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
39  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
40  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
41  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
42  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
43  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
44  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45  *
46  * @author Sam
47  */

48
49
50 package com.caucho.portal.generic;
51
52 import java.io.IOException JavaDoc;
53 import java.io.Writer JavaDoc;
54 import java.util.LinkedHashSet JavaDoc;
55 import java.util.Map JavaDoc;
56 import java.util.Set JavaDoc;
57 import java.util.regex.Matcher JavaDoc;
58 import java.util.regex.Pattern JavaDoc;
59
60 /**
61  * HTTP utilities.
62  *
63  * Encoding and decoding is for utf strings encoded with the rules defined for
64  * the "application/x-www-form-urlencoded" MIME format.
65  *
66  */

67 public class HttpUtil
68 {
69   /**
70    * benchmarks
71    * URLEncoder(717) encode(103) URLDecoder(165) decode(70)
72    * without _buffer: encode(153) decode(95) encodeJ(1034) decodeJ(229)
73    */

74   private static Object JavaDoc _bufferLock = new Integer JavaDoc(1);
75   private static StringBuffer JavaDoc _buffer = new StringBuffer JavaDoc(256);
76
77   static private StringBuffer JavaDoc getStringBuffer()
78   {
79     StringBuffer JavaDoc buf;
80
81     synchronized (_bufferLock) {
82       buf = _buffer;
83       _buffer = null;
84     }
85
86     if (buf == null)
87       buf = new StringBuffer JavaDoc(256);
88
89     return buf;
90   }
91
92   static private void releaseStringBuffer(StringBuffer JavaDoc buf)
93   {
94     if (buf.capacity() <= 1024) {
95
96       synchronized (_bufferLock) {
97         if (_buffer == null || _buffer.capacity() < buf.capacity()) {
98           buf.setLength(0);
99           _buffer = buf;
100         }
101       }
102     }
103   }
104
105   private static Pattern JavaDoc _headerPattern
106     = Pattern.compile("s/[,;\\s]*([^;,\\s]+)[^,]*//");
107
108   /**
109    * Return an ordered Set of header elements from an http header. If there
110    * are no header elements found, null is returned.
111    *
112    * <pre>
113    * text/html; q=1.0, text/*; q=0.8, image/gif; q=0.6, image/jpeg; q=0.8, image/*; q=0.5
114    * </pre>
115    *
116    * returns:
117    *
118    * <ul>
119    * <li>text/html
120    * <li>text/*
121    * <li>image/gif
122    * <li>image/jpeg
123    * <li>image/*
124    * </ul>
125    *
126    * Note that the qs value is ignored.
127    *
128    * @return null or a Set with at least one elemement
129    */

130   static public Set JavaDoc<String JavaDoc> getHeaderElements(String JavaDoc headerValue)
131   {
132     if (headerValue == null)
133       return null;
134
135     Matcher JavaDoc matcher = _headerPattern.matcher(headerValue);
136
137     Set JavaDoc<String JavaDoc> resultSet = null;
138
139     while (matcher.find()) {
140
141       if (resultSet == null)
142        return new LinkedHashSet JavaDoc<String JavaDoc>();
143
144       resultSet.add(matcher.group(1));
145     }
146
147     return resultSet;
148   }
149
150   /**
151    * Return only the first header element, null if headerValue is null or there
152    * are no header elements.
153    *
154    * A headerValue with a String like "text/html; charset=xxx" returns
155    * "text/html".
156    *
157    * A headerValue with a String like " en; q=1.0, fr; q=0.8 "
158    * returns "en".
159    */

160   static public String JavaDoc getFirstHeaderElement(String JavaDoc headerValue)
161   {
162     if (headerValue == null)
163       return null;
164
165     Matcher JavaDoc matcher = _headerPattern.matcher(headerValue);
166
167     if (matcher.find())
168       return matcher.group(1);
169     else
170       return null;
171   }
172
173   /**
174    * Extract and decode parameters out of the query string portion of the path
175    * and add them to the map. The parameters are found by looking for the '?'
176    * character.
177    *
178    * @param map the Map to put the parameters in
179    * @param url the url
180    *
181    * @returns the url without the query string
182    */

183   static public String JavaDoc extractParameters(Map JavaDoc<String JavaDoc, String JavaDoc[]> map, String JavaDoc url)
184   {
185     int beginIndex = url.indexOf('?');
186
187     if (beginIndex == -1)
188       return url;
189
190     return extractParameters(map, url, beginIndex + 1);
191   }
192
193   /**
194    * Extract and decode parameters out of the query string portion of the path
195    * and add them to the map.
196    *
197    * @param map the Map to put the parameters in
198    * @param url the url
199    * @param beginIndex the index of the character that follows the '?' character
200    *
201    * @returns the url without the query string
202    */

203   static public String JavaDoc extractParameters(Map JavaDoc<String JavaDoc, String JavaDoc[]> map,
204                                          String JavaDoc url,
205                                          int beginIndex)
206   {
207     if (beginIndex == -1)
208       return url;
209
210     String JavaDoc result = url.substring(0, beginIndex);
211
212     StringBuffer JavaDoc buf = getStringBuffer();
213
214     if (buf == null)
215       buf = new StringBuffer JavaDoc(256);
216
217     String JavaDoc name = null;
218     String JavaDoc value = null;
219
220     int len = url.length();
221
222     do {
223       int endIndex = url.indexOf('=', beginIndex);
224
225       if (endIndex == -1) {
226         endIndex = len;
227       }
228       else {
229         buf.setLength(0);
230         HttpUtil.decode(url, beginIndex, endIndex, buf);
231         name = buf.toString();
232       }
233
234       if (endIndex == len) {
235         value = "";
236       }
237       else {
238         beginIndex = endIndex + 1;
239         endIndex = url.indexOf('&', beginIndex);
240
241         if (endIndex == -1)
242           endIndex = len;
243
244         buf.setLength(0);
245         HttpUtil.decode(url, beginIndex, endIndex, buf);
246         value = buf.toString();
247       }
248
249       String JavaDoc[] values = map.get(name);
250
251       if (values == null) {
252         map.put(name, new String JavaDoc[] { value });
253       }
254       else {
255         int valuesLen = values.length;
256
257         String JavaDoc[] newValues = new String JavaDoc[valuesLen + 1];
258
259         for (int valuesIndex = 0; valuesIndex < valuesLen; valuesIndex++) {
260           newValues[valuesIndex] = values[valuesIndex];
261         }
262
263         newValues[valuesLen] = value;
264
265         map.put(name, newValues);
266       }
267
268       if (endIndex == len)
269         beginIndex = -1;
270       else
271         beginIndex = url.indexOf('&', endIndex) + 1;
272
273     } while (beginIndex > 0);
274
275     releaseStringBuffer(buf);
276
277     return result;
278   }
279
280   /**
281    * Encode a string.
282    *
283    * @param source the String to encode
284    *
285    * @return the encoded String
286    */

287   static public String JavaDoc encode(String JavaDoc source)
288   {
289     StringBuffer JavaDoc dest = getStringBuffer();
290
291     encodeUri(source, 0, source.length(), dest);
292
293     String JavaDoc result = dest.toString();
294
295     releaseStringBuffer(dest);
296
297     return result;
298   }
299
300   /**
301    * Encode a string.
302    *
303    * @param source the String to encode
304    * @param dest a StringBuffer that receives the encoded result
305    */

306   static public void encode(String JavaDoc source, StringBuffer JavaDoc dest)
307   {
308     encodeUri(source, 0, source.length(), dest);
309   }
310
311   /**
312    * Extract and encode a portion of a String.
313    *
314    * @param source the String to encode
315    * @param beginIndex the begin index, inclusive
316    * @param endIndex the end index, exclusive
317    * @param dest a StringBuffer that receives the encoded result
318    */

319   static public void encode(String JavaDoc source,
320                             int beginIndex,
321                             int endIndex,
322                             StringBuffer JavaDoc dest)
323   {
324     encodeUri(source, beginIndex, endIndex, dest);
325   }
326
327   /**
328    * Extract and encode a portion of a StringBuffer.
329    *
330    * @param source the StringBuffer to encode
331    * @param beginIndex the begin index, inclusive
332    * @param endIndex the end index, exclusive
333    * @param dest a StringBuffer that receives the encoded result
334    */

335   static public void encode(StringBuffer JavaDoc source,
336                             int beginIndex,
337                             int endIndex,
338                             StringBuffer JavaDoc dest)
339   {
340     encodeUri(source, beginIndex, endIndex, dest);
341   }
342
343   static public void encodeUri(CharSequence JavaDoc source,
344                                int beginIndex,
345                                int endIndex,
346                                StringBuffer JavaDoc dest)
347   {
348     for (int i = beginIndex; i < endIndex; i++) {
349       char ch = source.charAt(i);
350       if ( (ch >= 'a' && ch <= 'z')
351           || (ch >= 'A' && ch <= 'z')
352           || (ch >= '0' && ch <= '9')
353           || ch == '-'
354           || ch == '_'
355           || ch == '.'
356           || ch == '*')
357       {
358         dest.append(ch);
359       }
360       else if (ch == ' ') {
361         dest.append('+');
362       }
363       else if (ch <= 0xff) {
364         // 8 byte (utf-8)
365
dest.append('%');
366         dest.append(encodeHex(ch >> 4));
367         dest.append(encodeHex(ch));
368       }
369       else {
370         // 16 byte (utf-16)
371
dest.append('%');
372         dest.append('u');
373         dest.append(encodeHex(ch >> 12));
374         dest.append(encodeHex(ch >> 8));
375         dest.append(encodeHex(ch >> 4));
376         dest.append(encodeHex(ch));
377       }
378     }
379   }
380
381   /**
382    * Encode a string.
383    *
384    * @param source the String to encode
385    * @param dest a Writer that receives the encoded result
386    *
387    * @return the encoded String
388    */

389   static public void encode(String JavaDoc source, Writer JavaDoc dest)
390     throws IOException JavaDoc
391   {
392     encodeUri(source, 0, source.length(), dest);
393   }
394
395   /**
396    * Extract and encode a portion of a String.
397    *
398    * @param source the String to encode
399    * @param beginIndex the begin index, inclusive
400    * @param endIndex the end index, exclusive
401    * @param dest a Writer that receives the encoded result
402    */

403   static public void encode(String JavaDoc source,
404                             int beginIndex,
405                             int endIndex,
406                             Writer JavaDoc dest)
407     throws IOException JavaDoc
408   {
409     encodeUri(source, beginIndex, endIndex, dest);
410   }
411
412   /**
413    * Extract and encode a portion of a StringBuffer.
414    *
415    * @param source the StringBuffer to encode
416    * @param beginIndex the begin index, inclusive
417    * @param endIndex the end index, exclusive
418    * @param dest a Writer that receives the encoded result
419    */

420   static public void encode(StringBuffer JavaDoc source,
421                             int beginIndex,
422                             int endIndex,
423                             Writer JavaDoc dest)
424     throws IOException JavaDoc
425   {
426     encodeUri(source, beginIndex, endIndex, dest);
427   }
428
429   static public void encodeUri(CharSequence JavaDoc source,
430                                int beginIndex,
431                                int endIndex,
432                                Writer JavaDoc dest)
433     throws IOException JavaDoc
434   {
435     for (int i = beginIndex; i < endIndex; i++) {
436       char ch = source.charAt(i);
437       if ( (ch >= 'a' && ch <= 'z')
438           || (ch >= 'A' && ch <= 'z')
439           || (ch >= '0' && ch <= '9')
440           || ch == '-'
441           || ch == '_'
442           || ch == '.'
443           || ch == '*')
444       {
445         dest.write(ch);
446       }
447       else if (ch == ' ') {
448         dest.write('+');
449       }
450       else if (ch <= 0xff) {
451         // 8 byte (utf-8)
452
dest.write('%');
453         dest.write(encodeHex(ch >> 4));
454         dest.write(encodeHex(ch));
455       }
456       else {
457         // 16 byte (utf-16)
458
dest.write('%');
459         dest.write('u');
460         dest.write(encodeHex(ch >> 12));
461         dest.write(encodeHex(ch >> 8));
462         dest.write(encodeHex(ch >> 4));
463         dest.write(encodeHex(ch));
464       }
465     }
466   }
467
468   /**
469    * Decode a string.
470    *
471    * @param source the String to decode
472    *
473    * @return the decoded String
474    */

475   static public String JavaDoc decode(String JavaDoc source)
476   {
477     StringBuffer JavaDoc dest = getStringBuffer();
478
479     decodeUri(source, 0, source.length(), dest);
480
481     String JavaDoc result = dest.toString();
482
483     releaseStringBuffer(dest);
484
485     return result;
486   }
487
488   /**
489    * Decode a string.
490    *
491    * @param source the String to decode
492    * @param dest a StringBuffer that receives the decoded result
493    */

494   static public void decode(String JavaDoc source, StringBuffer JavaDoc dest)
495   {
496     decodeUri(source, 0, source.length(), dest);
497   }
498
499   /**
500    * Extract and decode an encoded portion of a string.
501    *
502    * @param source the String to extract from
503    * @param beginIndex the begin index, inclusive
504    * @param endIndex the end index, exclusive
505    * @param dest a StringBuffer that receives the decoded result
506    */

507   static public void decode(String JavaDoc source,
508                             int beginIndex,
509                             int endIndex,
510                             StringBuffer JavaDoc dest)
511   {
512     decodeUri(source, beginIndex, endIndex, dest);
513   }
514
515   /**
516    * Extract and decode an encoded portion of a StringBuffer.
517    *
518    * @param source the StringBuffer to extract from
519    * @param beginIndex the begin index, inclusive
520    * @param endIndex the end index, exclusive
521    * @param dest a StringBuffer that receives the decoded result
522    */

523   static public void decode(StringBuffer JavaDoc source,
524                             int beginIndex,
525                             int endIndex,
526                             StringBuffer JavaDoc dest)
527   {
528     decodeUri(source, beginIndex, endIndex, dest);
529   }
530
531   static private void decodeUri(CharSequence JavaDoc source,
532                                 int beginIndex,
533                                 int endIndex,
534                                 StringBuffer JavaDoc dest)
535   {
536     int i = beginIndex;
537
538     while (i < endIndex) {
539       char ch = source.charAt(i);
540       if (ch == '%')
541         i = scanUriEscape(source, i + 1, endIndex, dest);
542       else if (ch == '+') {
543         dest.append(' ');
544         i++;
545       } else {
546         dest.append(ch);
547         i++;
548       }
549     }
550   }
551
552   private static int scanUriEscape(CharSequence JavaDoc source, int i, int len,
553                                    StringBuffer JavaDoc dest )
554   {
555     int ch1 = i < len ? ( ((int)source.charAt(i++)) & 0xff) : -1;
556
557     if (ch1 == 'u') {
558       ch1 = i < len ? ( ((int)source.charAt(i++)) & 0xff) : -1;
559       int ch2 = i < len ? ( ((int)source.charAt(i++)) & 0xff) : -1;
560       int ch3 = i < len ? ( ((int)source.charAt(i++)) & 0xff) : -1;
561       int ch4 = i < len ? ( ((int)source.charAt(i++)) & 0xff) : -1;
562
563       dest.append((char) ((decodeHex(ch1) << 12) +
564                           (decodeHex(ch2) << 8) +
565                           (decodeHex(ch3) << 4) +
566                           (decodeHex(ch4))));
567     }
568     else {
569       int ch2 = i < len ? ( ((int)source.charAt(i++)) & 0xff) : -1;
570
571       int b = (decodeHex(ch1) << 4) + decodeHex(ch2);;
572
573       dest.append( (char) b);
574     }
575
576     return i;
577   }
578
579   static char encodeHex(int ch)
580   {
581     ch &= 0xf;
582     if (ch < 10)
583       return (char) (ch + '0');
584     else
585       return (char) (ch + 'a' - 10);
586   }
587   
588   private static int decodeHex(int ch)
589   {
590     if (ch >= '0' && ch <= '9')
591       return ch - '0';
592     else if (ch >= 'a' && ch <= 'f')
593       return ch - 'a' + 10;
594     else if (ch >= 'A' && ch <= 'F')
595       return ch - 'A' + 10;
596     else
597       return -1;
598   }
599 }
600
601
Popular Tags