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