View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.hc.core5.http;
29  
30  import java.io.Serializable;
31  import java.net.InetAddress;
32  import java.net.URI;
33  import java.net.URISyntaxException;
34  import java.util.Locale;
35  
36  import org.apache.hc.core5.annotation.Contract;
37  import org.apache.hc.core5.annotation.ThreadingBehavior;
38  import org.apache.hc.core5.net.NamedEndpoint;
39  import org.apache.hc.core5.net.Ports;
40  import org.apache.hc.core5.net.URIAuthority;
41  import org.apache.hc.core5.util.Args;
42  import org.apache.hc.core5.util.LangUtils;
43  import org.apache.hc.core5.util.TextUtils;
44  
45  /**
46   * Component that holds all details needed to describe an HTTP connection
47   * to a host. This includes remote host name, port and protocol scheme.
48   *
49   * @see org.apache.hc.core5.net.Host
50   *
51   * @since 4.0
52   * @since 5.0 For constructors that take a scheme as an argument, that argument is now the first one.
53   */
54  @Contract(threading = ThreadingBehavior.IMMUTABLE)
55  public final class HttpHost implements NamedEndpoint, Serializable {
56  
57      private static final long serialVersionUID = -7529410654042457626L;
58  
59      /** The default scheme is "http". */
60      public static final URIScheme DEFAULT_SCHEME = URIScheme.HTTP;
61  
62      /** The host to use. */
63      private final String hostname;
64  
65      /** The lowercase host, for {@link #equals} and {@link #hashCode}. */
66      private final String lcHostname;
67  
68      /** The port to use, defaults to -1 if not set. */
69      private final int port;
70  
71      /** The scheme (lowercased) */
72      private final String schemeName;
73  
74      private final InetAddress address;
75  
76      /**
77       * Creates a new {@link HttpHost HttpHost}, specifying all values.
78       * Constructor for HttpHost.
79       * @param scheme    the name of the scheme.
80       *                  {@code null} indicates the
81       *                  {@link #DEFAULT_SCHEME default scheme}
82       * @param address   the inet address. Can be {@code null}
83       * @param hostname   the hostname (IP or DNS name)
84       * @param port      the port number.
85       *                  {@code -1} indicates the scheme default port.
86       *
87       * @throws IllegalArgumentException
88       *             If the port parameter is outside the specified range of valid port values, which is between 0 and
89       *             65535, inclusive. {@code -1} indicates the scheme default port.
90       *
91       * @since 5.0
92       */
93      public HttpHost(final String scheme, final InetAddress address, final String hostname, final int port) {
94          this.hostname   = Args.containsNoBlanks(hostname, "Host name");
95          this.port = Ports.checkWithDefault(port);
96          this.lcHostname = hostname.toLowerCase(Locale.ROOT);
97          if (scheme != null) {
98              this.schemeName = scheme.toLowerCase(Locale.ROOT);
99          } else {
100             this.schemeName = DEFAULT_SCHEME.id;
101         }
102         this.address = address;
103     }
104 
105     /**
106      * Creates {@code HttpHost} instance with the given scheme, hostname and port.
107      * @param scheme    the name of the scheme.
108      *                  {@code null} indicates the
109      *                  {@link #DEFAULT_SCHEME default scheme}
110      * @param hostname  the hostname (IP or DNS name)
111      * @param port      the port number.
112      *                  {@code -1} indicates the scheme default port.
113      *
114      * @throws IllegalArgumentException
115      *             If the port parameter is outside the specified range of valid port values, which is between 0 and
116      *             65535, inclusive. {@code -1} indicates the scheme default port.
117      */
118     public HttpHost(final String scheme, final String hostname, final int port) {
119         this(scheme, null, hostname, port);
120     }
121 
122     /**
123      * Creates {@code HttpHost} instance with the default scheme and the given hostname and port.
124      *
125      * @param hostname  the hostname (IP or DNS name)
126      * @param port      the port number.
127      *                  {@code -1} indicates the scheme default port.
128      *
129      * @throws IllegalArgumentException
130      *             If the port parameter is outside the specified range of valid port values, which is between 0 and
131      *             65535, inclusive. {@code -1} indicates the scheme default port.
132      */
133     public HttpHost(final String hostname, final int port) {
134         this(null, hostname, port);
135     }
136 
137     /**
138      * Creates {@code HttpHost} instance with the given hostname and scheme and the default port for that scheme.
139      * @param scheme    the name of the scheme.
140      *                  {@code null} indicates the
141      *                  {@link #DEFAULT_SCHEME default scheme}
142      * @param hostname  the hostname (IP or DNS name)
143      *
144      * @throws IllegalArgumentException
145      *             If the port parameter is outside the specified range of valid port values, which is between 0 and
146      *             65535, inclusive. {@code -1} indicates the scheme default port.
147      */
148     public HttpHost(final String scheme, final String hostname) {
149         this(scheme, hostname, -1);
150     }
151 
152     /**
153      * Creates {@code HttpHost} instance from a string. Text may not contain any blanks.
154      *
155      * @since 4.4
156      */
157     public static HttpHost create(final String s) throws URISyntaxException {
158         Args.notEmpty(s, "HTTP Host");
159         String text = s;
160         String scheme = null;
161         final int schemeIdx = text.indexOf("://");
162         if (schemeIdx > 0) {
163             scheme = text.substring(0, schemeIdx);
164             if (TextUtils.containsBlanks(scheme)) {
165                 throw new URISyntaxException(s, "scheme contains blanks");
166             }
167             text = text.substring(schemeIdx + 3);
168         }
169         int port = -1;
170         final int portIdx = text.lastIndexOf(":");
171         if (portIdx > 0) {
172             try {
173                 port = Integer.parseInt(text.substring(portIdx + 1));
174             } catch (final NumberFormatException ex) {
175                 throw new URISyntaxException(s, "invalid port");
176             }
177             text = text.substring(0, portIdx);
178         }
179         if (TextUtils.containsBlanks(text)) {
180             throw new URISyntaxException(s, "hostname contains blanks");
181         }
182         return new HttpHost(scheme, null, text, port);
183     }
184 
185     /**
186      * Creates an {@code HttpHost} instance from the scheme, host, and port from the given URI. Other URI elements are ignored.
187      *
188      * @param uri scheme, host, and port.
189      * @return a new HttpHost
190      *
191      * @since 5.0
192      */
193     public static HttpHost create(final URI uri) {
194         final String scheme = uri.getScheme();
195         return new HttpHost(scheme != null ? scheme : URIScheme.HTTP.getId(), uri.getHost(), uri.getPort());
196     }
197 
198     /**
199      * Creates {@code HttpHost} instance with the default scheme and port and the given hostname.
200      *
201      * @param hostname  the hostname (IP or DNS name)
202      *
203      * @throws IllegalArgumentException
204      *             If the port parameter is outside the specified range of valid port values, which is between 0 and
205      *             65535, inclusive. {@code -1} indicates the scheme default port.
206      */
207     public HttpHost(final String hostname) {
208         this(null, hostname, -1);
209     }
210 
211     /**
212      * Creates {@code HttpHost} instance with the given scheme, inet address and port.
213      * @param scheme    the name of the scheme.
214      *                  {@code null} indicates the
215      *                  {@link #DEFAULT_SCHEME default scheme}
216      * @param address   the inet address.
217      * @param port      the port number.
218      *                  {@code -1} indicates the scheme default port.
219      *
220      * @throws IllegalArgumentException
221      *             If the port parameter is outside the specified range of valid port values, which is between 0 and
222      *             65535, inclusive. {@code -1} indicates the scheme default port.
223      *
224      * @since 5.0
225      */
226     public HttpHost(final String scheme, final InetAddress address, final int port) {
227         this(scheme, Args.notNull(address,"Inet address"), address.getHostName(), port);
228     }
229 
230     /**
231      * Creates {@code HttpHost} instance with the default scheme and the given inet address
232      * and port.
233      *
234      * @param address   the inet address.
235      * @param port      the port number.
236      *                  {@code -1} indicates the scheme default port.
237      *
238      * @throws IllegalArgumentException
239      *             If the port parameter is outside the specified range of valid port values, which is between 0 and
240      *             65535, inclusive. {@code -1} indicates the scheme default port.
241      *
242      * @since 4.3
243      */
244     public HttpHost(final InetAddress address, final int port) {
245         this(null, address, port);
246     }
247 
248     /**
249      * Creates {@code HttpHost} instance with the default scheme and port and the given inet
250      * address.
251      *
252      * @param address   the inet address.
253      *
254      * @throws IllegalArgumentException
255      *             If the port parameter is outside the specified range of valid port values, which is between 0 and
256      *             65535, inclusive. {@code -1} indicates the scheme default port.
257      *
258      * @since 4.3
259      */
260     public HttpHost(final InetAddress address) {
261         this(null, address, -1);
262     }
263 
264     /**
265      * @throws IllegalArgumentException
266      *             If the port parameter is outside the specified range of valid port values, which is between 0 and
267      *             65535, inclusive. {@code -1} indicates the scheme default port.
268      *
269      * @since 5.0
270      */
271     public HttpHost(final String scheme, final NamedEndpoint namedEndpoint) {
272         this(scheme, Args.notNull(namedEndpoint, "Named endpoint").getHostName(), namedEndpoint.getPort());
273     }
274 
275     /**
276      * @throws IllegalArgumentException
277      *             If the port parameter is outside the specified range of valid port values, which is between 0 and
278      *             65535, inclusive. {@code -1} indicates the scheme default port.
279      *
280      * @since 5.0
281      */
282     public HttpHost(final URIAuthority authority) {
283         this(null, authority);
284     }
285 
286     /**
287      * Returns the host name.
288      *
289      * @return the host name (IP or DNS name)
290      */
291     @Override
292     public String getHostName() {
293         return this.hostname;
294     }
295 
296     /**
297      * Returns the port.
298      *
299      * @return the host port, or {@code -1} if not set
300      */
301     @Override
302     public int getPort() {
303         return this.port;
304     }
305 
306     /**
307      * Returns the scheme name.
308      *
309      * @return the scheme name
310      */
311     public String getSchemeName() {
312         return this.schemeName;
313     }
314 
315     /**
316      * Returns the inet address if explicitly set by a constructor,
317      *   {@code null} otherwise.
318      * @return the inet address
319      *
320      * @since 4.3
321      */
322     public InetAddress getAddress() {
323         return this.address;
324     }
325 
326     /**
327      * Return the host URI, as a string.
328      *
329      * @return the host URI
330      */
331     public String toURI() {
332         final StringBuilder buffer = new StringBuilder();
333         buffer.append(this.schemeName);
334         buffer.append("://");
335         buffer.append(this.hostname);
336         if (this.port != -1) {
337             buffer.append(':');
338             buffer.append(Integer.toString(this.port));
339         }
340         return buffer.toString();
341     }
342 
343 
344     /**
345      * Obtains the host string, without scheme prefix.
346      *
347      * @return  the host string, for example {@code localhost:8080}
348      */
349     public String toHostString() {
350         if (this.port != -1) {
351             //the highest port number is 65535, which is length 6 with the addition of the colon
352             final StringBuilder buffer = new StringBuilder(this.hostname.length() + 6);
353             buffer.append(this.hostname);
354             buffer.append(":");
355             buffer.append(Integer.toString(this.port));
356             return buffer.toString();
357         }
358         return this.hostname;
359     }
360 
361 
362     @Override
363     public String toString() {
364         return toURI();
365     }
366 
367 
368     @Override
369     public boolean equals(final Object obj) {
370         if (this == obj) {
371             return true;
372         }
373         if (obj instanceof HttpHost) {
374             final HttpHost../../../../../org/apache/hc/core5/http/HttpHost.html#HttpHost">HttpHost that = (HttpHost) obj;
375             return this.lcHostname.equals(that.lcHostname)
376                 && this.port == that.port
377                 && this.schemeName.equals(that.schemeName)
378                 && LangUtils.equals(this.address, that.address);
379         }
380         return false;
381     }
382 
383     /**
384      * @see java.lang.Object#hashCode()
385      */
386     @Override
387     public int hashCode() {
388         int hash = LangUtils.HASH_SEED;
389         hash = LangUtils.hashCode(hash, this.lcHostname);
390         hash = LangUtils.hashCode(hash, this.port);
391         hash = LangUtils.hashCode(hash, this.schemeName);
392         hash = LangUtils.hashCode(hash, address);
393         return hash;
394     }
395 
396 }