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 }