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