1 /*
   2  * $Id$
   3  *
   4  * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
   5  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   6  *
   7  * This code is free software; you can redistribute it and/or modify it
   8  * under the terms of the GNU General Public License version 2 only, as
   9  * published by the Free Software Foundation.  Oracle designates this
  10  * particular file as subject to the "Classpath" exception as provided
  11  * by Oracle in the LICENSE file that accompanied this code.
  12  *
  13  * This code is distributed in the hope that it will be useful, but WITHOUT
  14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  16  * version 2 for more details (a copy is included in the LICENSE file that
  17  * accompanied this code).
  18  *
  19  * You should have received a copy of the GNU General Public License version
  20  * 2 along with this work; if not, write to the Free Software Foundation,
  21  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  22  *
  23  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  24  * or visit www.oracle.com if you need additional information or have any
  25  * questions.
  26  */
  27 package com.sun.javatest.httpd;
  28 
  29 import java.util.Dictionary;
  30 import java.util.Properties;
  31 
  32 /**
  33  * URL object including support for key-value pairs.
  34  */
  35 
  36 public class httpURL {
  37     public httpURL(String url) {
  38         firstQ = url.indexOf('?');
  39         file = url;
  40         fileLen = file.length();
  41 
  42         resetIterator();
  43 
  44         if (firstQ != -1) {
  45             parseURLKeys();
  46         }
  47     }
  48 
  49     /**
  50      * Get the next part of the requested path.  This is an iterator.
  51      * Ex: from /harness/foo/, it may return "harness" or "foo".
  52      *
  53      * @return null if the end of the file path has been reached
  54      */
  55     public String getNextFile() {
  56         // XXX 4 May 2000 rewrite this method so it is not so complex!
  57         if (pathPos == fileLen) return null;
  58 
  59         String ss;   // the substring of the whole file name
  60         if (pathPos == fileLen - 1) {   // * special cases
  61             if (fileLen == 1 && file.charAt(0) == '/') {        // should only happen when full URL is "/"
  62                 ss = "/";
  63                 pathPos = fileLen;
  64             }
  65             else {
  66                 ss = file.substring(pathPos, fileLen);
  67                 pathPos = fileLen;
  68             }
  69         }
  70         else {                          // * normal cases
  71             int nextPos = file.indexOf('/', pathPos+1);
  72 
  73             if (nextPos == -1) {
  74                 // give the remainder of the string up to:
  75                 // 1) the first question mark ahead
  76                 // 2) the end of the string
  77                 String result = file.substring(pathPos, (firstQ == -1 ? fileLen : firstQ));
  78                 pathPos = fileLen;
  79                 ss = result;
  80             }
  81             else {
  82                 ss = file.substring(pathPos, nextPos);
  83                 pathPos = nextPos + 1;
  84             }
  85         }
  86 
  87         //System.out.println("next file is: " + ss);
  88         return ss;
  89     }
  90 
  91     public void resetIterator() {
  92         // if the URL is only a slash, we let that through
  93         // otherwise we ignore the leading slash
  94         // this allows the web server root page to work, eg. http://foo:1903/
  95         if (file.indexOf('/') == 0 && fileLen > 1)
  96             pathPos = 1;
  97         else
  98             pathPos = 0;
  99     }
 100 
 101     /**
 102      * Returns the URL, minus the protocol, hostname and port.
 103      */
 104     public String getFullPath() {
 105         return file;
 106     }
 107 
 108     /**
 109      * Get the host portion of the URL.
 110      *
 111      * @throws Fault This info is not always available, so this exception may
 112      *               be thrown.
 113      */
 114     public String getLocalHost() throws Fault {
 115         if (lHost == null)
 116             throw new Fault("Local hostname for URL not available");
 117         else
 118             return lHost;
 119     }
 120 
 121     /**
 122      * Get the local hostname portion of the URL.
 123      *
 124      * @throws Fault This info is not always available, so this exception may
 125      *               be thrown.
 126      */
 127     public int getLocalPort() throws Fault {
 128         if (lPort == -1)
 129             throw new Fault("Local port for URL not available");
 130         else
 131             return lPort;
 132     }
 133 
 134     /**
 135      * Get the remote hostname.  Fully resolved host names are not required.
 136      *
 137      * @throws Fault This info is not always available, so this exception may
 138      *               be thrown.
 139      */
 140     public String getRemoteHost() throws Fault {
 141         if (rHost == null)
 142             throw new Fault("Remote hostname for URL not available");
 143         else
 144             return rHost;
 145     }
 146 
 147     // ------- Key-Value Processing ---------
 148     /**
 149      * Set the key-value pairs encoded in this URL.
 150      * Not implemented yet.
 151      */
 152     public void setProperties(Dictionary<String, String> props) {
 153     }
 154 
 155     /**
 156      * Not implemented yet.
 157      */
 158     public String[] getKeys() {
 159         return null;
 160     }
 161 
 162     /**
 163      * Not implemented yet.
 164      */
 165     public String[] getValues() {
 166         return null;
 167     }
 168 
 169     /**
 170      * Not implemented yet.
 171      */
 172     public String getValue(String key) {
 173         return urlValues.getProperty(key);
 174     }
 175 
 176     // ----------- Utility -----------
 177     /**
 178      * Assembles a slash separated path from the elements of the given array.
 179      * Null entries in the array are ignored.
 180      *
 181      * @param path An empty or null array results in a zero length string.
 182      * @param leadingSlash Whether or not to put a slash at the beginning of the path
 183      * @param trailSlash Whether or not to put a slash at the end of the path
 184      */
 185     public static String reassemblePath(String[] path, boolean leadingSlash,
 186                                         boolean trailSlash) {
 187         if (path == null || path.length == 0)
 188             return "";
 189 
 190         StringBuffer result = new StringBuffer();
 191 
 192         if (leadingSlash) result.append("/");
 193 
 194         for (int i = 0; i < path.length; i++) {
 195             if (path[i] != null) {
 196                 result.append(path[i]);
 197                 result.append("/");
 198             }
 199         }   // for
 200 
 201         // remove trailing slash if needed
 202         if (!trailSlash && result.length() > 1) result.setLength(result.length()-1);
 203 
 204         return result.toString();
 205     }
 206 
 207     // ----------- NON-PUBLIC ------------
 208 
 209     void setRemoteHost(String name) {
 210         rHost = name;
 211     }
 212 
 213     void setLocalHost(String name) {
 214         lHost = name;
 215     }
 216 
 217     void setLocalPort(int port) {
 218         lPort = port;
 219     }
 220 
 221     private void parseURLKeys() {
 222         String data = file.substring(firstQ + 1, fileLen);
 223 
 224         if (data.length() == 0) return;
 225 
 226         String key = null;
 227         int pos = 0;
 228         int dataLen = data.length();    // optimization
 229 
 230         while ((key = readSegment(data, pos, dataLen)) != null) {
 231             pos = pos + key.length();
 232             if (debug) System.out.println("   Read key: " + key);
 233 
 234             if (pos >= dataLen || data.charAt(pos) == '&') {
 235                 // there is no value with this key
 236                 if (debug) System.out.println("key: " + key + "  value: null");
 237                 urlValues.put(key, "true");
 238             }
 239             else {
 240                 // char at pos must be '='
 241                 String val = readSegment(data, ++pos, dataLen);
 242                 if (debug) System.out.println("key: " + key + "  value: " + val);
 243                 urlValues.put(key, val);
 244 
 245                 // assume that the resulting value will either jump over
 246                 // the & or put us past the end of the string
 247                 // http://machine1:1904/version?apple=orange&grape=apricot
 248                 //                                         ^
 249                 //                                        pos
 250                 pos = pos + val.length() + 1;
 251 
 252                 // end of data string
 253                 if (pos > dataLen) break;
 254             }
 255         }   // while
 256     }
 257 
 258     private String readSegment(String data, int position, int dataLen) {
 259         StringBuffer buf = new StringBuffer();
 260         int i = position;
 261 
 262         // loop until you hit end of string, a & or a =
 263         while (i < dataLen && data.charAt(i) != '&' &&
 264                data.charAt(i) != '=') {
 265             buf.append(data.charAt(i));
 266             i++;
 267         }   // while
 268 
 269         // a zero length string is not acceptable
 270         return (buf.length() == 0 ? null : buf.toString());
 271     }
 272 
 273     private String lHost;
 274     private int lPort = -1;
 275     private String rHost;
 276     private int pathPos;
 277     private int firstQ;     // location of the first question mark in the file path
 278     protected static final boolean debug = Boolean.getBoolean("debug." + httpURL.class.getName());
 279 
 280     /**
 281      * The URL, of the format:
 282      * <tt>/harness/foo/index.html?key1=value1?key2=value2</tt>
 283      */
 284     private String file;
 285 
 286     /**
 287      * The length of the file string.  Used often.
 288      */
 289     private int fileLen;
 290 
 291     /**
 292      * Any key-values encoded in the URL string.
 293      */
 294     private Properties urlValues = new Properties();
 295 
 296     public static class Fault extends Exception {
 297         public Fault(String s) {
 298             super(s);
 299         }
 300 
 301         /**
 302          * Provide description and pass-thru exception
 303          */
 304         public Fault(String s, Throwable e) {
 305             super(s);
 306         }
 307 
 308         /**
 309          * Get the original exception.
 310          */
 311         public Throwable getException() {
 312             return orig;
 313         }
 314 
 315         private Throwable orig;
 316     }
 317 }
 318