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       * @throws IllegalArgumentException
78       *             If the port parameter is outside the specified range of valid port values, which is between 0 and
79       *             65535, inclusive. {@code -1} indicates the scheme default port.
80       */
81      private HttpHost(final String scheme, final String hostname, final int port, final boolean internal) {
82          super();
83          this.hostname = hostname;
84          this.lcHostname = hostname;
85          this.schemeName = scheme;
86          this.port = Ports.checkWithDefault(port);
87          this.address = null;
88      }
89  
90      /**
91       * Creates {@code HttpHost} instance with the given scheme, hostname and port.
92       * @param scheme    the name of the scheme.
93       *                  {@code null} indicates the
94       *                  {@link #DEFAULT_SCHEME default scheme}
95       * @param hostname  the hostname (IP or DNS name)
96       * @param port      the port number.
97       *                  {@code -1} indicates the scheme default port.
98       *
99       * @throws IllegalArgumentException
100      *             If the port parameter is outside the specified range of valid port values, which is between 0 and
101      *             65535, inclusive. {@code -1} indicates the scheme default port.
102      */
103     public HttpHost(final String scheme, final String hostname, final int port) {
104         super();
105         this.hostname   = Args.containsNoBlanks(hostname, "Host name");
106         this.lcHostname = hostname.toLowerCase(Locale.ROOT);
107         if (scheme != null) {
108             this.schemeName = scheme.toLowerCase(Locale.ROOT);
109         } else {
110             this.schemeName = DEFAULT_SCHEME.id;
111         }
112         this.port = Ports.checkWithDefault(port);
113         this.address = null;
114     }
115 
116     /**
117      * Creates {@code HttpHost} instance with the given scheme, hostname and port.
118      * @param scheme    the name of the scheme.
119      *                  {@code null} indicates the
120      *                  {@link #DEFAULT_SCHEME default scheme}
121      * @param hostname  the hostname (IP or DNS name)
122      * @param port      the port number.
123      *                  {@code -1} indicates the scheme default port.
124      *
125      * @throws IllegalArgumentException
126      *             If the port parameter is outside the specified range of valid port values, which is between 0 and
127      *             65535, inclusive. {@code -1} indicates the scheme default port.
128      * @deprecated Use {@link #HttpHost(String, String, int)}.
129      */
130     @Deprecated
131     public HttpHost(final String hostname, final int port, final String scheme) {
132         this(scheme, hostname, port);
133     }
134 
135     /**
136      * Creates {@code HttpHost} instance with the default scheme and the given hostname and port.
137      *
138      * @param hostname  the hostname (IP or DNS name)
139      * @param port      the port number.
140      *                  {@code -1} indicates the scheme default port.
141      *
142      * @throws IllegalArgumentException
143      *             If the port parameter is outside the specified range of valid port values, which is between 0 and
144      *             65535, inclusive. {@code -1} indicates the scheme default port.
145      */
146     public HttpHost(final String hostname, final int port) {
147         this(null, hostname, port);
148     }
149 
150     /**
151      * Creates {@code HttpHost} instance with the given hostname and scheme and the default port for that scheme.
152      * @param scheme    the name of the scheme.
153      *                  {@code null} indicates the
154      *                  {@link #DEFAULT_SCHEME default scheme}
155      * @param hostname  the hostname (IP or DNS name)
156      *
157      * @throws IllegalArgumentException
158      *             If the port parameter is outside the specified range of valid port values, which is between 0 and
159      *             65535, inclusive. {@code -1} indicates the scheme default port.
160      */
161     public HttpHost(final String scheme, final String hostname) {
162         this(scheme, hostname, -1);
163     }
164 
165     /**
166      * Creates {@code HttpHost} instance from a string. Text may not contain any blanks.
167      *
168      * @since 4.4
169      */
170     public static HttpHost create(final String s) throws URISyntaxException {
171         Args.notEmpty(s, "HTTP Host");
172         String text = s;
173         String scheme = null;
174         final int schemeIdx = text.indexOf("://");
175         if (schemeIdx > 0) {
176             scheme = text.substring(0, schemeIdx);
177             if (TextUtils.containsBlanks(scheme)) {
178                 throw new URISyntaxException(s, "scheme contains blanks");
179             }
180             text = text.substring(schemeIdx + 3);
181         }
182         int port = -1;
183         final int portIdx = text.lastIndexOf(":");
184         if (portIdx > 0) {
185             try {
186                 port = Integer.parseInt(text.substring(portIdx + 1));
187             } catch (final NumberFormatException ex) {
188                 throw new URISyntaxException(s, "invalid port");
189             }
190             text = text.substring(0, portIdx);
191         }
192         if (TextUtils.containsBlanks(text)) {
193             throw new URISyntaxException(s, "hostname contains blanks");
194         }
195         return new HttpHost(
196                 scheme != null ? scheme.toLowerCase(Locale.ROOT) : DEFAULT_SCHEME.id,
197                 text.toLowerCase(Locale.ROOT),
198                 port, true);
199     }
200 
201     /**
202      * Creates an {@code HttpHost} instance from the scheme, host, and port from the given URI. Other URI elements are ignored.
203      *
204      * @param uri scheme, host, and port.
205      * @return a new HttpHost
206      *
207      * @since 5.0
208      */
209     public static HttpHost create(final URI uri) {
210         final String scheme = uri.getScheme();
211         return new HttpHost(scheme != null ? scheme : URIScheme.HTTP.getId(), uri.getHost(), uri.getPort());
212     }
213 
214     /**
215      * Creates {@code HttpHost} instance with the default scheme and port and the given hostname.
216      *
217      * @param hostname  the hostname (IP or DNS name)
218      *
219      * @throws IllegalArgumentException
220      *             If the port parameter is outside the specified range of valid port values, which is between 0 and
221      *             65535, inclusive. {@code -1} indicates the scheme default port.
222      */
223     public HttpHost(final String hostname) {
224         this(null, hostname, -1);
225     }
226 
227     /**
228      * Creates {@code HttpHost} instance with the given scheme, inet address and port.
229      * @param scheme    the name of the scheme.
230      *                  {@code null} indicates the
231      *                  {@link #DEFAULT_SCHEME default scheme}
232      * @param address   the inet address.
233      * @param port      the port number.
234      *                  {@code -1} indicates the scheme default port.
235      *
236      * @throws IllegalArgumentException
237      *             If the port parameter is outside the specified range of valid port values, which is between 0 and
238      *             65535, inclusive. {@code -1} indicates the scheme default port.
239      *
240      * @since 5.0
241      */
242     public HttpHost(final String scheme, final InetAddress address, final int port) {
243         this(scheme, Args.notNull(address,"Inet address"), address.getHostName(), port);
244     }
245 
246     /**
247      * Creates {@code HttpHost} instance with the given scheme, inet address and port.
248      * @param scheme    the name of the scheme.
249      *                  {@code null} indicates the
250      *                  {@link #DEFAULT_SCHEME default scheme}
251      * @param address   the inet address.
252      * @param port      the port number.
253      *                  {@code -1} indicates the scheme default port.
254      *
255      * @throws IllegalArgumentException
256      *             If the port parameter is outside the specified range of valid port values, which is between 0 and
257      *             65535, inclusive. {@code -1} indicates the scheme default port.
258      *
259      * @since 4.3
260      * @deprecated Use {@link #HttpHost(String, InetAddress, int)}
261      */
262     @Deprecated
263     public HttpHost(final InetAddress address, final int port, final String scheme) {
264         this(scheme, Args.notNull(address,"Inet address"), address.getHostName(), port);
265     }
266 
267     /**
268      * Creates a new {@link HttpHost HttpHost}, specifying all values.
269      * Constructor for HttpHost.
270      * @param scheme    the name of the scheme.
271      *                  {@code null} indicates the
272      *                  {@link #DEFAULT_SCHEME default scheme}
273      * @param address   the inet address.
274      * @param hostname   the hostname (IP or DNS name)
275      * @param port      the port number.
276      *                  {@code -1} indicates the scheme default port.
277      *
278      * @throws IllegalArgumentException
279      *             If the port parameter is outside the specified range of valid port values, which is between 0 and
280      *             65535, inclusive. {@code -1} indicates the scheme default port.
281      *
282      * @since 5.0
283      */
284     public HttpHost(final String scheme, final InetAddress address, final String hostname, final int port) {
285         super();
286         this.address = Args.notNull(address, "Inet address");
287         this.hostname = Args.notNull(hostname, "Hostname");
288         this.lcHostname = this.hostname.toLowerCase(Locale.ROOT);
289         if (scheme != null) {
290             this.schemeName = scheme.toLowerCase(Locale.ROOT);
291         } else {
292             this.schemeName = DEFAULT_SCHEME.id;
293         }
294         this.port = Ports.checkWithDefault(port);
295     }
296 
297     /**
298      * Creates a new {@link HttpHost HttpHost}, specifying all values.
299      * Constructor for HttpHost.
300      * @param scheme    the name of the scheme.
301      *                  {@code null} indicates the
302      *                  {@link #DEFAULT_SCHEME default scheme}
303      * @param address   the inet address.
304      * @param hostname   the hostname (IP or DNS name)
305      * @param port      the port number.
306      *                  {@code -1} indicates the scheme default port.
307      *
308      * @throws IllegalArgumentException
309      *             If the port parameter is outside the specified range of valid port values, which is between 0 and
310      *             65535, inclusive. {@code -1} indicates the scheme default port.
311      *
312      * @since 4.4
313      * @deprecated Use {@link #HttpHost(String, InetAddress, String, int)}
314      */
315     @Deprecated
316     public HttpHost(final InetAddress address, final String hostname, final int port, final String scheme) {
317         super();
318         this.address = Args.notNull(address, "Inet address");
319         this.hostname = Args.notNull(hostname, "Hostname");
320         this.lcHostname = this.hostname.toLowerCase(Locale.ROOT);
321         if (scheme != null) {
322             this.schemeName = scheme.toLowerCase(Locale.ROOT);
323         } else {
324             this.schemeName = DEFAULT_SCHEME.id;
325         }
326         this.port = Ports.checkWithDefault(port);
327     }
328 
329 
330     /**
331      * Creates {@code HttpHost} instance with the default scheme and the given inet address
332      * and port.
333      *
334      * @param address   the inet address.
335      * @param port      the port number.
336      *                  {@code -1} indicates the scheme default port.
337      *
338      * @throws IllegalArgumentException
339      *             If the port parameter is outside the specified range of valid port values, which is between 0 and
340      *             65535, inclusive. {@code -1} indicates the scheme default port.
341      *
342      * @since 4.3
343      */
344     public HttpHost(final InetAddress address, final int port) {
345         this(null, address, port);
346     }
347 
348     /**
349      * Creates {@code HttpHost} instance with the default scheme and port and the given inet
350      * address.
351      *
352      * @param address   the inet address.
353      *
354      * @throws IllegalArgumentException
355      *             If the port parameter is outside the specified range of valid port values, which is between 0 and
356      *             65535, inclusive. {@code -1} indicates the scheme default port.
357      *
358      * @since 4.3
359      */
360     public HttpHost(final InetAddress address) {
361         this(null, address, -1);
362     }
363 
364     /**
365      * @throws IllegalArgumentException
366      *             If the port parameter is outside the specified range of valid port values, which is between 0 and
367      *             65535, inclusive. {@code -1} indicates the scheme default port.
368      *
369      * @since 5.0
370      */
371     public HttpHost(final String scheme, final NamedEndpoint namedEndpoint) {
372         this(scheme, namedEndpoint.getHostName(), namedEndpoint.getPort());
373     }
374 
375     /**
376      * @throws IllegalArgumentException
377      *             If the port parameter is outside the specified range of valid port values, which is between 0 and
378      *             65535, inclusive. {@code -1} indicates the scheme default port.
379      *
380      * @since 5.0
381      * @deprecated Use {@link #HttpHost(String, NamedEndpoint)}
382      */
383     @Deprecated
384     public HttpHost(final NamedEndpoint namedEndpoint, final String scheme) {
385         this(scheme, namedEndpoint.getHostName(), namedEndpoint.getPort());
386     }
387 
388     /**
389      * @throws IllegalArgumentException
390      *             If the port parameter is outside the specified range of valid port values, which is between 0 and
391      *             65535, inclusive. {@code -1} indicates the scheme default port.
392      *
393      * @since 5.0
394      */
395     public HttpHost(final URIAuthority authority) {
396         this(null, authority);
397     }
398 
399     /**
400      * Returns the host name.
401      *
402      * @return the host name (IP or DNS name)
403      */
404     @Override
405     public String getHostName() {
406         return this.hostname;
407     }
408 
409     /**
410      * Returns the port.
411      *
412      * @return the host port, or {@code -1} if not set
413      */
414     @Override
415     public int getPort() {
416         return this.port;
417     }
418 
419     /**
420      * Returns the scheme name.
421      *
422      * @return the scheme name
423      */
424     public String getSchemeName() {
425         return this.schemeName;
426     }
427 
428     /**
429      * Returns the inet address if explicitly set by a constructor,
430      *   {@code null} otherwise.
431      * @return the inet address
432      *
433      * @since 4.3
434      */
435     public InetAddress getAddress() {
436         return this.address;
437     }
438 
439     /**
440      * Return the host URI, as a string.
441      *
442      * @return the host URI
443      */
444     public String toURI() {
445         final StringBuilder buffer = new StringBuilder();
446         buffer.append(this.schemeName);
447         buffer.append("://");
448         buffer.append(this.hostname);
449         if (this.port != -1) {
450             buffer.append(':');
451             buffer.append(Integer.toString(this.port));
452         }
453         return buffer.toString();
454     }
455 
456 
457     /**
458      * Obtains the host string, without scheme prefix.
459      *
460      * @return  the host string, for example {@code localhost:8080}
461      */
462     public String toHostString() {
463         if (this.port != -1) {
464             //the highest port number is 65535, which is length 6 with the addition of the colon
465             final StringBuilder buffer = new StringBuilder(this.hostname.length() + 6);
466             buffer.append(this.hostname);
467             buffer.append(":");
468             buffer.append(Integer.toString(this.port));
469             return buffer.toString();
470         }
471         return this.hostname;
472     }
473 
474 
475     @Override
476     public String toString() {
477         return toURI();
478     }
479 
480 
481     @Override
482     public boolean equals(final Object obj) {
483         if (this == obj) {
484             return true;
485         }
486         if (obj instanceof HttpHost) {
487             final HttpHost../../../../../org/apache/hc/core5/http/HttpHost.html#HttpHost">HttpHost that = (HttpHost) obj;
488             return this.lcHostname.equals(that.lcHostname)
489                 && this.port == that.port
490                 && this.schemeName.equals(that.schemeName)
491                 && LangUtils.equals(this.address, that.address);
492         }
493         return false;
494     }
495 
496     /**
497      * @see java.lang.Object#hashCode()
498      */
499     @Override
500     public int hashCode() {
501         int hash = LangUtils.HASH_SEED;
502         hash = LangUtils.hashCode(hash, this.lcHostname);
503         hash = LangUtils.hashCode(hash, this.port);
504         hash = LangUtils.hashCode(hash, this.schemeName);
505         hash = LangUtils.hashCode(hash, address);
506         return hash;
507     }
508 
509 }