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.http;
29
30 import java.io.Serializable;
31 import java.net.InetAddress;
32 import java.util.Locale;
33
34 import org.apache.http.annotation.ThreadingBehavior;
35 import org.apache.http.annotation.Contract;
36 import org.apache.http.util.Args;
37 import org.apache.http.util.LangUtils;
38
39 /**
40 * Holds all of the variables needed to describe an HTTP connection to a host.
41 * This includes remote host name, port and scheme.
42 *
43 * @since 4.0
44 */
45 @Contract(threading = ThreadingBehavior.IMMUTABLE)
46 public final class HttpHost implements Cloneable, Serializable {
47
48 private static final long serialVersionUID = -7529410654042457626L;
49
50 /** The default scheme is "http". */
51 public static final String DEFAULT_SCHEME_NAME = "http";
52
53 /** The host to use. */
54 protected final String hostname;
55
56 /** The lowercase host, for {@link #equals} and {@link #hashCode}. */
57 protected final String lcHostname;
58
59
60 /** The port to use, defaults to -1 if not set. */
61 protected final int port;
62
63 /** The scheme (lowercased) */
64 protected final String schemeName;
65
66 protected final InetAddress address;
67
68 /**
69 * Creates {@code HttpHost} instance with the given scheme, hostname and port.
70 *
71 * @param hostname the hostname (IP or DNS name)
72 * @param port the port number.
73 * {@code -1} indicates the scheme default port.
74 * @param scheme the name of the scheme.
75 * {@code null} indicates the
76 * {@link #DEFAULT_SCHEME_NAME default scheme}
77 */
78 public HttpHost(final String hostname, final int port, final String scheme) {
79 super();
80 this.hostname = Args.containsNoBlanks(hostname, "Host name");
81 this.lcHostname = hostname.toLowerCase(Locale.ROOT);
82 if (scheme != null) {
83 this.schemeName = scheme.toLowerCase(Locale.ROOT);
84 } else {
85 this.schemeName = DEFAULT_SCHEME_NAME;
86 }
87 this.port = port;
88 this.address = null;
89 }
90
91 /**
92 * Creates {@code HttpHost} instance with the default scheme and the given hostname and port.
93 *
94 * @param hostname the hostname (IP or DNS name)
95 * @param port the port number.
96 * {@code -1} indicates the scheme default port.
97 */
98 public HttpHost(final String hostname, final int port) {
99 this(hostname, port, null);
100 }
101
102 /**
103 * Creates {@code HttpHost} instance from string. Text may not contain any blanks.
104 *
105 * @since 4.4
106 */
107 public static HttpHost create(final String s) {
108 Args.containsNoBlanks(s, "HTTP Host");
109 String text = s;
110 String scheme = null;
111 final int schemeIdx = text.indexOf("://");
112 if (schemeIdx > 0) {
113 scheme = text.substring(0, schemeIdx);
114 text = text.substring(schemeIdx + 3);
115 }
116 int port = -1;
117 final int portIdx = text.lastIndexOf(":");
118 if (portIdx > 0) {
119 try {
120 port = Integer.parseInt(text.substring(portIdx + 1));
121 } catch (final NumberFormatException ex) {
122 throw new IllegalArgumentException("Invalid HTTP host: " + text);
123 }
124 text = text.substring(0, portIdx);
125 }
126 return new HttpHost(text, port, scheme);
127 }
128
129 /**
130 * Creates {@code HttpHost} instance with the default scheme and port and the given hostname.
131 *
132 * @param hostname the hostname (IP or DNS name)
133 */
134 public HttpHost(final String hostname) {
135 this(hostname, -1, null);
136 }
137
138 /**
139 * Creates {@code HttpHost} instance with the given scheme, inet address and port.
140 *
141 * @param address the inet address.
142 * @param port the port number.
143 * {@code -1} indicates the scheme default port.
144 * @param scheme the name of the scheme.
145 * {@code null} indicates the
146 * {@link #DEFAULT_SCHEME_NAME default scheme}
147 *
148 * @since 4.3
149 */
150 public HttpHost(final InetAddress address, final int port, final String scheme) {
151 this(Args.notNull(address,"Inet address"), address.getHostName(), port, scheme);
152 }
153 /**
154 * Creates a new {@link HttpHost HttpHost}, specifying all values.
155 * Constructor for HttpHost.
156 *
157 * @param address the inet address.
158 * @param hostname the hostname (IP or DNS name)
159 * @param port the port number.
160 * {@code -1} indicates the scheme default port.
161 * @param scheme the name of the scheme.
162 * {@code null} indicates the
163 * {@link #DEFAULT_SCHEME_NAME default scheme}
164 *
165 * @since 4.4
166 */
167 public HttpHost(final InetAddress address, final String hostname, final int port, final String scheme) {
168 super();
169 this.address = Args.notNull(address, "Inet address");
170 this.hostname = Args.notNull(hostname, "Hostname");
171 this.lcHostname = this.hostname.toLowerCase(Locale.ROOT);
172 if (scheme != null) {
173 this.schemeName = scheme.toLowerCase(Locale.ROOT);
174 } else {
175 this.schemeName = DEFAULT_SCHEME_NAME;
176 }
177 this.port = port;
178 }
179
180 /**
181 * Creates {@code HttpHost} instance with the default scheme and the given inet address
182 * and port.
183 *
184 * @param address the inet address.
185 * @param port the port number.
186 * {@code -1} indicates the scheme default port.
187 *
188 * @since 4.3
189 */
190 public HttpHost(final InetAddress address, final int port) {
191 this(address, port, null);
192 }
193
194 /**
195 * Creates {@code HttpHost} instance with the default scheme and port and the given inet
196 * address.
197 *
198 * @param address the inet address.
199 *
200 * @since 4.3
201 */
202 public HttpHost(final InetAddress address) {
203 this(address, -1, null);
204 }
205
206 /**
207 * Copy constructor for {@link HttpHost HttpHost}.
208 *
209 * @param httphost the HTTP host to copy details from
210 */
211 public HttpHost (final HttpHost httphost) {
212 super();
213 Args.notNull(httphost, "HTTP host");
214 this.hostname = httphost.hostname;
215 this.lcHostname = httphost.lcHostname;
216 this.schemeName = httphost.schemeName;
217 this.port = httphost.port;
218 this.address = httphost.address;
219 }
220
221 /**
222 * Returns the host name.
223 *
224 * @return the host name (IP or DNS name)
225 */
226 public String getHostName() {
227 return this.hostname;
228 }
229
230 /**
231 * Returns the port.
232 *
233 * @return the host port, or {@code -1} if not set
234 */
235 public int getPort() {
236 return this.port;
237 }
238
239 /**
240 * Returns the scheme name.
241 *
242 * @return the scheme name
243 */
244 public String getSchemeName() {
245 return this.schemeName;
246 }
247
248 /**
249 * Returns the inet address if explicitly set by a constructor,
250 * {@code null} otherwise.
251 * @return the inet address
252 *
253 * @since 4.3
254 */
255 public InetAddress getAddress() {
256 return this.address;
257 }
258
259 /**
260 * Return the host URI, as a string.
261 *
262 * @return the host URI
263 */
264 public String toURI() {
265 final StringBuilder buffer = new StringBuilder();
266 buffer.append(this.schemeName);
267 buffer.append("://");
268 buffer.append(this.hostname);
269 if (this.port != -1) {
270 buffer.append(':');
271 buffer.append(Integer.toString(this.port));
272 }
273 return buffer.toString();
274 }
275
276
277 /**
278 * Obtains the host string, without scheme prefix.
279 *
280 * @return the host string, for example {@code localhost:8080}
281 */
282 public String toHostString() {
283 if (this.port != -1) {
284 //the highest port number is 65535, which is length 6 with the addition of the colon
285 final StringBuilder buffer = new StringBuilder(this.hostname.length() + 6);
286 buffer.append(this.hostname);
287 buffer.append(":");
288 buffer.append(Integer.toString(this.port));
289 return buffer.toString();
290 }
291 return this.hostname;
292 }
293
294
295 @Override
296 public String toString() {
297 return toURI();
298 }
299
300
301 @Override
302 public boolean equals(final Object obj) {
303 if (this == obj) {
304 return true;
305 }
306 if (obj instanceof HttpHost) {
307 final HttpHost that = (HttpHost) obj;
308 return this.lcHostname.equals(that.lcHostname)
309 && this.port == that.port
310 && this.schemeName.equals(that.schemeName)
311 && (this.address==null ? that.address== null : this.address.equals(that.address));
312 }
313 return false;
314 }
315
316 /**
317 * @see java.lang.Object#hashCode()
318 */
319 @Override
320 public int hashCode() {
321 int hash = LangUtils.HASH_SEED;
322 hash = LangUtils.hashCode(hash, this.lcHostname);
323 hash = LangUtils.hashCode(hash, this.port);
324 hash = LangUtils.hashCode(hash, this.schemeName);
325 if (address!=null) {
326 hash = LangUtils.hashCode(hash, address);
327 }
328 return hash;
329 }
330
331 @Override
332 public Object clone() throws CloneNotSupportedException {
333 return super.clone();
334 }
335
336 }
337