• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 The Guava Authors
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.google.common.net;
18 
19 import com.google.common.annotations.Beta;
20 import com.google.common.base.Preconditions;
21 
22 import java.net.InetAddress;
23 import java.text.ParseException;
24 
25 import javax.annotation.Nullable;
26 
27 /**
28  * A syntactically valid host specifier, suitable for use in a URI.
29  * This may be either a numeric IP address in IPv4 or IPv6 notation, or a
30  * domain name.
31  *
32  * <p>Because this class is intended to represent host specifiers which can
33  * reasonably be used in a URI, the domain name case is further restricted to
34  * include only those domain names which end in a recognized public suffix; see
35  * {@link InternetDomainName#isPublicSuffix()} for details.
36  *
37  * <p>Note that no network lookups are performed by any {@code HostSpecifier}
38  * methods.  No attempt is made to verify that a provided specifier corresponds
39  * to a real or accessible host.  Only syntactic and pattern-based checks are
40  * performed.
41  *
42  * <p>If you know that a given string represents a numeric IP address, use
43  * {@link InetAddresses} to obtain and manipulate a
44  * {@link java.net.InetAddress} instance from it rather than using this class.
45  * Similarly, if you know that a given string represents a domain name, use
46  * {@link InternetDomainName} rather than this class.
47  *
48  * @author Craig Berry
49  * @since 5.0
50  */
51 @Beta
52 public final class HostSpecifier {
53 
54   private final String canonicalForm;
55 
HostSpecifier(String canonicalForm)56   private HostSpecifier(String canonicalForm) {
57     this.canonicalForm = canonicalForm;
58   }
59 
60   /**
61    * Returns a {@code HostSpecifier} built from the provided {@code specifier},
62    * which is already known to be valid.  If the {@code specifier} might be
63    * invalid, use {@link #from(String)} instead.
64    *
65    * <p>The specifier must be in one of these formats:
66    * <ul>
67    * <li>A domain name, like {@code google.com}
68    * <li>A IPv4 address string, like {@code 127.0.0.1}
69    * <li>An IPv6 address string with or without brackets, like
70    *     {@code [2001:db8::1]} or {@code 2001:db8::1}
71    * </ul>
72    *
73    * @throws IllegalArgumentException if the specifier is not valid.
74    */
fromValid(String specifier)75   public static HostSpecifier fromValid(String specifier) {
76     // Verify that no port was specified, and strip optional brackets from
77     // IPv6 literals.
78     final HostAndPort parsedHost = HostAndPort.fromString(specifier);
79     Preconditions.checkArgument(!parsedHost.hasPort());
80     final String host = parsedHost.getHostText();
81 
82     // Try to interpret the specifier as an IP address.  Note we build
83     // the address rather than using the .is* methods because we want to
84     // use InetAddresses.toUriString to convert the result to a string in
85     // canonical form.
86     InetAddress addr = null;
87     try {
88       addr = InetAddresses.forString(host);
89     } catch (IllegalArgumentException e) {
90       // It is not an IPv4 or IPv6 literal
91     }
92 
93     if (addr != null) {
94       return new HostSpecifier(InetAddresses.toUriString(addr));
95     }
96 
97     // It is not any kind of IP address; must be a domain name or invalid.
98 
99     // TODO(user): different versions of this for different factories?
100     final InternetDomainName domain = InternetDomainName.from(host);
101 
102     if (domain.hasPublicSuffix()) {
103       return new HostSpecifier(domain.toString());
104     }
105 
106     throw new IllegalArgumentException(
107         "Domain name does not have a recognized public suffix: " + host);
108   }
109 
110   /**
111    * Attempts to return a {@code HostSpecifier} for the given string, throwing
112    * an exception if parsing fails. Always use this method in preference to
113    * {@link #fromValid(String)} for a specifier that is not already known to be
114    * valid.
115    *
116    * @throws ParseException if the specifier is not valid.
117    */
from(String specifier)118   public static HostSpecifier from(String specifier)
119       throws ParseException {
120     try {
121       return fromValid(specifier);
122     } catch (IllegalArgumentException e) {
123       // Since the IAE can originate at several different points inside
124       // fromValid(), we implement this method in terms of that one rather
125       // than the reverse.
126 
127       ParseException parseException =
128           new ParseException("Invalid host specifier: " + specifier, 0);
129       parseException.initCause(e);
130       throw parseException;
131     }
132   }
133 
134   /**
135    * Determines whether {@code specifier} represents a valid
136    * {@link HostSpecifier} as described in the documentation for
137    * {@link #fromValid(String)}.
138    */
isValid(String specifier)139   public static boolean isValid(String specifier) {
140     try {
141       fromValid(specifier);
142       return true;
143     } catch (IllegalArgumentException e) {
144       return false;
145     }
146   }
147 
148   @Override
equals(@ullable Object other)149   public boolean equals(@Nullable Object other) {
150     if (this == other) {
151       return true;
152     }
153 
154     if (other instanceof HostSpecifier) {
155       final HostSpecifier that = (HostSpecifier) other;
156       return this.canonicalForm.equals(that.canonicalForm);
157     }
158 
159     return false;
160   }
161 
162   @Override
hashCode()163   public int hashCode() {
164     return canonicalForm.hashCode();
165   }
166 
167   /**
168    * Returns a string representation of the host specifier suitable for
169    * inclusion in a URI.  If the host specifier is a domain name, the
170    * string will be normalized to all lower case.  If the specifier was
171    * an IPv6 address without brackets, brackets are added so that the
172    * result will be usable in the host part of a URI.
173    */
174   @Override
toString()175   public String toString() {
176     return canonicalForm;
177   }
178 }
179