001 /* 002 * Copyright (C) 2009 Google Inc. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 package com.google.common.net; 018 019 import com.google.common.annotations.Beta; 020 021 import java.net.InetAddress; 022 import java.text.ParseException; 023 024 import javax.annotation.Nullable; 025 026 /** 027 * A syntactically valid host specifier, suitable for use in a URI. 028 * This may be either a numeric IP address in IPv4 or IPv6 notation, or a 029 * domain name. 030 * 031 * <p>Because this class is intended to represent host specifiers which can 032 * reasonably be used in a URI, the domain name case is further restricted to 033 * include only those domain names which end in a recognized public suffix; see 034 * {@link InternetDomainName#isPublicSuffix()} for details. 035 * 036 * <p>Note that no network lookups are performed by any {@code HostSpecifier} 037 * methods. No attempt is made to verify that a provided specifier corresponds 038 * to a real or accessible host. Only syntactic and pattern-based checks are 039 * performed. 040 * 041 * <p>If you know that a given string represents a numeric IP address, use 042 * {@link InetAddresses} to obtain and manipulate a 043 * {@link java.net.InetAddress} instance from it rather than using this class. 044 * Similarly, if you know that a given string represents a domain name, use 045 * {@link InternetDomainName} rather than this class. 046 * 047 * @author Craig Berry 048 * @since 5 049 */ 050 @Beta 051 public final class HostSpecifier { 052 053 private final String canonicalForm; 054 055 private HostSpecifier(String canonicalForm) { 056 this.canonicalForm = canonicalForm; 057 } 058 059 /** 060 * Returns a {@code HostSpecifier} built from the provided {@code specifier}, 061 * which is already known to be valid. If the {@code specifier} might be 062 * valid, use {@link #from(String)} instead. 063 * 064 * <p>The specifier must be in one of these formats: 065 * <ul> 066 * <li>A domain name, like {@code google.com} 067 * <li>A IPv4 address string, like {@code 127.0.0.1} 068 * <li>An IPv6 address string with or without brackets, like 069 * {@code [2001:db8::1]} or {@code 2001:db8::1} 070 * <li>An IPv6 address string enclosed in square brackets, like 071 * {[2001:db8::1]} 072 * </ul> 073 * 074 * @throws IllegalArgumentException if the specifier is not valid. 075 */ 076 public static HostSpecifier fromValid(String specifier) { 077 // First, try to interpret the specifier as an IP address. Note we build 078 // the address rather than using the .is* methods because we want to 079 // use InetAddresses.toUriString to convert the result to a string in 080 // canonical form. 081 082 InetAddress addr = null; 083 084 try { 085 addr = InetAddresses.forString(specifier); 086 } catch (IllegalArgumentException e) { 087 // It is not an IPv4 or bracketless IPv6 specifier 088 } 089 090 if (addr == null) { 091 try { 092 addr = InetAddresses.forUriString(specifier); 093 } catch (IllegalArgumentException e) { 094 // It is not a bracketed IPv6 specifier 095 } 096 } 097 098 if (addr != null) { 099 return new HostSpecifier(InetAddresses.toUriString(addr)); 100 } 101 102 // It is not any kind of IP address; must be a domain name or invalid. 103 104 final InternetDomainName domain = InternetDomainName.from(specifier); 105 106 if (domain.isUnderPublicSuffix()) { 107 return new HostSpecifier(domain.name()); 108 } 109 110 throw new IllegalArgumentException( 111 "Domain name not under a recognized TLD: " + specifier); 112 } 113 114 /** 115 * Attempts to return a {@code HostSpecifier} for the given string, throwing 116 * an exception if parsing fails. Always use this method in preference to 117 * {@link #fromValid(String)} for a specifier that is not already known to be 118 * valid. 119 * 120 * @throws ParseException if the specifier is not valid. 121 */ 122 public static HostSpecifier from(String specifier) 123 throws ParseException { 124 try { 125 return fromValid(specifier); 126 } catch (IllegalArgumentException e) { 127 // Since the IAE can originate at several different points inside 128 // fromValid(), we implement this method in terms of that one rather 129 // than the reverse. 130 131 throw new ParseException("Invalid host specifier: " + specifier, 0); 132 } 133 } 134 135 /** 136 * Determines whether {@code specifier} represents a valid 137 * {@link HostSpecifier} as described in the documentation for 138 * {@link #fromValid(String)}. 139 */ 140 public static boolean isValid(String specifier) { 141 try { 142 fromValid(specifier); 143 return true; 144 } catch (IllegalArgumentException e) { 145 return false; 146 } 147 } 148 149 @Override 150 public boolean equals(@Nullable Object other) { 151 if (this == other) { 152 return true; 153 } 154 155 if (other instanceof HostSpecifier) { 156 final HostSpecifier that = (HostSpecifier) other; 157 return this.canonicalForm.equals(that.canonicalForm); 158 } 159 160 return false; 161 } 162 163 @Override 164 public int hashCode() { 165 return canonicalForm.hashCode(); 166 } 167 168 /** 169 * Returns a string representation of the host specifier suitable for 170 * inclusion in a URI. If the host specifier is a domain name, the 171 * string will be normalized to all lower case. If the specifier was 172 * an IPv6 address without brackets, brackets are added so that the 173 * result will be usable in the host part of a URI. 174 */ 175 @Override 176 public String toString() { 177 return canonicalForm; 178 } 179 }