Util.java
Upload User: demmber
Upload Date: 2007-12-22
Package Size: 717k
Code Size: 31k
Category:

Java Develop

Development Platform:

Java

  1. /*
  2.  * @(#)Util.java 0.3-3 06/05/2001
  3.  *
  4.  *  This file is part of the HTTPClient package
  5.  *  Copyright (C) 1996-2001 Ronald Tschal鋜
  6.  *
  7.  *  This library is free software; you can redistribute it and/or
  8.  *  modify it under the terms of the GNU Lesser General Public
  9.  *  License as published by the Free Software Foundation; either
  10.  *  version 2 of the License, or (at your option) any later version.
  11.  *
  12.  *  This library is distributed in the hope that it will be useful,
  13.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  *  Lesser General Public License for more details.
  16.  *
  17.  *  You should have received a copy of the GNU Lesser General Public
  18.  *  License along with this library; if not, write to the Free
  19.  *  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  20.  *  MA 02111-1307, USA
  21.  *
  22.  *  For questions, suggestions, bug-reports, enhancement-requests etc.
  23.  *  I may be contacted at:
  24.  *
  25.  *  ronald@innovation.ch
  26.  *
  27.  *  The HTTPClient's home page is located at:
  28.  *
  29.  *  http://www.innovation.ch/java/HTTPClient/ 
  30.  *
  31.  */
  32. package HTTPClient;
  33. import java.lang.reflect.Array;
  34. import java.net.URL;
  35. import java.util.Date;
  36. import java.util.BitSet;
  37. import java.util.Locale;
  38. import java.util.Vector;
  39. import java.util.Hashtable;
  40. import java.util.SimpleTimeZone;
  41. import java.util.StringTokenizer;
  42. import java.text.DateFormat;
  43. import java.text.SimpleDateFormat;
  44. /**
  45.  * This class holds various utility methods.
  46.  *
  47.  * @version 0.3-3  06/05/2001
  48.  * @author Ronald Tschal鋜
  49.  */
  50. public class Util
  51. {
  52.     private static final BitSet Separators = new BitSet(128);
  53.     private static final BitSet TokenChar = new BitSet(128);
  54.     private static final BitSet UnsafeChar = new BitSet(128);
  55.     private static DateFormat http_format;
  56.     private static DateFormat parse_1123;
  57.     private static DateFormat parse_850;
  58.     private static DateFormat parse_asctime;
  59.     private static final Object http_format_lock = new Object();
  60.     private static final Object http_parse_lock  = new Object();
  61.     static
  62.     {
  63. // rfc-2616 tspecial
  64. Separators.set('(');
  65. Separators.set(')');
  66. Separators.set('<');
  67. Separators.set('>');
  68. Separators.set('@');
  69. Separators.set(',');
  70. Separators.set(';');
  71. Separators.set(':');
  72. Separators.set('\');
  73. Separators.set('"');
  74. Separators.set('/');
  75. Separators.set('[');
  76. Separators.set(']');
  77. Separators.set('?');
  78. Separators.set('=');
  79. Separators.set('{');
  80. Separators.set('}');
  81. Separators.set(' ');
  82. Separators.set('t');
  83. // rfc-2616 token
  84. for (int ch=32; ch<127; ch++)  TokenChar.set(ch);
  85. TokenChar.xor(Separators);
  86. // rfc-1738 unsafe characters, including CTL and SP, and excluding
  87. // "#" and "%"
  88. for (int ch=0; ch<32; ch++)  UnsafeChar.set(ch);
  89. UnsafeChar.set(' ');
  90. UnsafeChar.set('<');
  91. UnsafeChar.set('>');
  92. UnsafeChar.set('"');
  93. UnsafeChar.set('{');
  94. UnsafeChar.set('}');
  95. UnsafeChar.set('|');
  96. UnsafeChar.set('\');
  97. UnsafeChar.set('^');
  98. UnsafeChar.set('~');
  99. UnsafeChar.set('[');
  100. UnsafeChar.set(']');
  101. UnsafeChar.set('`');
  102. UnsafeChar.set(127);
  103. // rfc-1123 date format (restricted to GMT, as per rfc-2616)
  104. /* This initialization has been moved to httpDate() because it
  105.  * takes an awfully long time and is often not needed
  106.  *
  107. http_format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'",
  108.    Locale.US);
  109. http_format.setTimeZone(new SimpleTimeZone(0, "GMT"));
  110. */
  111.     }
  112.     // Constructors
  113.     /**
  114.      * This class isn't meant to be instantiated.
  115.      */
  116.     private Util() {}
  117.     // Methods
  118.     final static Object[] resizeArray(Object[] src, int new_size)
  119.     {
  120. Class compClass = src.getClass().getComponentType();
  121. Object tmp[] = (Object[]) Array.newInstance(compClass, new_size);
  122. System.arraycopy(src, 0, tmp, 0,
  123. (src.length < new_size ? src.length : new_size));
  124. return tmp;
  125.     }
  126.     final static NVPair[] resizeArray(NVPair[] src, int new_size)
  127.     {
  128. NVPair tmp[] = new NVPair[new_size];
  129. System.arraycopy(src, 0, tmp, 0,
  130. (src.length < new_size ? src.length : new_size));
  131. return tmp;
  132.     }
  133.     final static AuthorizationInfo[] resizeArray(AuthorizationInfo[] src,
  134.  int new_size)
  135.     {
  136. AuthorizationInfo tmp[] = new AuthorizationInfo[new_size];
  137. System.arraycopy(src, 0, tmp, 0,
  138. (src.length < new_size ? src.length : new_size));
  139. return tmp;
  140.     }
  141.     final static Cookie[] resizeArray(Cookie[] src, int new_size)
  142.     {
  143. Cookie tmp[] = new Cookie[new_size];
  144. System.arraycopy(src, 0, tmp, 0,
  145. (src.length < new_size ? src.length : new_size));
  146. return tmp;
  147.     }
  148.     final static String[] resizeArray(String[] src, int new_size)
  149.     {
  150. String tmp[] = new String[new_size];
  151. System.arraycopy(src, 0, tmp, 0,
  152. (src.length < new_size ? src.length : new_size));
  153. return tmp;
  154.     }
  155.     final static boolean[] resizeArray(boolean[] src, int new_size)
  156.     {
  157. boolean tmp[] = new boolean[new_size];
  158. System.arraycopy(src, 0, tmp, 0,
  159. (src.length < new_size ? src.length : new_size));
  160. return tmp;
  161.     }
  162.     final static byte[] resizeArray(byte[] src, int new_size)
  163.     {
  164. byte tmp[] = new byte[new_size];
  165. System.arraycopy(src, 0, tmp, 0,
  166. (src.length < new_size ? src.length : new_size));
  167. return tmp;
  168.     }
  169.     final static char[] resizeArray(char[] src, int new_size)
  170.     {
  171. char tmp[] = new char[new_size];
  172. System.arraycopy(src, 0, tmp, 0,
  173. (src.length < new_size ? src.length : new_size));
  174. return tmp;
  175.     }
  176.     final static int[] resizeArray(int[] src, int new_size)
  177.     {
  178. int tmp[] = new int[new_size];
  179. System.arraycopy(src, 0, tmp, 0,
  180. (src.length < new_size ? src.length : new_size));
  181. return tmp;
  182.     }
  183.     /**
  184.      * Split a property into an array of Strings, using "|" as the
  185.      * separator.
  186.      */
  187.     static String[] splitProperty(String prop)
  188.     {
  189. if (prop == null)  return new String[0];
  190. StringTokenizer tok = new StringTokenizer(prop, "|");
  191. String[] list = new String[tok.countTokens()];
  192. for (int idx=0; idx<list.length; idx++)
  193.     list[idx] = tok.nextToken().trim();
  194. return list;
  195.     }
  196.     /**
  197.      * Helper method for context lists used by modules. Returns the
  198.      * list associated with the context if it exists; otherwise it creates
  199.      * a new list and adds it to the context list.
  200.      *
  201.      * @param cntxt_list the list of lists indexed by context
  202.      * @param cntxt the context
  203.      */
  204.     final static Hashtable getList(Hashtable cntxt_list, Object cntxt)
  205.     {
  206. synchronized (cntxt_list)
  207. {
  208.     Hashtable list = (Hashtable) cntxt_list.get(cntxt);
  209.     if (list == null)
  210.     {
  211. list = new Hashtable();
  212. cntxt_list.put(cntxt, list);
  213.     }
  214.     return list;
  215. }
  216.     }
  217.     /**
  218.      * Creates an array of distances to speed up the search in findStr().
  219.      * The returned array should be passed as the second argument to
  220.      * findStr().
  221.      *
  222.      * @param search the search string (same as the first argument to
  223.      *               findStr()).
  224.      * @return an array of distances (to be passed as the second argument to
  225.      *         findStr()).
  226.      */
  227.     final static int[] compile_search(byte[] search)
  228.     {
  229. int[] cmp = {0, 1, 0, 1, 0, 1};
  230. int   end;
  231. for (int idx=0; idx<search.length; idx++)
  232. {
  233.     for (end=idx+1; end<search.length; end++)
  234.     {
  235. if (search[idx] == search[end])  break;
  236.     }
  237.     if (end < search.length)
  238.     {
  239. if ((end-idx) > cmp[1])
  240. {
  241.     cmp[4] = cmp[2];
  242.     cmp[5] = cmp[3];
  243.     cmp[2] = cmp[0];
  244.     cmp[3] = cmp[1];
  245.     cmp[0] = idx;
  246.     cmp[1] = end - idx;
  247. }
  248. else if ((end-idx) > cmp[3])
  249. {
  250.     cmp[4] = cmp[2];
  251.     cmp[5] = cmp[3];
  252.     cmp[2] = idx;
  253.     cmp[3] = end - idx;
  254. }
  255. else if ((end-idx) > cmp[3])
  256. {
  257.     cmp[4] = idx;
  258.     cmp[5] = end - idx;
  259. }
  260.     }
  261. }
  262. cmp[1] += cmp[0];
  263. cmp[3] += cmp[2];
  264. cmp[5] += cmp[4];
  265. return cmp;
  266.     }
  267.     /**
  268.      * Search for a string. Use compile_search() to first generate the second
  269.      * argument. This uses a Knuth-Morris-Pratt like algorithm.
  270.      *
  271.      * @param search  the string to search for.
  272.      * @param cmp     the the array returned by compile_search.
  273.      * @param str     the string in which to look for <var>search</var>.
  274.      * @param beg     the position at which to start the search in
  275.      *                <var>str</var>.
  276.      * @param end     the position at which to end the search in <var>str</var>,
  277.      *                noninclusive.
  278.      * @return the position in <var>str</var> where <var>search</var> was
  279.      *         found, or -1 if not found.
  280.      */
  281.     final static int findStr(byte[] search, int[] cmp, byte[] str,
  282.      int beg, int end)
  283.     {
  284. int c1f  = cmp[0],
  285.     c1l  = cmp[1],
  286.     d1   = c1l - c1f,
  287.     c2f  = cmp[2],
  288.     c2l  = cmp[3],
  289.     d2   = c2l - c2f,
  290.     c3f  = cmp[4],
  291.     c3l  = cmp[5],
  292.     d3   = c3l - c3f;
  293. Find: while (beg+search.length <= end)
  294. {
  295.     if (search[c1l] == str[beg+c1l])
  296.     {
  297. /* This is correct, but Visual J++ can't cope with it...
  298. Comp: if (search[c1f] == str[beg+c1f])
  299. {
  300.     for (int idx=0; idx<search.length; idx++)
  301. if (search[idx] != str[beg+idx])  break Comp;
  302.     break Find; // we found it
  303. }
  304. *  so here is the replacement: */
  305. if (search[c1f] == str[beg+c1f])
  306. {
  307.     boolean same = true;
  308.     for (int idx=0; idx<search.length; idx++)
  309. if (search[idx] != str[beg+idx])
  310. {
  311. same = false;
  312. break;
  313. }
  314.     if (same)
  315. break Find;         // we found it
  316. }
  317. beg += d1;
  318.     }
  319.     else if (search[c2l] == str[beg+c2l])
  320. beg += d2;
  321.     else if (search[c3l] == str[beg+c3l])
  322. beg += d3;
  323.     else
  324. beg++;
  325. }
  326. if (beg+search.length > end)
  327.     return -1;
  328. else
  329.     return beg;
  330.     }
  331.     /**
  332.      * Replace quoted characters by their unquoted version. Quoted characters
  333.      * are characters preceded by a slash. E.g. "c" would be replaced by "c".
  334.      * This is used in parsing http headers where quoted-characters are
  335.      * allowed in quoted-strings and often used to quote the quote character
  336.      * &lt;"&gt;.
  337.      *
  338.      * @param str the string do dequote
  339.      * @return the string do with all quoted characters replaced by their
  340.      *         true value.
  341.      */
  342.     public final static String dequoteString(String str)
  343.     {
  344. if (str.indexOf('\') == -1)  return str;
  345. char[] buf = str.toCharArray();
  346. int pos = 0, num_deq = 0;
  347. while (pos < buf.length)
  348. {
  349.     if (buf[pos] == '\'  &&  pos+1 < buf.length)
  350.     {
  351. System.arraycopy(buf, pos+1, buf, pos, buf.length-pos-1);
  352. num_deq++;
  353.     }
  354.     pos++;
  355. }
  356. return new String(buf, 0, buf.length-num_deq);
  357.     }
  358.     /**
  359.      * Replace given characters by their quoted version. Quoted characters
  360.      * are characters preceded by a slash. E.g. "c" would be replaced by "c".
  361.      * This is used in generating http headers where certain characters need
  362.      * to be quoted, such as the quote character &lt;"&gt;.
  363.      *
  364.      * @param str   the string do quote
  365.      * @param qlist the list of characters to quote
  366.      * @return the string do with all characters replaced by their
  367.      *         quoted version.
  368.      */
  369.     public final static String quoteString(String str, String qlist)
  370.     {
  371. char[] list = qlist.toCharArray();
  372. int idx;
  373. for (idx=0; idx<list.length; idx++)
  374.     if (str.indexOf(list[idx]) != -1)  break;
  375. if (idx == list.length)  return str;
  376. int len = str.length();
  377. char[] buf = new char[len*2];
  378. str.getChars(0, len, buf, 0);
  379. int pos = 0;
  380. while (pos < len)
  381. {
  382.     if (qlist.indexOf(buf[pos], 0) != -1)
  383.     {
  384. if (len == buf.length)
  385.     buf = Util.resizeArray(buf, len+str.length());
  386. System.arraycopy(buf, pos, buf, pos+1, len-pos);
  387. len++;
  388. buf[pos++] = '\';
  389.     }
  390.     pos++;
  391. }
  392. return new String(buf, 0, len);
  393.     }
  394.     /**
  395.      * This parses the value part of a header. All quoted strings are
  396.      * dequoted.
  397.      *
  398.      * @see #parseHeader(java.lang.String, boolean)
  399.      * @param header  the value part of the header.
  400.      * @return a Vector containing all the elements; each entry is an
  401.      *         instance of <var>HttpHeaderElement</var>.
  402.      * @exception ParseException if the syntax rules are violated.
  403.      */
  404.     public final static Vector parseHeader(String header)  throws ParseException
  405.     {
  406. return parseHeader(header, true);
  407.     }
  408.     /**
  409.      * This parses the value part of a header. The result is a Vector of
  410.      * HttpHeaderElement's. The syntax the header must conform to is:
  411.      *
  412.      * <PRE>
  413.      * header  = [ element ] *( "," [ element ] )
  414.      * element = name [ "=" [ value ] ] *( ";" [ param ] )
  415.      * param   = name [ "=" [ value ] ]
  416.      * 
  417.      * name    = token
  418.      * value   = ( token | quoted-string )
  419.      * 
  420.      * token         = 1*&lt;any char except "=", ",", ";", &lt;"&gt; and
  421.      *                       white space&gt;
  422.      * quoted-string = &lt;"&gt; *( text | quoted-char ) &lt;"&gt;
  423.      * text          = any char except &lt;"&gt;
  424.      * quoted-char   = "" char
  425.      * </PRE>
  426.      *
  427.      * Any amount of white space is allowed between any part of the header,
  428.      * element or param and is ignored. A missing value in any element or
  429.      * param will be stored as the empty string; if the "=" is also missing
  430.      * <var>null</var> will be stored instead.
  431.      *
  432.      * @param header  the value part of the header.
  433.      * @param dequote if true all quoted strings are dequoted.
  434.      * @return a Vector containing all the elements; each entry is an
  435.      *         instance of <var>HttpHeaderElement</var>.
  436.      * @exception ParseException if the above syntax rules are violated.
  437.      * @see HTTPClient.HttpHeaderElement
  438.      */
  439.     public final static Vector parseHeader(String header, boolean dequote)
  440.     throws ParseException
  441.     {
  442. if (header == null)  return null;
  443. char[]  buf    = header.toCharArray();
  444. Vector  elems  = new Vector();
  445. boolean first  = true;
  446. int     beg = -1, end = 0, len = buf.length, abeg[] = new int[1];
  447. String  elem_name, elem_value;
  448. elements: while (true)
  449. {
  450.     if (!first) // find required ","
  451.     {
  452. beg = skipSpace(buf, end);
  453. if (beg == len)  break;
  454. if (buf[beg] != ',')
  455.     throw new ParseException("Bad header format: '" + header +
  456.      "'nExpected "," at position " +
  457.      beg);
  458.     }
  459.     first = false;
  460.     beg = skipSpace(buf, beg+1);
  461.     if (beg == len)  break elements;
  462.     if (buf[beg] == ',') // skip empty elements
  463.     {
  464. end = beg;
  465. continue elements;
  466.     }
  467.     if (buf[beg] == '='  ||  buf[beg] == ';'  ||  buf[beg] == '"')
  468. throw new ParseException("Bad header format: '" + header +
  469.  "'nEmpty element name at position " +
  470.  beg);
  471.     end = beg+1; // extract element name
  472.     while (end < len  &&  !Character.isWhitespace(buf[end])  &&
  473.    buf[end] != '='  &&  buf[end] != ','  &&  buf[end] != ';')
  474. end++;
  475.     elem_name = new String(buf, beg, end-beg);
  476.     beg = skipSpace(buf, end);
  477.     if (beg < len  &&  buf[beg] == '=') // element value
  478.     {
  479. abeg[0] = beg+1;
  480. elem_value = parseValue(buf, abeg, header, dequote);
  481. end = abeg[0];
  482.     }
  483.     else
  484.     {
  485. elem_value = null;
  486. end = beg;
  487.     }
  488.     NVPair[] params = new NVPair[0];
  489.     params: while (true)
  490.     {
  491. String param_name, param_value;
  492. beg = skipSpace(buf, end); // expect ";"
  493. if (beg == len  ||  buf[beg] != ';')
  494.     break params;
  495. beg = skipSpace(buf, beg+1);
  496. if (beg == len  ||  buf[beg] == ',')
  497. {
  498.     end = beg;
  499.     break params;
  500. }
  501. if (buf[beg] == ';') // skip empty parameters
  502. {
  503.     end = beg;
  504.     continue params;
  505. }
  506. if (buf[beg] == '='  ||  buf[beg] == '"')
  507.     throw new ParseException("Bad header format: '" + header +
  508.  "'nEmpty parameter name at position "+
  509.  beg);
  510. end = beg+1; // extract param name
  511. while (end < len  &&  !Character.isWhitespace(buf[end])  &&
  512.        buf[end] != '='  &&  buf[end] != ','  && buf[end] != ';')
  513.     end++;
  514. param_name = new String(buf, beg, end-beg);
  515. beg = skipSpace(buf, end);
  516. if (beg < len  &&  buf[beg] == '=') // element value
  517. {
  518.     abeg[0] = beg+1;
  519.     param_value = parseValue(buf, abeg, header, dequote);
  520.     end = abeg[0];
  521. }
  522. else
  523. {
  524.     param_value = null;
  525.     end = beg;
  526. }
  527. params = Util.resizeArray(params, params.length+1);
  528. params[params.length-1] = new NVPair(param_name, param_value);
  529.     }
  530.     elems.addElement(
  531.       new HttpHeaderElement(elem_name, elem_value, params));
  532. }
  533. return elems;
  534.     }
  535.     /**
  536.      * Parse the value part. Accepts either token or quoted string.
  537.      */
  538.     private static String parseValue(char[] buf, int[] abeg, String header,
  539.      boolean dequote)
  540. throws ParseException
  541.     {
  542. int beg = abeg[0], end = beg, len = buf.length;
  543. String value;
  544. beg = skipSpace(buf, beg);
  545. if (beg < len  &&  buf[beg] == '"') // it's a quoted-string
  546. {
  547.     beg++;
  548.     end = beg;
  549.     char[] deq_buf = null;
  550.     int    deq_pos = 0, lst_pos = beg;
  551.     while (end < len  &&  buf[end] != '"')
  552.     {
  553. if (buf[end] == '\')
  554. {
  555.     if (dequote) // dequote char
  556.     {
  557. if (deq_buf == null)
  558.     deq_buf = new char[buf.length];
  559. System.arraycopy(buf, lst_pos, deq_buf, deq_pos,
  560.  end-lst_pos);
  561. deq_pos += end-lst_pos;
  562. lst_pos = ++end;
  563.     }
  564.     else
  565. end++; // skip quoted char
  566. }
  567. end++;
  568.     }
  569.     if (end == len)
  570. throw new ParseException("Bad header format: '" + header +
  571.  "'nClosing <"> for quoted-string"+
  572.  " starting at position " +
  573.  (beg-1) + " not found");
  574.     if (deq_buf != null)
  575.     {
  576. System.arraycopy(buf, lst_pos, deq_buf, deq_pos, end-lst_pos);
  577. deq_pos += end-lst_pos;
  578. value = new String(deq_buf, 0, deq_pos);
  579.     }
  580.     else
  581. value = new String(buf, beg, end-beg);
  582.     end++;
  583. }
  584. else // it's a simple token value
  585. {
  586.     end = beg;
  587.     while (end < len  &&  !Character.isWhitespace(buf[end])  &&
  588.    buf[end] != ','  &&  buf[end] != ';')
  589. end++;
  590.     value = new String(buf, beg, end-beg);
  591. }
  592. abeg[0] = end;
  593. return value;
  594.     }
  595.     /**
  596.      * Determines if the given header contains a certain token. The header
  597.      * must conform to the rules outlined in parseHeader().
  598.      *
  599.      * @see #parseHeader(java.lang.String)
  600.      * @param header the header value.
  601.      * @param token  the token to find; the match is case-insensitive.
  602.      * @return true if the token is present, false otherwise.
  603.      * @exception ParseException if this is thrown parseHeader().
  604.      */
  605.     public final static boolean hasToken(String header, String token)
  606.     throws ParseException
  607.     {
  608. if (header == null)
  609.     return false;
  610. else
  611.     return parseHeader(header).contains(new HttpHeaderElement(token));
  612.     }
  613.     /**
  614.      * Get the HttpHeaderElement with the name <var>name</var>.
  615.      *
  616.      * @param header a vector of HttpHeaderElement's, such as is returned
  617.      *               from <code>parseHeader()</code>
  618.      * @param name   the name of element to retrieve; matching is
  619.      *               case-insensitive
  620.      * @return the request element, or null if none found.
  621.      * @see #parseHeader(java.lang.String)
  622.      */
  623.     public final static HttpHeaderElement getElement(Vector header, String name)
  624.     {
  625. int idx = header.indexOf(new HttpHeaderElement(name));
  626. if (idx == -1)
  627.     return null;
  628. else
  629.     return (HttpHeaderElement) header.elementAt(idx);
  630.     }
  631.     /**
  632.      * retrieves the value associated with the parameter <var>param</var> in
  633.      * a given header string. It parses the header using
  634.      * <code>parseHeader()</code> and then searches the first element for the
  635.      * given parameter. This is used especially in headers like
  636.      * 'Content-type' and 'Content-Disposition'.
  637.      *
  638.      * <P>quoted characters ("x") in a quoted string are dequoted.
  639.      *
  640.      * @see #parseHeader(java.lang.String)
  641.      * @param  param  the parameter name
  642.      * @param  hdr    the header value
  643.      * @return the value for this parameter, or null if not found.
  644.      * @exception ParseException if the above syntax rules are violated.
  645.      */
  646.     public final static String getParameter(String param, String hdr)
  647.     throws ParseException
  648.     {
  649. NVPair[] params = ((HttpHeaderElement) parseHeader(hdr).firstElement()).
  650.     getParams();
  651. for (int idx=0; idx<params.length; idx++)
  652. {
  653.     if (params[idx].getName().equalsIgnoreCase(param))
  654. return params[idx].getValue();
  655. }
  656. return null;
  657.     }
  658.     /**
  659.      * Assembles a Vector of HttpHeaderElements into a full header string.
  660.      * The individual header elements are seperated by a ", ".
  661.      *
  662.      * @param the parsed header
  663.      * @return a string containing the assembled header
  664.      */
  665.     public final static String assembleHeader(Vector pheader)
  666.     {
  667. StringBuffer hdr = new StringBuffer(200);
  668. int len = pheader.size();
  669. for (int idx=0; idx<len; idx++)
  670. {
  671.     ((HttpHeaderElement) pheader.elementAt(idx)).appendTo(hdr);
  672.     hdr.append(", ");
  673. }
  674. hdr.setLength(hdr.length()-2);
  675. return hdr.toString();
  676.     }
  677.     /**
  678.      * returns the position of the first non-space character in a char array
  679.      * starting a position pos.
  680.      *
  681.      * @param str the char array
  682.      * @param pos the position to start looking
  683.      * @return the position of the first non-space character
  684.      */
  685.     final static int skipSpace(char[] str, int pos)
  686.     {
  687. int len = str.length;
  688. while (pos < len  &&  Character.isWhitespace(str[pos]))  pos++;
  689. return pos;
  690.     }
  691.     /**
  692.      * returns the position of the first space character in a char array
  693.      * starting a position pos.
  694.      *
  695.      * @param str the char array
  696.      * @param pos the position to start looking
  697.      * @return the position of the first space character, or the length of
  698.      *         the string if not found
  699.      */
  700.     final static int findSpace(char[] str, int pos)
  701.     {
  702. int len = str.length;
  703. while (pos < len  &&  !Character.isWhitespace(str[pos]))  pos++;
  704. return pos;
  705.     }
  706.     /**
  707.      * returns the position of the first non-token character in a char array
  708.      * starting a position pos.
  709.      *
  710.      * @param str the char array
  711.      * @param pos the position to start looking
  712.      * @return the position of the first non-token character, or the length
  713.      *         of the string if not found
  714.      */
  715.     final static int skipToken(char[] str, int pos)
  716.     {
  717. int len = str.length;
  718. while (pos < len  &&  TokenChar.get(str[pos]))  pos++;
  719. return pos;
  720.     }
  721.     /**
  722.      * Does the string need to be quoted when sent in a header? I.e. does
  723.      * it contain non-token characters?
  724.      *
  725.      * @param str the string
  726.      * @return true if it needs quoting (i.e. it contains non-token chars)
  727.      */
  728.     final static boolean needsQuoting(String str)
  729.     {
  730. int len = str.length(), pos = 0;
  731. while (pos < len  &&  TokenChar.get(str.charAt(pos)))  pos++;
  732. return (pos < len);
  733.     }
  734.     /**
  735.      * Compares two http urls for equality. This exists because the method
  736.      * <code>java.net.URL.sameFile()</code> is broken (an explicit port 80
  737.      * doesn't compare equal to an implicit port, and it doesn't take
  738.      * escapes into account).
  739.      *
  740.      * <P>Two http urls are considered equal if they have the same protocol
  741.      * (case-insensitive match), the same host (case-insensitive), the
  742.      * same port and the same file (after decoding escaped characters).
  743.      *
  744.      * @param url1 the first url
  745.      * @param url1 the second url
  746.      * @return true if <var>url1</var> and <var>url2</var> compare equal
  747.      */
  748.     public final static boolean sameHttpURL(URL url1, URL url2)
  749.     {
  750. if (!url1.getProtocol().equalsIgnoreCase(url2.getProtocol()))
  751.     return false;
  752. if (!url1.getHost().equalsIgnoreCase(url2.getHost()))
  753.     return false;
  754. int port1 = url1.getPort(), port2 = url2.getPort();
  755. if (port1 == -1)  port1 = URI.defaultPort(url1.getProtocol());
  756. if (port2 == -1)  port2 = URI.defaultPort(url1.getProtocol());
  757. if (port1 != port2)
  758.     return false;
  759. try
  760.     { return URI.unescape(url1.getFile(), null).equals(URI.unescape(url2.getFile(), null)); }
  761. catch (ParseException pe)
  762.     { return url1.getFile().equals(url2.getFile());}
  763.     }
  764.     /**
  765.      * Return the default port used by a given protocol.
  766.      *
  767.      * @param protocol the protocol
  768.      * @return the port number, or 0 if unknown
  769.      * @deprecated use URI.defaultPort() instead
  770.      * @see HTTPClient.URI#defaultPort(java.lang.String)
  771.      */
  772.     public final static int defaultPort(String protocol)
  773.     {
  774. return URI.defaultPort(protocol);
  775.     }
  776.     /**
  777.      * Parse the http date string. java.util.Date will do this fine, but
  778.      * is deprecated, so we use SimpleDateFormat instead.
  779.      *
  780.      * @param dstr  the date string to parse
  781.      * @return the Date object
  782.      */
  783.     final static Date parseHttpDate(String dstr)
  784.     {
  785. synchronized (http_parse_lock)
  786. {
  787.     if (parse_1123 == null)
  788. setupParsers();
  789. }
  790. try
  791.     { return parse_1123.parse(dstr); }
  792. catch (java.text.ParseException pe)
  793.     { }
  794. try
  795.     { return parse_850.parse(dstr); }
  796. catch (java.text.ParseException pe)
  797.     { }
  798. try
  799.     { return parse_asctime.parse(dstr); }
  800. catch (java.text.ParseException pe)
  801.     { throw new IllegalArgumentException(pe.toString()); }
  802.     }
  803.     private static final void setupParsers()
  804.     {
  805. parse_1123 =
  806.     new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
  807. parse_850 =
  808.     new SimpleDateFormat("EEEE, dd-MMM-yy HH:mm:ss 'GMT'", Locale.US);
  809. parse_asctime =
  810.     new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", Locale.US);
  811. parse_1123.setTimeZone(new SimpleTimeZone(0, "GMT"));
  812. parse_850.setTimeZone(new SimpleTimeZone(0, "GMT"));
  813. parse_asctime.setTimeZone(new SimpleTimeZone(0, "GMT"));
  814. parse_1123.setLenient(true);
  815. parse_850.setLenient(true);
  816. parse_asctime.setLenient(true);
  817.     }
  818.     /**
  819.      * This returns a string containing the date and time in <var>date</var>
  820.      * formatted according to a subset of RFC-1123. The format is defined in
  821.      * the HTTP/1.0 spec (RFC-1945), section 3.3, and the HTTP/1.1 spec
  822.      * (RFC-2616), section 3.3.1. Note that Date.toGMTString() is close, but
  823.      * is missing the weekday and supresses the leading zero if the day is
  824.      * less than the 10th. Instead we use the SimpleDateFormat class.
  825.      *
  826.      * <P>Some versions of JDK 1.1.x are bugged in that their GMT uses
  827.      * daylight savings time... Therefore we use our own timezone
  828.      * definitions.
  829.      *
  830.      * @param date the date and time to be converted
  831.      * @return a string containg the date and time as used in http
  832.      */
  833.     public static final String httpDate(Date date)
  834.     {
  835. synchronized (http_format_lock)
  836. {
  837.     if (http_format == null)
  838. setupFormatter();
  839. }
  840. return http_format.format(date);
  841.     }
  842.     private static final void setupFormatter()
  843.     {
  844. http_format =
  845.     new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
  846. http_format.setTimeZone(new SimpleTimeZone(0, "GMT"));
  847.     }
  848.     /**
  849.      * Escape unsafe characters in a path.
  850.      *
  851.      * @param path the original path
  852.      * @return the path with all unsafe characters escaped
  853.      */
  854.     final static String escapeUnsafeChars(String path)
  855.     {
  856. int len = path.length();
  857. char[] buf = new char[3*len];
  858. int dst = 0;
  859. for (int src=0; src<len; src++)
  860. {
  861.     char ch = path.charAt(src);
  862.     if (ch >= 128  ||  UnsafeChar.get(ch))
  863.     {
  864. buf[dst++] = '%';
  865. buf[dst++] = hex_map[(ch & 0xf0) >>> 4];
  866. buf[dst++] = hex_map[ch & 0x0f];
  867.     }
  868.     else
  869. buf[dst++] = ch;
  870. }
  871. if (dst > len)
  872.     return new String(buf, 0, dst);
  873. else
  874.     return path;
  875.     }
  876.     static final char[] hex_map =
  877.     {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
  878.     /**
  879.      * Extract the path from an http resource.
  880.      *
  881.      * <P>The "resource" part of an HTTP URI can contain a number of parts,
  882.      * some of which are not always of interest. These methods here will
  883.      * extract the various parts, assuming the following syntanx (taken from
  884.      * RFC-2616):
  885.      *
  886.      * <PRE>
  887.      * resource = [ "/" ] [ path ] [ ";" params ] [ "?" query ] [ "#" fragment ]
  888.      * </PRE>
  889.      *
  890.      * @param the resource to split
  891.      * @return the path, including any leading "/"
  892.      * @see #getParams
  893.      * @see #getQuery
  894.      * @see #getFragment
  895.      */
  896.     public final static String getPath(String resource)
  897.     {
  898. int p, end = resource.length();
  899. if ((p = resource.indexOf('#')) != -1) // find fragment
  900.     end = p;
  901. if ((p = resource.indexOf('?')) != -1  &&  p < end) // find query
  902.     end = p;
  903. if ((p = resource.indexOf(';')) != -1  &&  p < end) // find params
  904.     end = p;
  905. return resource.substring(0, end);
  906.     }
  907.     /**
  908.      * Extract the params part from an http resource.
  909.      *
  910.      * @param the resource to split
  911.      * @return the params, or null if there are none
  912.      * @see #getPath
  913.      */
  914.     public final static String getParams(String resource)
  915.     {
  916. int beg, f, q;
  917. if ((beg = resource.indexOf(';')) == -1) // find params
  918.     return null;
  919. if ((f = resource.indexOf('#')) != -1  &&  f < beg) // find fragment
  920.     return null;
  921. if ((q = resource.indexOf('?')) != -1  &&  q < beg) // find query
  922.     return null;
  923. if (q == -1  &&  f == -1)
  924.     return resource.substring(beg+1);
  925. if (f == -1  ||  (q != -1  &&  q < f))
  926.     return resource.substring(beg+1, q);
  927. else
  928.     return resource.substring(beg+1, f);
  929.     }
  930.     /**
  931.      * Extract the query string from an http resource.
  932.      *
  933.      * @param the resource to split
  934.      * @return the query, or null if there was none
  935.      * @see #getPath
  936.      */
  937.     public final static String getQuery(String resource)
  938.     {
  939. int beg, f;
  940. if ((beg = resource.indexOf('?')) == -1) // find query
  941.     return null;
  942. if ((f = resource.indexOf('#')) != -1  &&  f < beg) // find fragment
  943.     return null; // '?' is in fragment
  944. if (f == -1)
  945.     return resource.substring(beg+1); // no fragment
  946. else
  947.     return resource.substring(beg+1, f); // strip fragment
  948.     }
  949.     /**
  950.      * Extract the fragment part from an http resource.
  951.      *
  952.      * @param the resource to split
  953.      * @return the fragment, or null if there was none
  954.      * @see #getPath
  955.      */
  956.     public final static String getFragment(String resource)
  957.     {
  958. int beg;
  959. if ((beg = resource.indexOf('#')) == -1) // find fragment
  960.     return null;
  961. else
  962.     return resource.substring(beg+1);
  963.     }
  964.     /**
  965.      * Match <var>pattern</var> against <var>name</var>, where
  966.      * <var>pattern</var> may contain wildcards ('*').
  967.      *
  968.      * @param pattern the pattern to match; may contain '*' which match
  969.      *                any number (0 or more) of any character (think file
  970.      *                globbing)
  971.      * @param name    the name to match against the pattern
  972.      * @return true if the name matches the pattern; false otherwise
  973.      */
  974.     public static final boolean wildcardMatch(String pattern, String name)
  975.     {
  976. return
  977.     wildcardMatch(pattern, name, 0, 0, pattern.length(), name.length());
  978.     }
  979.     private static final boolean wildcardMatch(String pattern, String name,
  980.        int ppos, int npos, int plen,
  981.        int nlen)
  982.     {
  983. // find wildcard
  984. int star = pattern.indexOf('*', ppos);
  985. if (star < 0)
  986. {
  987.     return ((plen-ppos) == (nlen-npos)  &&
  988.     pattern.regionMatches(ppos, name, npos, plen-ppos));
  989. }
  990. // match prefix
  991. if (!pattern.regionMatches(ppos, name, npos, star-ppos))
  992.     return false;
  993. // match suffix
  994. if (star == plen-1)
  995.     return true;
  996. while(!wildcardMatch(pattern, name, star+1, npos, plen, nlen)  &&
  997.       npos < nlen)
  998.     npos++;
  999. return (npos < nlen);
  1000.     }
  1001. }