1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package org.apache.hc.core5.net;
28
29 import java.io.Serializable;
30 import java.net.URISyntaxException;
31
32 import org.apache.hc.core5.annotation.Contract;
33 import org.apache.hc.core5.annotation.ThreadingBehavior;
34 import org.apache.hc.core5.util.Args;
35 import org.apache.hc.core5.util.LangUtils;
36 import org.apache.hc.core5.util.TextUtils;
37 import org.apache.hc.core5.util.Tokenizer;
38
39
40
41
42
43
44
45 @Contract(threading = ThreadingBehavior.IMMUTABLE)
46 public final class Host implements NamedEndpoint, Serializable {
47
48 private static final long serialVersionUID = 1L;
49 private final String name;
50 private final String lcName;
51 private final int port;
52
53 public Host(final String name, final int port) {
54 super();
55 this.name = Args.notNull(name, "Host name");
56 this.port = Ports.checkWithDefault(port);
57 this.lcName = TextUtils.toLowerCase(this.name);
58 }
59
60 static Host parse(final CharSequence s, final Tokenizer.Cursor cursor) throws URISyntaxException {
61 final Tokenizer tokenizer = Tokenizer.INSTANCE;
62 final String hostName;
63 final boolean ipv6Brackets = !cursor.atEnd() && s.charAt(cursor.getPos()) == '[';
64 if (ipv6Brackets) {
65 cursor.updatePos(cursor.getPos() + 1);
66 hostName = tokenizer.parseContent(s, cursor, URISupport.IPV6_HOST_TERMINATORS);
67 if (cursor.atEnd() || !(s.charAt(cursor.getPos()) == ']')) {
68 throw URISupport.createException(s, cursor, "Expected an IPv6 closing bracket ']'");
69 }
70 cursor.updatePos(cursor.getPos() + 1);
71 if (!InetAddressUtils.isIPv6Address(hostName)) {
72 throw URISupport.createException(s, cursor, "Expected an IPv6 address");
73 }
74 } else {
75 hostName = tokenizer.parseContent(s, cursor, URISupport.PORT_SEPARATORS);
76 }
77 String portText = null;
78 if (!cursor.atEnd() && s.charAt(cursor.getPos()) == ':') {
79 cursor.updatePos(cursor.getPos() + 1);
80 portText = tokenizer.parseContent(s, cursor, URISupport.TERMINATORS);
81 }
82 final int port;
83 if (!TextUtils.isBlank(portText)) {
84 if (!ipv6Brackets && portText.contains(":")) {
85 throw URISupport.createException(s, cursor, "Expected IPv6 address to be enclosed in brackets");
86 }
87 try {
88 port = Integer.parseInt(portText);
89 } catch (final NumberFormatException ex) {
90 throw URISupport.createException(s, cursor, "Port is invalid");
91 }
92 } else {
93 port = -1;
94 }
95 return new Host(hostName, port);
96 }
97
98 static Host parse(final CharSequence s) throws URISyntaxException {
99 final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length());
100 return parse(s, cursor);
101 }
102
103 static void format(final StringBuilder buf, final NamedEndpoint endpoint) {
104 final String hostName = endpoint.getHostName();
105 if (InetAddressUtils.isIPv6Address(hostName)) {
106 buf.append('[').append(hostName).append(']');
107 } else {
108 buf.append(hostName);
109 }
110 if (endpoint.getPort() != -1) {
111 buf.append(":");
112 buf.append(endpoint.getPort());
113 }
114 }
115
116 static void format(final StringBuilder buf, final Host host) {
117 format(buf, (NamedEndpoint) host);
118 }
119
120 static String format(final Host host) {
121 final StringBuilder buf = new StringBuilder();
122 format(buf, host);
123 return buf.toString();
124 }
125
126 public static Host create(final String s) throws URISyntaxException {
127 Args.notEmpty(s, "HTTP Host");
128 final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length());
129 final Host host = parse(s, cursor);
130 if (TextUtils.isBlank(host.getHostName())) {
131 throw URISupport.createException(s, cursor, "Hostname is invalid");
132 }
133 if (!cursor.atEnd()) {
134 throw URISupport.createException(s, cursor, "Unexpected content");
135 }
136 return host;
137 }
138
139 @Override
140 public String getHostName() {
141 return name;
142 }
143
144 @Override
145 public int getPort() {
146 return port;
147 }
148
149 @Override
150 public boolean equals(final Object o) {
151 if (this == o) {
152 return true;
153 }
154 if (o instanceof Host) {
155 final Host that = (Host) o;
156 return this.lcName.equals(that.lcName) && this.port == that.port;
157 }
158 return false;
159 }
160
161 @Override
162 public int hashCode() {
163 int hash = LangUtils.HASH_SEED;
164 hash = LangUtils.hashCode(hash, this.lcName);
165 hash = LangUtils.hashCode(hash, this.port);
166 return hash;
167 }
168
169 @Override
170 public String toString() {
171 return format(this);
172 }
173
174 }