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
28 package org.apache.hc.client5.http;
29
30 import java.net.InetAddress;
31 import java.net.InetSocketAddress;
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.Collections;
35 import java.util.List;
36
37 import org.apache.hc.core5.annotation.Contract;
38 import org.apache.hc.core5.annotation.ThreadingBehavior;
39 import org.apache.hc.core5.http.HttpHost;
40 import org.apache.hc.core5.util.Args;
41 import org.apache.hc.core5.util.LangUtils;
42
43
44
45
46
47
48 @Contract(threading = ThreadingBehavior.IMMUTABLE)
49 public final class HttpRoute implements RouteInfo, Cloneable {
50
51
52 private final HttpHost targetHost;
53
54
55
56
57
58 private final InetAddress localAddress;
59
60
61 private final List<HttpHost> proxyChain;
62
63
64 private final TunnelType tunnelled;
65
66
67 private final LayerType layered;
68
69
70 private final boolean secure;
71
72 private HttpRoute(final HttpHost targetHost, final InetAddress local, final List<HttpHost> proxies,
73 final boolean secure, final TunnelType tunnelled, final LayerType layered) {
74 Args.notNull(targetHost, "Target host");
75 Args.notNegative(targetHost.getPort(), "Target port");
76 this.targetHost = targetHost;
77 this.localAddress = local;
78 if (proxies != null && !proxies.isEmpty()) {
79 this.proxyChain = new ArrayList<>(proxies);
80 } else {
81 this.proxyChain = null;
82 }
83 if (tunnelled == TunnelType.TUNNELLED) {
84 Args.check(this.proxyChain != null, "Proxy required if tunnelled");
85 }
86 this.secure = secure;
87 this.tunnelled = tunnelled != null ? tunnelled : TunnelType.PLAIN;
88 this.layered = layered != null ? layered : LayerType.PLAIN;
89 }
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104 public HttpRoute(final HttpHost target, final InetAddress local, final HttpHost[] proxies,
105 final boolean secure, final TunnelType tunnelled, final LayerType layered) {
106 this(target, local, proxies != null ? Arrays.asList(proxies) : null,
107 secure, tunnelled, layered);
108 }
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127 public HttpRoute(final HttpHost target, final InetAddress local, final HttpHost proxy,
128 final boolean secure, final TunnelType tunnelled, final LayerType layered) {
129 this(target, local, proxy != null ? Collections.singletonList(proxy) : null,
130 secure, tunnelled, layered);
131 }
132
133
134
135
136
137
138
139
140
141
142
143 public HttpRoute(final HttpHost target, final InetAddress local, final boolean secure) {
144 this(target, local, Collections.emptyList(), secure, TunnelType.PLAIN, LayerType.PLAIN);
145 }
146
147
148
149
150
151
152 public HttpRoute(final HttpHost target) {
153 this(target, null, Collections.emptyList(), false, TunnelType.PLAIN, LayerType.PLAIN);
154 }
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169 public HttpRoute(final HttpHost target, final InetAddress local, final HttpHost proxy,
170 final boolean secure) {
171 this(target, local, Collections.singletonList(Args.notNull(proxy, "Proxy host")), secure,
172 secure ? TunnelType.TUNNELLED : TunnelType.PLAIN,
173 secure ? LayerType.LAYERED : LayerType.PLAIN);
174 }
175
176
177
178
179
180
181
182
183
184 public HttpRoute(final HttpHost target, final HttpHost proxy) {
185 this(target, null, proxy, false);
186 }
187
188 @Override
189 public HttpHost getTargetHost() {
190 return this.targetHost;
191 }
192
193 @Override
194 public InetAddress getLocalAddress() {
195 return this.localAddress;
196 }
197
198 public InetSocketAddress getLocalSocketAddress() {
199 return this.localAddress != null ? new InetSocketAddress(this.localAddress, 0) : null;
200 }
201
202 @Override
203 public int getHopCount() {
204 return proxyChain != null ? proxyChain.size() + 1 : 1;
205 }
206
207 @Override
208 public HttpHost getHopTarget(final int hop) {
209 Args.notNegative(hop, "Hop index");
210 final int hopcount = getHopCount();
211 Args.check(hop < hopcount, "Hop index exceeds tracked route length");
212 if (hop < hopcount - 1) {
213 return this.proxyChain.get(hop);
214 }
215 return this.targetHost;
216 }
217
218 @Override
219 public HttpHost getProxyHost() {
220 return proxyChain != null && !this.proxyChain.isEmpty() ? this.proxyChain.get(0) : null;
221 }
222
223 @Override
224 public TunnelType getTunnelType() {
225 return this.tunnelled;
226 }
227
228 @Override
229 public boolean isTunnelled() {
230 return (this.tunnelled == TunnelType.TUNNELLED);
231 }
232
233 @Override
234 public LayerType getLayerType() {
235 return this.layered;
236 }
237
238 @Override
239 public boolean isLayered() {
240 return (this.layered == LayerType.LAYERED);
241 }
242
243 @Override
244 public boolean isSecure() {
245 return this.secure;
246 }
247
248
249
250
251
252
253
254
255
256 @Override
257 public boolean equals(final Object obj) {
258 if (this == obj) {
259 return true;
260 }
261 if (obj instanceof HttpRoute) {
262 final HttpRoute that = (HttpRoute) obj;
263 return
264
265 (this.secure == that.secure) &&
266 (this.tunnelled == that.tunnelled) &&
267 (this.layered == that.layered) &&
268 LangUtils.equals(this.targetHost, that.targetHost) &&
269 LangUtils.equals(this.localAddress, that.localAddress) &&
270 LangUtils.equals(this.proxyChain, that.proxyChain);
271 }
272 return false;
273 }
274
275
276
277
278
279
280
281 @Override
282 public int hashCode() {
283 int hash = LangUtils.HASH_SEED;
284 hash = LangUtils.hashCode(hash, this.targetHost);
285 hash = LangUtils.hashCode(hash, this.localAddress);
286 if (this.proxyChain != null) {
287 for (final HttpHost element : this.proxyChain) {
288 hash = LangUtils.hashCode(hash, element);
289 }
290 }
291 hash = LangUtils.hashCode(hash, this.secure);
292 hash = LangUtils.hashCode(hash, this.tunnelled);
293 hash = LangUtils.hashCode(hash, this.layered);
294 return hash;
295 }
296
297
298
299
300
301
302 @Override
303 public String toString() {
304 final StringBuilder cab = new StringBuilder(50 + getHopCount()*30);
305 if (this.localAddress != null) {
306 cab.append(this.localAddress);
307 cab.append("->");
308 }
309 cab.append('{');
310 if (this.tunnelled == TunnelType.TUNNELLED) {
311 cab.append('t');
312 }
313 if (this.layered == LayerType.LAYERED) {
314 cab.append('l');
315 }
316 if (this.secure) {
317 cab.append('s');
318 }
319 cab.append("}->");
320 if (this.proxyChain != null) {
321 for (final HttpHost aProxyChain : this.proxyChain) {
322 cab.append(aProxyChain);
323 cab.append("->");
324 }
325 }
326 cab.append(this.targetHost);
327 return cab.toString();
328 }
329
330
331 @Override
332 public Object clone() throws CloneNotSupportedException {
333 return super.clone();
334 }
335
336 }