1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This code is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License version 2 only, as
8  * published by the Free Software Foundation.  Oracle designates this
9  * particular file as subject to the "Classpath" exception as provided
10  * by Oracle in the LICENSE file that accompanied this code.
11  *
12  * This code is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15  * version 2 for more details (a copy is included in the LICENSE file that
16  * accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License version
19  * 2 along with this work; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21  *
22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23  * or visit www.oracle.com if you need additional information or have any
24  * questions.
25  */
26 
27 package java.net;
28 
29 import java.io.IOException;
30 import java.util.Objects;
31 
32 import sun.net.util.IPAddressUtil;
33 
34 /**
35  * The abstract class {@code URLStreamHandler} is the common
36  * superclass for all stream protocol handlers. A stream protocol
37  * handler knows how to make a connection for a particular protocol
38  * type, such as {@code http} or {@code https}.
39  * <p>
40  * In most cases, an instance of a {@code URLStreamHandler}
41  * subclass is not created directly by an application. Rather, the
42  * first time a protocol name is encountered when constructing a
43  * {@code URL}, the appropriate stream protocol handler is
44  * automatically loaded.
45  *
46  * @author  James Gosling
47  * @see     java.net.URL#URL(java.lang.String, java.lang.String, int, java.lang.String)
48  * @since   JDK1.0
49  */
50 public abstract class URLStreamHandler {
51     /**
52      * Opens a connection to the object referenced by the
53      * {@code URL} argument.
54      * This method should be overridden by a subclass.
55      *
56      * <p>If for the handler's protocol (such as HTTP or JAR), there
57      * exists a public, specialized URLConnection subclass belonging
58      * to one of the following packages or one of their subpackages:
59      * java.lang, java.io, java.util, java.net, the connection
60      * returned will be of that subclass. For example, for HTTP an
61      * HttpURLConnection will be returned, and for JAR a
62      * JarURLConnection will be returned.
63      *
64      * @param      u   the URL that this connects to.
65      * @return     a {@code URLConnection} object for the {@code URL}.
66      * @exception  IOException  if an I/O error occurs while opening the
67      *               connection.
68      */
openConnection(URL u)69     abstract protected URLConnection openConnection(URL u) throws IOException;
70 
71     /**
72      * Same as openConnection(URL), except that the connection will be
73      * made through the specified proxy; Protocol handlers that do not
74      * support proxying will ignore the proxy parameter and make a
75      * normal connection.
76      *
77      * Calling this method preempts the system's default ProxySelector
78      * settings.
79      *
80      * @param      u   the URL that this connects to.
81      * @param      p   the proxy through which the connection will be made.
82      *                 If direct connection is desired, Proxy.NO_PROXY
83      *                 should be specified.
84      * @return     a {@code URLConnection} object for the {@code URL}.
85      * @exception  IOException  if an I/O error occurs while opening the
86      *               connection.
87      * @exception  IllegalArgumentException if either u or p is null,
88      *               or p has the wrong type.
89      * @exception  UnsupportedOperationException if the subclass that
90      *               implements the protocol doesn't support this method.
91      * @since      1.5
92      */
openConnection(URL u, Proxy p)93     protected URLConnection openConnection(URL u, Proxy p) throws IOException {
94         throw new UnsupportedOperationException("Method not implemented.");
95     }
96 
97     /**
98      * Parses the string representation of a {@code URL} into a
99      * {@code URL} object.
100      * <p>
101      * If there is any inherited context, then it has already been
102      * copied into the {@code URL} argument.
103      * <p>
104      * The {@code parseURL} method of {@code URLStreamHandler}
105      * parses the string representation as if it were an
106      * {@code http} specification. Most URL protocol families have a
107      * similar parsing. A stream protocol handler for a protocol that has
108      * a different syntax must override this routine.
109      *
110      * @param   u       the {@code URL} to receive the result of parsing
111      *                  the spec.
112      * @param   spec    the {@code String} representing the URL that
113      *                  must be parsed.
114      * @param   start   the character index at which to begin parsing. This is
115      *                  just past the '{@code :}' (if there is one) that
116      *                  specifies the determination of the protocol name.
117      * @param   limit   the character position to stop parsing at. This is the
118      *                  end of the string or the position of the
119      *                  "{@code #}" character, if present. All information
120      *                  after the sharp sign indicates an anchor.
121      */
parseURL(URL u, String spec, int start, int limit)122     protected void parseURL(URL u, String spec, int start, int limit) {
123         // These fields may receive context content if this was relative URL
124         String protocol = u.getProtocol();
125         String authority = u.getAuthority();
126         String userInfo = u.getUserInfo();
127         String host = u.getHost();
128         int port = u.getPort();
129         String path = u.getPath();
130         String query = u.getQuery();
131 
132         // This field has already been parsed
133         String ref = u.getRef();
134 
135         boolean isRelPath = false;
136         boolean queryOnly = false;
137         // BEGIN Android-changed
138         boolean querySet = false;
139         // END Android-changed
140 
141 // FIX: should not assume query if opaque
142         // Strip off the query part
143         if (start < limit) {
144             int queryStart = spec.indexOf('?');
145             queryOnly = queryStart == start;
146             if ((queryStart != -1) && (queryStart < limit)) {
147                 query = spec.substring(queryStart+1, limit);
148                 if (limit > queryStart)
149                     limit = queryStart;
150                 spec = spec.substring(0, queryStart);
151                 // BEGIN Android-changed
152                 querySet = true;
153                 // END Android-changed
154             }
155         }
156 
157         int i = 0;
158         // Parse the authority part if any
159         // BEGIN Android-changed
160         // boolean isUNCName = (start <= limit - 4) &&
161         //                 (spec.charAt(start) == '/') &&
162         //                 (spec.charAt(start + 1) == '/') &&
163         //                 (spec.charAt(start + 2) == '/') &&
164         //                 (spec.charAt(start + 3) == '/');
165         boolean isUNCName = false;
166         // END Android-changed
167         if (!isUNCName && (start <= limit - 2) && (spec.charAt(start) == '/') &&
168             (spec.charAt(start + 1) == '/')) {
169             start += 2;
170             i = spec.indexOf('/', start);
171             if (i < 0 || i > limit) {
172                 i = spec.indexOf('?', start);
173                 if (i < 0 || i > limit)
174                     i = limit;
175             }
176 
177             host = authority = spec.substring(start, i);
178 
179             int ind = authority.indexOf('@');
180             if (ind != -1) {
181                 if (ind != authority.lastIndexOf('@')) {
182                     // more than one '@' in authority. This is not server based
183                     userInfo = null;
184                     host = null;
185                 } else {
186                     userInfo = authority.substring(0, ind);
187                     host = authority.substring(ind+1);
188                 }
189             } else {
190                 userInfo = null;
191             }
192             if (host != null) {
193                 // If the host is surrounded by [ and ] then its an IPv6
194                 // literal address as specified in RFC2732
195                 if (host.length()>0 && (host.charAt(0) == '[')) {
196                     if ((ind = host.indexOf(']')) > 2) {
197 
198                         String nhost = host ;
199                         host = nhost.substring(0,ind+1);
200                         if (!IPAddressUtil.
201                             isIPv6LiteralAddress(host.substring(1, ind))) {
202                             throw new IllegalArgumentException(
203                                 "Invalid host: "+ host);
204                         }
205 
206                         port = -1 ;
207                         if (nhost.length() > ind+1) {
208                             if (nhost.charAt(ind+1) == ':') {
209                                 ++ind ;
210                                 // port can be null according to RFC2396
211                                 if (nhost.length() > (ind + 1)) {
212                                     port = Integer.parseInt(nhost.substring(ind+1));
213                                 }
214                             } else {
215                                 throw new IllegalArgumentException(
216                                     "Invalid authority field: " + authority);
217                             }
218                         }
219                     } else {
220                         throw new IllegalArgumentException(
221                             "Invalid authority field: " + authority);
222                     }
223                 } else {
224                     ind = host.indexOf(':');
225                     port = -1;
226                     if (ind >= 0) {
227                         // port can be null according to RFC2396
228                         if (host.length() > (ind + 1)) {
229                             // BEGIN Android-changed
230                             // port = Integer.parseInt(host.substring(ind + 1));
231                             char firstPortChar = host.charAt(ind+1);
232                             if (firstPortChar >= '0' && firstPortChar <= '9') {
233                                 port = Integer.parseInt(host.substring(ind + 1));
234                             } else {
235                                 throw new IllegalArgumentException("invalid port: " +
236                                                                    host.substring(ind + 1));
237                             }
238                             // END Android-changed
239                         }
240                         host = host.substring(0, ind);
241                     }
242                 }
243             } else {
244                 host = "";
245             }
246             if (port < -1)
247                 throw new IllegalArgumentException("Invalid port number :" +
248                                                    port);
249             start = i;
250 
251             // BEGIN Android-changed
252             // If the authority is defined then the path is defined by the
253             // spec only; See RFC 2396 Section 5.2.4.
254             // if (authority != null && authority.length() > 0)
255             //   path = "";
256             path = null;
257             if (!querySet) {
258                 query = null;
259             }
260             // END Android-changed
261         }
262 
263         if (host == null) {
264             host = "";
265         }
266 
267         // Parse the file path if any
268         if (start < limit) {
269             if (spec.charAt(start) == '/') {
270                 path = spec.substring(start, limit);
271             } else if (path != null && path.length() > 0) {
272                 isRelPath = true;
273                 int ind = path.lastIndexOf('/');
274                 String seperator = "";
275                 if (ind == -1 && authority != null)
276                     seperator = "/";
277                 path = path.substring(0, ind + 1) + seperator +
278                          spec.substring(start, limit);
279 
280             } else {
281                 String seperator = (authority != null) ? "/" : "";
282                 path = seperator + spec.substring(start, limit);
283             }
284         }
285         // BEGIN Android-changed
286         //else if (queryOnly && path != null) {
287         //    int ind = path.lastIndexOf('/');
288         //    if (ind < 0)
289         //        ind = 0;
290         //    path = path.substring(0, ind) + "/";
291         //}
292         // END Android-changed
293         if (path == null)
294             path = "";
295 
296         // BEGIN Android-changed
297         //if (isRelPath) {
298         if (true) {
299         // END Android-changed
300             // Remove embedded /./
301             while ((i = path.indexOf("/./")) >= 0) {
302                 path = path.substring(0, i) + path.substring(i + 2);
303             }
304             // Remove embedded /../ if possible
305             i = 0;
306             while ((i = path.indexOf("/../", i)) >= 0) {
307                 // BEGIN Android-changed
308                 /*
309                  * Trailing /../
310                  */
311                 if (i == 0) {
312                     path = path.substring(i + 3);
313                     i = 0;
314                 // END Android-changed
315                 /*
316                  * A "/../" will cancel the previous segment and itself,
317                  * unless that segment is a "/../" itself
318                  * i.e. "/a/b/../c" becomes "/a/c"
319                  * but "/../../a" should stay unchanged
320                  */
321                 } else if (i > 0 && (limit = path.lastIndexOf('/', i - 1)) >= 0 &&
322                     (path.indexOf("/../", limit) != 0)) {
323                     path = path.substring(0, limit) + path.substring(i + 3);
324                     i = 0;
325                 } else {
326                     i = i + 3;
327                 }
328             }
329             // Remove trailing .. if possible
330             while (path.endsWith("/..")) {
331                 i = path.indexOf("/..");
332                 if ((limit = path.lastIndexOf('/', i - 1)) >= 0) {
333                     path = path.substring(0, limit+1);
334                 } else {
335                     break;
336                 }
337             }
338             // Remove starting .
339             if (path.startsWith("./") && path.length() > 2)
340                 path = path.substring(2);
341 
342             // Remove trailing .
343             if (path.endsWith("/."))
344                 path = path.substring(0, path.length() -1);
345 
346             // Remove trailing ?
347             if (path.endsWith("?"))
348                 path = path.substring(0, path.length() -1);
349         }
350 
351         setURL(u, protocol, host, port, authority, userInfo, path, query, ref);
352     }
353 
354     /**
355      * Returns the default port for a URL parsed by this handler. This method
356      * is meant to be overidden by handlers with default port numbers.
357      * @return the default port for a {@code URL} parsed by this handler.
358      * @since 1.3
359      */
getDefaultPort()360     protected int getDefaultPort() {
361         return -1;
362     }
363 
364     /**
365      * Provides the default equals calculation. May be overidden by handlers
366      * for other protocols that have different requirements for equals().
367      * This method requires that none of its arguments is null. This is
368      * guaranteed by the fact that it is only called by java.net.URL class.
369      * @param u1 a URL object
370      * @param u2 a URL object
371      * @return {@code true} if the two urls are
372      * considered equal, ie. they refer to the same
373      * fragment in the same file.
374      * @since 1.3
375      */
equals(URL u1, URL u2)376     protected boolean equals(URL u1, URL u2) {
377         return Objects.equals(u1.getRef(), u2.getRef()) &&
378                Objects.equals(u1.getQuery(), u2.getQuery()) &&
379                // sameFile compares the protocol, file, port & host components of
380                // the URLs.
381                sameFile(u1, u2);
382     }
383 
384     /**
385      * Provides the default hash calculation. May be overidden by handlers for
386      * other protocols that have different requirements for hashCode
387      * calculation.
388      * @param u a URL object
389      * @return an {@code int} suitable for hash table indexing
390      * @since 1.3
391      */
hashCode(URL u)392     protected int hashCode(URL u) {
393         // Hash on the same set of fields that we compare in equals().
394         return Objects.hash(
395                 u.getRef(),
396                 u.getQuery(),
397                 u.getProtocol(),
398                 u.getFile(),
399                 u.getHost(),
400                 u.getPort());
401     }
402 
403     /**
404      * Compare two urls to see whether they refer to the same file,
405      * i.e., having the same protocol, host, port, and path.
406      * This method requires that none of its arguments is null. This is
407      * guaranteed by the fact that it is only called indirectly
408      * by java.net.URL class.
409      * @param u1 a URL object
410      * @param u2 a URL object
411      * @return true if u1 and u2 refer to the same file
412      * @since 1.3
413      */
sameFile(URL u1, URL u2)414     protected boolean sameFile(URL u1, URL u2) {
415         // Compare the protocols.
416         if (!((u1.getProtocol() == u2.getProtocol()) ||
417               (u1.getProtocol() != null &&
418                u1.getProtocol().equalsIgnoreCase(u2.getProtocol()))))
419             return false;
420 
421         // Compare the files.
422         if (!(u1.getFile() == u2.getFile() ||
423               (u1.getFile() != null && u1.getFile().equals(u2.getFile()))))
424             return false;
425 
426         // Compare the ports.
427         int port1, port2;
428         port1 = (u1.getPort() != -1) ? u1.getPort() : u1.handler.getDefaultPort();
429         port2 = (u2.getPort() != -1) ? u2.getPort() : u2.handler.getDefaultPort();
430         if (port1 != port2)
431             return false;
432 
433         // Compare the hosts.
434         if (!hostsEqual(u1, u2))
435             return false;
436 
437         return true;
438     }
439 
440     /**
441      * Get the IP address of our host. An empty host field or a DNS failure
442      * will result in a null return.
443      *
444      * @param u a URL object
445      * @return an {@code InetAddress} representing the host
446      * IP address.
447      * @since 1.3
448      */
getHostAddress(URL u)449     protected synchronized InetAddress getHostAddress(URL u) {
450         if (u.hostAddress != null)
451             return u.hostAddress;
452 
453         String host = u.getHost();
454         if (host == null || host.equals("")) {
455             return null;
456         } else {
457             try {
458                 u.hostAddress = InetAddress.getByName(host);
459             } catch (UnknownHostException ex) {
460                 return null;
461             } catch (SecurityException se) {
462                 return null;
463             }
464         }
465         return u.hostAddress;
466     }
467 
468     /**
469      * Compares the host components of two URLs.
470      * @param u1 the URL of the first host to compare
471      * @param u2 the URL of the second host to compare
472      * @return  {@code true} if and only if they
473      * are equal, {@code false} otherwise.
474      * @since 1.3
475      */
hostsEqual(URL u1, URL u2)476     protected boolean hostsEqual(URL u1, URL u2) {
477         // Android-changed: Don't compare the InetAddresses of the hosts.
478         if (u1.getHost() != null && u2.getHost() != null)
479             return u1.getHost().equalsIgnoreCase(u2.getHost());
480          else
481             return u1.getHost() == null && u2.getHost() == null;
482     }
483 
484     /**
485      * Converts a {@code URL} of a specific protocol to a
486      * {@code String}.
487      *
488      * @param   u   the URL.
489      * @return  a string representation of the {@code URL} argument.
490      */
toExternalForm(URL u)491     protected String toExternalForm(URL u) {
492 
493         // pre-compute length of StringBuffer
494         int len = u.getProtocol().length() + 1;
495         if (u.getAuthority() != null && u.getAuthority().length() > 0)
496             len += 2 + u.getAuthority().length();
497         if (u.getPath() != null) {
498             len += u.getPath().length();
499         }
500         if (u.getQuery() != null) {
501             len += 1 + u.getQuery().length();
502         }
503         if (u.getRef() != null)
504             len += 1 + u.getRef().length();
505 
506         StringBuilder result = new StringBuilder(len);
507         result.append(u.getProtocol());
508         result.append(":");
509         if (u.getAuthority() != null) {// ANDROID: && u.getAuthority().length() > 0) {
510             result.append("//");
511             result.append(u.getAuthority());
512         }
513         String fileAndQuery = u.getFile();
514         if (fileAndQuery != null) {
515             result.append(fileAndQuery);
516         }
517         if (u.getRef() != null) {
518             result.append("#");
519             result.append(u.getRef());
520         }
521         return result.toString();
522     }
523 
524     /**
525      * Sets the fields of the {@code URL} argument to the indicated values.
526      * Only classes derived from URLStreamHandler are able
527      * to use this method to set the values of the URL fields.
528      *
529      * @param   u         the URL to modify.
530      * @param   protocol  the protocol name.
531      * @param   host      the remote host value for the URL.
532      * @param   port      the port on the remote machine.
533      * @param   authority the authority part for the URL.
534      * @param   userInfo the userInfo part of the URL.
535      * @param   path      the path component of the URL.
536      * @param   query     the query part for the URL.
537      * @param   ref       the reference.
538      * @exception       SecurityException       if the protocol handler of the URL is
539      *                                  different from this one
540      * @see     java.net.URL#set(java.lang.String, java.lang.String, int, java.lang.String, java.lang.String)
541      * @since 1.3
542      */
setURL(URL u, String protocol, String host, int port, String authority, String userInfo, String path, String query, String ref)543        protected void setURL(URL u, String protocol, String host, int port,
544                              String authority, String userInfo, String path,
545                              String query, String ref) {
546         if (this != u.handler) {
547             throw new SecurityException("handler for url different from " +
548                                         "this handler");
549         }
550         // ensure that no one can reset the protocol on a given URL.
551         u.set(u.getProtocol(), host, port, authority, userInfo, path, query, ref);
552     }
553 
554     /**
555      * Sets the fields of the {@code URL} argument to the indicated values.
556      * Only classes derived from URLStreamHandler are able
557      * to use this method to set the values of the URL fields.
558      *
559      * @param   u         the URL to modify.
560      * @param   protocol  the protocol name. This value is ignored since 1.2.
561      * @param   host      the remote host value for the URL.
562      * @param   port      the port on the remote machine.
563      * @param   file      the file.
564      * @param   ref       the reference.
565      * @exception       SecurityException       if the protocol handler of the URL is
566      *                                  different from this one
567      * @deprecated Use setURL(URL, String, String, int, String, String, String,
568      *             String);
569      */
570     @Deprecated
setURL(URL u, String protocol, String host, int port, String file, String ref)571     protected void setURL(URL u, String protocol, String host, int port,
572                           String file, String ref) {
573         /*
574          * Only old URL handlers call this, so assume that the host
575          * field might contain "user:passwd@host". Fix as necessary.
576          */
577         String authority = null;
578         String userInfo = null;
579         if (host != null && host.length() != 0) {
580             authority = (port == -1) ? host : host + ":" + port;
581             int at = host.lastIndexOf('@');
582             if (at != -1) {
583                 userInfo = host.substring(0, at);
584                 host = host.substring(at+1);
585             }
586         }
587 
588         /*
589          * Assume file might contain query part. Fix as necessary.
590          */
591         String path = null;
592         String query = null;
593         if (file != null) {
594             int q = file.lastIndexOf('?');
595             if (q != -1) {
596                 query = file.substring(q+1);
597                 path = file.substring(0, q);
598             } else
599                 path = file;
600         }
601         setURL(u, protocol, host, port, authority, userInfo, path, query, ref);
602     }
603 }
604