1adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/* 2adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Licensed to the Apache Software Foundation (ASF) under one or more 3adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * contributor license agreements. See the NOTICE file distributed with 4adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * this work for additional information regarding copyright ownership. 5adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * The ASF licenses this file to You under the Apache License, Version 2.0 6adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * (the "License"); you may not use this file except in compliance with 7adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the License. You may obtain a copy of the License at 8adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * 9adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 10adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * 11adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software 12adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 13adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * See the License for the specific language governing permissions and 15adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * limitations under the License. 16adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 17adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 18adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpackage java.net; 19adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 20adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.IOException; 215292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilsonimport libcore.net.url.UrlUtils; 226186821cb13f4ac7ff50950c813394367e021eaeJesse Wilsonimport libcore.util.Objects; 23adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 24adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/** 25adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * The abstract class {@code URLStreamHandler} is the base for all classes which 26adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * can handle the communication with a URL object over a particular protocol 27adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * type. 28adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 29adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpublic abstract class URLStreamHandler { 30adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 31adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Establishes a new connection to the resource specified by the URL {@code 32adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * u}. Since different protocols also have unique ways of connecting, it 33adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * must be overwritten by the subclass. 34f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes * 35adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @param u 36adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the URL to the resource where a connection has to be opened. 37adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @return the opened URLConnection to the specified resource. 38adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @throws IOException 39adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * if an I/O error occurs during opening the connection. 40adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 41adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project protected abstract URLConnection openConnection(URL u) throws IOException; 42adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 43adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 44adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Establishes a new connection to the resource specified by the URL {@code 45adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * u} using the given {@code proxy}. Since different protocols also have 46adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * unique ways of connecting, it must be overwritten by the subclass. 47f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes * 48adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @param u 49adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the URL to the resource where a connection has to be opened. 50adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @param proxy 51adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the proxy that is used to make the connection. 52adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @return the opened URLConnection to the specified resource. 53adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @throws IOException 54adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * if an I/O error occurs during opening the connection. 55adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @throws IllegalArgumentException 56adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * if any argument is {@code null} or the type of proxy is 57adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * wrong. 58adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @throws UnsupportedOperationException 59adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * if the protocol handler doesn't support this method. 60adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 61b1396870f92135aa140bd2b86221768dea5bc11dElliott Hughes protected URLConnection openConnection(URL u, Proxy proxy) throws IOException { 62b1396870f92135aa140bd2b86221768dea5bc11dElliott Hughes throw new UnsupportedOperationException(); 63adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 64adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 65adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 66adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Parses the clear text URL in {@code str} into a URL object. URL strings 67adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * generally have the following format: 68adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * <p> 69adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * http://www.company.com/java/file1.java#reference 70f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson * <p> 71adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * The string is parsed in HTTP format. If the protocol has a different URL 72adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * format this method must be overridden. 73f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes * 745292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson * @param url 75adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the URL to fill in the parsed clear text URL parts. 765292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson * @param spec 77adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the URL string that is to be parsed. 78adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @param start 79adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the string position from where to begin parsing. 80adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @param end 81adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the string position to stop parsing. 82adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @see #toExternalForm 83adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @see URL 84adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 855292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson protected void parseURL(URL url, String spec, int start, int end) { 865292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson if (this != url.streamHandler) { 875292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson throw new SecurityException("Only a URL's stream handler is permitted to mutate it"); 88adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 89adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project if (end < start) { 905292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson throw new StringIndexOutOfBoundsException(spec, start, end - start); 91adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 92adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 935292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson int fileStart; 945292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson String authority; 955292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson String userInfo; 965292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson String host; 975292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson int port = -1; 985292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson String path; 995292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson String query; 1005292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson String ref; 1015292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson if (spec.regionMatches(start, "//", 0, 2)) { 1025292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson // Parse the authority from the spec. 1035292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson int authorityStart = start + 2; 104ce257b03a1e5ff6075967e6a84cdb439cb2b01c8Jesse Wilson fileStart = UrlUtils.findFirstOf(spec, "/?#", authorityStart, end); 1055292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson authority = spec.substring(authorityStart, fileStart); 106ce257b03a1e5ff6075967e6a84cdb439cb2b01c8Jesse Wilson int userInfoEnd = UrlUtils.findFirstOf(spec, "@", authorityStart, fileStart); 1075292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson int hostStart; 1085292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson if (userInfoEnd != fileStart) { 1095292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson userInfo = spec.substring(authorityStart, userInfoEnd); 1105292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson hostStart = userInfoEnd + 1; 1118fd0225a5c3918fe0cd4680258388985c25533e5Jesse Wilson } else { 1125292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson userInfo = null; 1135292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson hostStart = authorityStart; 114adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 115adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 1165292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson /* 1175292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson * Extract the host and port. The host may be an IPv6 address with 1185292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson * colons like "[::1]", in which case we look for the port delimiter 1195292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson * colon after the ']' character. 1205292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson */ 12198c564c72480313ae2954a7f0b666ff94345a2f1Jesse Wilson int colonSearchFrom = hostStart; 122ce257b03a1e5ff6075967e6a84cdb439cb2b01c8Jesse Wilson int ipv6End = UrlUtils.findFirstOf(spec, "]", hostStart, fileStart); 12398c564c72480313ae2954a7f0b666ff94345a2f1Jesse Wilson if (ipv6End != fileStart) { 12498c564c72480313ae2954a7f0b666ff94345a2f1Jesse Wilson if (UrlUtils.findFirstOf(spec, ":", hostStart, ipv6End) == ipv6End) { 12598c564c72480313ae2954a7f0b666ff94345a2f1Jesse Wilson throw new IllegalArgumentException("Expected an IPv6 address: " 12698c564c72480313ae2954a7f0b666ff94345a2f1Jesse Wilson + spec.substring(hostStart, ipv6End + 1)); 12798c564c72480313ae2954a7f0b666ff94345a2f1Jesse Wilson } 12898c564c72480313ae2954a7f0b666ff94345a2f1Jesse Wilson colonSearchFrom = ipv6End; 12998c564c72480313ae2954a7f0b666ff94345a2f1Jesse Wilson } 130ce257b03a1e5ff6075967e6a84cdb439cb2b01c8Jesse Wilson int hostEnd = UrlUtils.findFirstOf(spec, ":", colonSearchFrom, fileStart); 1315292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson host = spec.substring(hostStart, hostEnd); 1325292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson int portStart = hostEnd + 1; 1335292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson if (portStart < fileStart) { 134bb3195ff5c5fa60456fe86927bee3aebcbe867aaCalin Juravle char firstPortChar = spec.charAt(portStart); 135bb3195ff5c5fa60456fe86927bee3aebcbe867aaCalin Juravle if (firstPortChar >= '0' && firstPortChar <= '9') { 136bb3195ff5c5fa60456fe86927bee3aebcbe867aaCalin Juravle port = Integer.parseInt(spec.substring(portStart, fileStart)); 137bb3195ff5c5fa60456fe86927bee3aebcbe867aaCalin Juravle } else { 138bb3195ff5c5fa60456fe86927bee3aebcbe867aaCalin Juravle throw new IllegalArgumentException("invalid port: " + port); 139adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 140adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 1415292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson path = null; 142adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project query = null; 1435292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson ref = null; 1445292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson } else { 1455292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson // Get the authority from the context URL. 1465292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson fileStart = start; 1475292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson authority = url.getAuthority(); 1485292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson userInfo = url.getUserInfo(); 1495292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson host = url.getHost(); 1505292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson if (host == null) { 1515292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson host = ""; 1525292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson } 1535292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson port = url.getPort(); 1545292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson path = url.getPath(); 1555292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson query = url.getQuery(); 1565292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson ref = url.getRef(); 157adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 158adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 1595292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson /* 1605292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson * Extract the path, query and fragment. Each part has its own leading 1615292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson * delimiter character. The query can contain slashes and the fragment 1625292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson * can contain slashes and question marks. 1635292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson * / path ? query # fragment 1645292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson */ 1655292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson int pos = fileStart; 1665292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson while (pos < end) { 1675292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson int nextPos; 1685292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson switch (spec.charAt(pos)) { 1695292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson case '#': 1705292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson nextPos = end; 1715292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson ref = spec.substring(pos + 1, nextPos); 1725292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson break; 1735292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson case '?': 174ce257b03a1e5ff6075967e6a84cdb439cb2b01c8Jesse Wilson nextPos = UrlUtils.findFirstOf(spec, "#", pos, end); 1755292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson query = spec.substring(pos + 1, nextPos); 1765292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson ref = null; 1775292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson break; 1785292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson default: 179ce257b03a1e5ff6075967e6a84cdb439cb2b01c8Jesse Wilson nextPos = UrlUtils.findFirstOf(spec, "?#", pos, end); 1805292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson path = relativePath(path, spec.substring(pos, nextPos)); 1815292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson query = null; 1825292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson ref = null; 1835292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson break; 184adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 1855292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson pos = nextPos; 186adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 1875292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson 1885292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson if (path == null) { 1895292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson path = ""; 190adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 191adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 1922d99ef561304174b8ae01a0a68d5b96d5edb9f10Jesse Wilson path = UrlUtils.authoritySafePath(authority, path); 193adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 1945292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson setURL(url, url.getProtocol(), host, port, authority, userInfo, path, query, ref); 1955292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson } 1965292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson 1975292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson /** 1985292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson * Returns a new path by resolving {@code path} relative to {@code base}. 1995292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson */ 2005292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson private static String relativePath(String base, String path) { 2015292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson if (path.startsWith("/")) { 202c5727263001f1eae068f7821063d7bfb2da8e24cJesse Wilson return UrlUtils.canonicalizePath(path, true); 2035292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson } else if (base != null) { 2045292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson String combined = base.substring(0, base.lastIndexOf('/') + 1) + path; 205c5727263001f1eae068f7821063d7bfb2da8e24cJesse Wilson return UrlUtils.canonicalizePath(combined, true); 2065292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson } else { 2075292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson return path; 2085292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson } 209adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 210adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 211adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 212adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Sets the fields of the URL {@code u} to the values of the supplied 213adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * arguments. 214f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes * 215adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @param u 216adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the non-null URL object to be set. 217adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @param protocol 218adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the protocol. 219adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @param host 220adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the host name. 221adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @param port 222adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the port number. 223adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @param file 224adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the file component. 225adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @param ref 226adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the reference. 22799b4489d0555c6e0e5df941cbfad4cf250c8f0b8Elliott Hughes * @deprecated Use setURL(URL, String String, int, String, String, String, 228adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * String, String) instead. 229adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 230adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project @Deprecated 231adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project protected void setURL(URL u, String protocol, String host, int port, 232adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project String file, String ref) { 2338f99aa098c6a06b8be788abbca1c1d1060342709Jesse Wilson if (this != u.streamHandler) { 234adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project throw new SecurityException(); 235adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 236adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project u.set(protocol, host, port, file, ref); 237adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 238adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 239adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 240adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Sets the fields of the URL {@code u} to the values of the supplied 241adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * arguments. 242adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 243adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project protected void setURL(URL u, String protocol, String host, int port, 2445292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson String authority, String userInfo, String path, String query, 245adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project String ref) { 2468f99aa098c6a06b8be788abbca1c1d1060342709Jesse Wilson if (this != u.streamHandler) { 247adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project throw new SecurityException(); 248adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 2495292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson u.set(protocol, host, port, authority, userInfo, path, query, ref); 250adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 251adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 252adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 253adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Returns the clear text representation of a given URL using HTTP format. 254f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes * 255adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @param url 256adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the URL object to be converted. 257adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @return the clear text representation of the specified URL. 258adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @see #parseURL 259adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @see URL#toExternalForm() 260adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 261adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project protected String toExternalForm(URL url) { 262d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson return toExternalForm(url, false); 263d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson } 264d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson 265d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson String toExternalForm(URL url, boolean escapeIllegalCharacters) { 266d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson StringBuilder result = new StringBuilder(); 267d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson result.append(url.getProtocol()); 268d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson result.append(':'); 269d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson 270adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project String authority = url.getAuthority(); 2715292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson if (authority != null) { 272d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson result.append("//"); 273d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson if (escapeIllegalCharacters) { 27432559028b14b9b321b10eede050afd554a376569Jesse Wilson URI.AUTHORITY_ENCODER.appendPartiallyEncoded(result, authority); 27532559028b14b9b321b10eede050afd554a376569Jesse Wilson } else { 27632559028b14b9b321b10eede050afd554a376569Jesse Wilson result.append(authority); 277d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson } 278adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 279adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 280d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson String fileAndQuery = url.getFile(); 281d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson if (fileAndQuery != null) { 282d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson if (escapeIllegalCharacters) { 28332559028b14b9b321b10eede050afd554a376569Jesse Wilson URI.FILE_AND_QUERY_ENCODER.appendPartiallyEncoded(result, fileAndQuery); 28432559028b14b9b321b10eede050afd554a376569Jesse Wilson } else { 28532559028b14b9b321b10eede050afd554a376569Jesse Wilson result.append(fileAndQuery); 286d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson } 287a389b4a499f40379b0b204d7ba1c2057663d95c0Jesse Wilson } 288d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson 289d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson String ref = url.getRef(); 290adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project if (ref != null) { 291d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson result.append('#'); 292d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson if (escapeIllegalCharacters) { 29332559028b14b9b321b10eede050afd554a376569Jesse Wilson URI.ALL_LEGAL_ENCODER.appendPartiallyEncoded(result, ref); 29432559028b14b9b321b10eede050afd554a376569Jesse Wilson } else { 29532559028b14b9b321b10eede050afd554a376569Jesse Wilson result.append(ref); 296d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson } 297adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 298d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson 299d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson return result.toString(); 300d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson } 301d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson 302d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson /** 3037e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson * Returns true if {@code a} and {@code b} have the same protocol, host, 3047e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson * port, file, and reference. 305adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 3067e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson protected boolean equals(URL a, URL b) { 3077e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson return sameFile(a, b) 3087e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson && Objects.equal(a.getRef(), b.getRef()) 3097e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson && Objects.equal(a.getQuery(), b.getQuery()); 310adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 311adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 312adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 313adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Returns the default port of the protocol used by the handled URL. The 3147e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson * default implementation always returns {@code -1}. 315adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 316adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project protected int getDefaultPort() { 317adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project return -1; 318adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 319adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 320adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 3217e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson * Returns the host address of {@code url}. 322adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 323adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project protected InetAddress getHostAddress(URL url) { 324adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project try { 325adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project String host = url.getHost(); 326adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project if (host == null || host.length() == 0) { 327adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project return null; 328adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 329adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project return InetAddress.getByName(host); 330adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } catch (UnknownHostException e) { 331adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project return null; 332adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 333adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 334adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 335adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 3367e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson * Returns the hash code of {@code url}. 337adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 338adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project protected int hashCode(URL url) { 339adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project return toExternalForm(url).hashCode(); 340adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 341adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 342adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 3437e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson * Returns true if the hosts of {@code a} and {@code b} are equal. 344adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 34567db542e53c47a28e3f96beb7c9e46330483dc40Jesse Wilson protected boolean hostsEqual(URL a, URL b) { 3467e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson // URLs with the same case-insensitive host name have equal hosts 3475292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson String aHost = a.getHost(); 3485292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson String bHost = b.getHost(); 3495292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson return (aHost == bHost) || aHost != null && aHost.equalsIgnoreCase(bHost); 350adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 351adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 352adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 3537e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson * Returns true if {@code a} and {@code b} have the same protocol, host, 3547e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson * port and file. 355adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 3567e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson protected boolean sameFile(URL a, URL b) { 3577e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson return Objects.equal(a.getProtocol(), b.getProtocol()) 3587e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson && hostsEqual(a, b) 3597e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson && a.getEffectivePort() == b.getEffectivePort() 3607e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson && Objects.equal(a.getFile(), b.getFile()); 361adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 362adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project} 363