1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.adservices.service.common; 18 19 import android.net.Uri; 20 21 import com.google.common.net.InternetDomainName; 22 23 import java.util.Optional; 24 25 /** Web address utilities. */ 26 public final class WebAddresses { 27 28 private static final String HTTPS_SCHEME = "https"; 29 private static final String LOCALHOST = "localhost"; 30 private static final String LOCALHOST_IP = "127.0.0.1"; 31 WebAddresses()32 private WebAddresses() { } 33 34 /** 35 * Returns a {@code Uri} of the scheme concatenated with the first subdomain of the provided URL 36 * that is beneath the public suffix or localhost. 37 * 38 * @param uri the Uri to parse. 39 */ topPrivateDomainAndScheme(Uri uri)40 public static Optional<Uri> topPrivateDomainAndScheme(Uri uri) { 41 return domainAndScheme(uri, false); 42 } 43 44 /** 45 * Returns an origin of {@code Uri} that is defined by the concatenation of scheme (protocol), 46 * hostname (domain), and port, validating public suffix or localhost. 47 * 48 * @param uri the Uri to parse. 49 * @return 50 */ originAndScheme(Uri uri)51 public static Optional<Uri> originAndScheme(Uri uri) { 52 return domainAndScheme(uri, true); 53 } 54 55 /** 56 * Returns an origin of {@code Uri} that is defined by the concatenation of scheme (protocol), 57 * hostname (domain), and port if useOrigin is true. If useOrigin is false the method returns 58 * the scheme concatenation of first subdomain that is beneath the public suffix or localhost. 59 * 60 * @param uri the Uri to parse 61 * @param useOrigin true if extract origin, false if extract only top domain 62 */ domainAndScheme(Uri uri, boolean useOrigin)63 private static Optional<Uri> domainAndScheme(Uri uri, boolean useOrigin) { 64 String scheme = uri.getScheme(); 65 String host = uri.getHost(); 66 int port = uri.getPort(); 67 68 if (scheme == null || host == null) { 69 return Optional.empty(); 70 } 71 72 String url = scheme + "://"; 73 74 if (isLocalhost(uri)) { 75 url += host; 76 } else { 77 try { 78 InternetDomainName domainName = InternetDomainName.from(host); 79 if (!domainName.hasPublicSuffix()) { 80 return Optional.empty(); 81 } 82 url += useOrigin ? domainName.toString() : domainName.topPrivateDomain().toString(); 83 } catch (IllegalArgumentException | IllegalStateException e) { 84 return Optional.empty(); 85 } 86 } 87 88 if (useOrigin && port >= 0) { 89 url += ":" + port; 90 } 91 92 return Optional.of(Uri.parse(url)); 93 } 94 95 /** 96 * Determines if the provided URI is effectively localhost for Measurement CTS testing. 97 * 98 * @param uri the Uri to parse. 99 */ isLocalhost(Uri uri)100 public static boolean isLocalhost(Uri uri) { 101 String host = uri.getHost(); 102 return HTTPS_SCHEME.equals(uri.getScheme()) 103 && (LOCALHOST.equals(host) || LOCALHOST_IP.equals(host)); 104 } 105 106 /** 107 * Determines if the provided URI is the localhost IP for Measurement CTS testing. 108 * 109 * @param uri the Uri to parse. 110 */ isLocalhostIp(Uri uri)111 public static boolean isLocalhostIp(Uri uri) { 112 return HTTPS_SCHEME.equals(uri.getScheme()) && LOCALHOST_IP.equals(uri.getHost()); 113 } 114 } 115