001/*
002 * ====================================================================
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *   http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing,
014 * software distributed under the License is distributed on an
015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 * KIND, either express or implied.  See the License for the
017 * specific language governing permissions and limitations
018 * under the License.
019 * ====================================================================
020 *
021 * This software consists of voluntary contributions made by many
022 * individuals on behalf of the Apache Software Foundation.  For more
023 * information on the Apache Software Foundation, please see
024 * <http://www.apache.org/>.
025 *
026 */
027
028package org.apache.http;
029
030import java.io.Serializable;
031import java.net.InetAddress;
032import java.util.Locale;
033
034import org.apache.http.annotation.ThreadingBehavior;
035import org.apache.http.annotation.Contract;
036import org.apache.http.util.Args;
037import org.apache.http.util.LangUtils;
038
039/**
040 * Holds all of the variables needed to describe an HTTP connection to a host.
041 * This includes remote host name, port and scheme.
042 *
043 * @since 4.0
044 */
045@Contract(threading = ThreadingBehavior.IMMUTABLE)
046public final class HttpHost implements Cloneable, Serializable {
047
048    private static final long serialVersionUID = -7529410654042457626L;
049
050    /** The default scheme is "http". */
051    public static final String DEFAULT_SCHEME_NAME = "http";
052
053    /** The host to use. */
054    protected final String hostname;
055
056    /** The lowercase host, for {@link #equals} and {@link #hashCode}. */
057    protected final String lcHostname;
058
059
060    /** The port to use, defaults to -1 if not set. */
061    protected final int port;
062
063    /** The scheme (lowercased) */
064    protected final String schemeName;
065
066    protected final InetAddress address;
067
068    /**
069     * Creates {@code HttpHost} instance with the given scheme, hostname and port.
070     *
071     * @param hostname  the hostname (IP or DNS name)
072     * @param port      the port number.
073     *                  {@code -1} indicates the scheme default port.
074     * @param scheme    the name of the scheme.
075     *                  {@code null} indicates the
076     *                  {@link #DEFAULT_SCHEME_NAME default scheme}
077     */
078    public HttpHost(final String hostname, final int port, final String scheme) {
079        super();
080        this.hostname   = Args.containsNoBlanks(hostname, "Host name");
081        this.lcHostname = hostname.toLowerCase(Locale.ROOT);
082        if (scheme != null) {
083            this.schemeName = scheme.toLowerCase(Locale.ROOT);
084        } else {
085            this.schemeName = DEFAULT_SCHEME_NAME;
086        }
087        this.port = port;
088        this.address = null;
089    }
090
091    /**
092     * Creates {@code HttpHost} instance with the default scheme and the given hostname and port.
093     *
094     * @param hostname  the hostname (IP or DNS name)
095     * @param port      the port number.
096     *                  {@code -1} indicates the scheme default port.
097     */
098    public HttpHost(final String hostname, final int port) {
099        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        } else {
291            return this.hostname;
292        }
293    }
294
295
296    @Override
297    public String toString() {
298        return toURI();
299    }
300
301
302    @Override
303    public boolean equals(final Object obj) {
304        if (this == obj) {
305            return true;
306        }
307        if (obj instanceof HttpHost) {
308            final HttpHost that = (HttpHost) obj;
309            return this.lcHostname.equals(that.lcHostname)
310                && this.port == that.port
311                && this.schemeName.equals(that.schemeName)
312                && (this.address==null ? that.address== null : this.address.equals(that.address));
313        } else {
314            return false;
315        }
316    }
317
318    /**
319     * @see java.lang.Object#hashCode()
320     */
321    @Override
322    public int hashCode() {
323        int hash = LangUtils.HASH_SEED;
324        hash = LangUtils.hashCode(hash, this.lcHostname);
325        hash = LangUtils.hashCode(hash, this.port);
326        hash = LangUtils.hashCode(hash, this.schemeName);
327        if (address!=null) {
328            hash = LangUtils.hashCode(hash, address);
329        }
330        return hash;
331    }
332
333    @Override
334    public Object clone() throws CloneNotSupportedException {
335        return super.clone();
336    }
337
338}