1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.mortbay.jetty.client;
16
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.net.UnknownHostException;
20 import java.security.KeyStore;
21 import java.security.SecureRandom;
22 import java.util.HashMap;
23 import java.util.LinkedList;
24 import java.util.Map;
25 import java.util.Set;
26
27 import javax.net.ssl.HostnameVerifier;
28 import javax.net.ssl.KeyManager;
29 import javax.net.ssl.KeyManagerFactory;
30 import javax.net.ssl.SSLContext;
31 import javax.net.ssl.SSLSession;
32 import javax.net.ssl.TrustManager;
33 import javax.net.ssl.TrustManagerFactory;
34 import javax.net.ssl.X509TrustManager;
35
36 import org.mortbay.component.LifeCycle;
37 import org.mortbay.io.Buffer;
38 import org.mortbay.io.ByteArrayBuffer;
39 import org.mortbay.io.nio.DirectNIOBuffer;
40 import org.mortbay.io.nio.IndirectNIOBuffer;
41 import org.mortbay.io.nio.NIOBuffer;
42 import org.mortbay.jetty.AbstractBuffers;
43 import org.mortbay.jetty.HttpSchemes;
44 import org.mortbay.jetty.client.security.Authorization;
45 import org.mortbay.jetty.client.security.RealmResolver;
46 import org.mortbay.log.Log;
47 import org.mortbay.resource.Resource;
48 import org.mortbay.thread.QueuedThreadPool;
49 import org.mortbay.thread.ThreadPool;
50 import org.mortbay.thread.Timeout;
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80 public class HttpClient extends AbstractBuffers
81 {
82 public static final int CONNECTOR_SOCKET = 0;
83 public static final int CONNECTOR_SELECT_CHANNEL = 2;
84
85 private int _connectorType = CONNECTOR_SELECT_CHANNEL;
86 private boolean _useDirectBuffers = true;
87 private int _maxConnectionsPerAddress = 32;
88 private Map<Address, HttpDestination> _destinations = new HashMap<Address, HttpDestination>();
89 ThreadPool _threadPool;
90 Connector _connector;
91 private long _idleTimeout = 20000;
92 private long _timeout = 320000;
93 int _soTimeout = 10000;
94 private Timeout _timeoutQ = new Timeout();
95 private Address _proxy;
96 private Authorization _proxyAuthentication;
97 private Set<String> _noProxy;
98 private int _maxRetries = 3;
99 private LinkedList<String> _registeredListeners;
100
101
102 private String _keyStoreLocation;
103 private String _keyStoreType = "JKS";
104 private String _keyStorePassword;
105 private String _keyManagerAlgorithm = "SunX509";
106 private String _keyManagerPassword;
107 private String _trustStoreLocation;
108 private String _trustStoreType = "JKS";
109 private String _trustStorePassword;
110 private String _trustManagerAlgorithm = "SunX509";
111
112 private SSLContext _sslContext;
113
114 private String _protocol = "TLS";
115 private String _provider;
116 private String _secureRandomAlgorithm;
117
118 private RealmResolver _realmResolver;
119
120 public void dump() throws IOException
121 {
122 for (Map.Entry<Address, HttpDestination> entry : _destinations.entrySet())
123 {
124 System.err.println("\n" + entry.getKey() + ":");
125 entry.getValue().dump();
126 }
127 }
128
129
130 public void send(HttpExchange exchange) throws IOException
131 {
132 boolean ssl = HttpSchemes.HTTPS_BUFFER.equalsIgnoreCase(exchange.getScheme());
133 exchange.setStatus(HttpExchange.STATUS_WAITING_FOR_CONNECTION);
134 HttpDestination destination = getDestination(exchange.getAddress(), ssl);
135 destination.send(exchange);
136 }
137
138
139
140
141
142 public ThreadPool getThreadPool()
143 {
144 return _threadPool;
145 }
146
147
148
149
150
151 public void setThreadPool(ThreadPool threadPool)
152 {
153 _threadPool = threadPool;
154 }
155
156
157 public HttpDestination getDestination(Address remote, boolean ssl) throws UnknownHostException, IOException
158 {
159 if (remote == null)
160 throw new UnknownHostException("Remote socket address cannot be null.");
161
162 synchronized (_destinations)
163 {
164 HttpDestination destination = _destinations.get(remote);
165 if (destination == null)
166 {
167 destination = new HttpDestination(this, remote, ssl, _maxConnectionsPerAddress);
168 if (_proxy != null && (_noProxy == null || !_noProxy.contains(remote.getHost())))
169 {
170 destination.setProxy(_proxy);
171 if (_proxyAuthentication != null)
172 destination.setProxyAuthentication(_proxyAuthentication);
173 }
174 _destinations.put(remote, destination);
175 }
176 return destination;
177 }
178 }
179
180
181 public void schedule(Timeout.Task task)
182 {
183 _timeoutQ.schedule(task);
184 }
185
186
187 public void cancel(Timeout.Task task)
188 {
189 task.cancel();
190 }
191
192
193
194
195
196 public boolean getUseDirectBuffers()
197 {
198 return _useDirectBuffers;
199 }
200
201
202 public void setRealmResolver(RealmResolver resolver)
203 {
204 _realmResolver = resolver;
205 }
206
207
208
209
210
211
212
213 public RealmResolver getRealmResolver()
214 {
215 return _realmResolver;
216 }
217
218
219 public boolean hasRealms()
220 {
221 return _realmResolver == null ? false : true;
222 }
223
224
225
226
227
228
229
230
231
232
233
234
235
236 public void registerListener(String listenerClass)
237 {
238 if (_registeredListeners == null)
239 {
240 _registeredListeners = new LinkedList<String>();
241 }
242 _registeredListeners.add(listenerClass);
243 }
244
245 public LinkedList<String> getRegisteredListeners()
246 {
247 return _registeredListeners;
248 }
249
250
251
252
253
254
255
256
257
258
259 public void setUseDirectBuffers(boolean direct)
260 {
261 _useDirectBuffers = direct;
262 }
263
264
265
266
267
268 public int getConnectorType()
269 {
270 return _connectorType;
271 }
272
273
274 public void setConnectorType(int connectorType)
275 {
276 this._connectorType = connectorType;
277 }
278
279
280
281
282
283
284 @Override
285 protected Buffer newBuffer(int size)
286 {
287 if (_connectorType != CONNECTOR_SOCKET)
288 {
289 Buffer buf = null;
290 if (size==getHeaderBufferSize())
291 buf=new IndirectNIOBuffer(size);
292 else if (_useDirectBuffers)
293 buf=new DirectNIOBuffer(size);
294 else
295 buf=new IndirectNIOBuffer(size);
296 return buf;
297 }
298 else
299 {
300 return new ByteArrayBuffer(size);
301 }
302 }
303
304
305 public int getMaxConnectionsPerAddress()
306 {
307 return _maxConnectionsPerAddress;
308 }
309
310
311 public void setMaxConnectionsPerAddress(int maxConnectionsPerAddress)
312 {
313 _maxConnectionsPerAddress = maxConnectionsPerAddress;
314 }
315
316
317 protected void doStart() throws Exception
318 {
319 super.doStart();
320
321 _timeoutQ.setNow();
322 _timeoutQ.setDuration(_timeout);
323
324 if (_threadPool == null)
325 {
326 QueuedThreadPool pool = new QueuedThreadPool();
327 pool.setMaxThreads(16);
328 pool.setDaemon(true);
329 pool.setName("HttpClient");
330 _threadPool = pool;
331 }
332
333 if (_threadPool instanceof LifeCycle)
334 {
335 ((LifeCycle)_threadPool).start();
336 }
337
338
339 if (_connectorType == CONNECTOR_SELECT_CHANNEL)
340 {
341
342 _connector = new SelectConnector(this);
343 }
344 else
345 {
346 _connector = new SocketConnector(this);
347 }
348 _connector.start();
349
350 _threadPool.dispatch(new Runnable()
351 {
352 public void run()
353 {
354 while (isStarted())
355 {
356 _timeoutQ.setNow();
357 _timeoutQ.tick();
358 try
359 {
360 Thread.sleep(1000);
361 }
362 catch (InterruptedException e)
363 {
364 }
365 }
366 }
367 });
368
369 }
370
371
372 protected void doStop() throws Exception
373 {
374 _connector.stop();
375 _connector = null;
376 if (_threadPool instanceof LifeCycle)
377 {
378 ((LifeCycle)_threadPool).stop();
379 }
380 for (HttpDestination destination : _destinations.values())
381 {
382 destination.close();
383 }
384
385 _timeoutQ.cancelAll();
386 super.doStop();
387 }
388
389
390 interface Connector extends LifeCycle
391 {
392 public void startConnection(HttpDestination destination) throws IOException;
393
394 }
395
396
397
398
399
400
401
402
403 protected SSLContext getSSLContext() throws IOException
404 {
405 if (_sslContext == null)
406 {
407 if (_keyStoreLocation == null)
408 {
409 _sslContext = getLooseSSLContext();
410 }
411 else
412 {
413 _sslContext = getStrictSSLContext();
414 }
415 }
416 return _sslContext;
417 }
418
419 protected SSLContext getStrictSSLContext() throws IOException
420 {
421
422 try
423 {
424 if (_trustStoreLocation == null)
425 {
426 _trustStoreLocation = _keyStoreLocation;
427 _trustStoreType = _keyStoreType;
428 }
429
430 KeyManager[] keyManagers = null;
431 InputStream keystoreInputStream = null;
432
433 keystoreInputStream = Resource.newResource(_keyStoreLocation).getInputStream();
434 KeyStore keyStore = KeyStore.getInstance(_keyStoreType);
435 keyStore.load(keystoreInputStream, _keyStorePassword == null ? null : _keyStorePassword.toString().toCharArray());
436
437 KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(_keyManagerAlgorithm);
438 keyManagerFactory.init(keyStore, _keyManagerPassword == null ? null : _keyManagerPassword.toString().toCharArray());
439 keyManagers = keyManagerFactory.getKeyManagers();
440
441 TrustManager[] trustManagers = null;
442 InputStream truststoreInputStream = null;
443
444 truststoreInputStream = Resource.newResource(_trustStoreLocation).getInputStream();
445 KeyStore trustStore = KeyStore.getInstance(_trustStoreType);
446 trustStore.load(truststoreInputStream, _trustStorePassword == null ? null : _trustStorePassword.toString().toCharArray());
447
448 TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(_trustManagerAlgorithm);
449 trustManagerFactory.init(trustStore);
450 trustManagers = trustManagerFactory.getTrustManagers();
451
452 SecureRandom secureRandom = _secureRandomAlgorithm == null ? null : SecureRandom.getInstance(_secureRandomAlgorithm);
453 SSLContext context = _provider == null ? SSLContext.getInstance(_protocol) : SSLContext.getInstance(_protocol, _provider);
454 context.init(keyManagers, trustManagers, secureRandom);
455 return context;
456 }
457 catch (Exception e)
458 {
459 e.printStackTrace();
460 throw new IOException("error generating ssl context for " + _keyStoreLocation + " " + e.getMessage());
461 }
462 }
463
464 protected SSLContext getLooseSSLContext() throws IOException
465 {
466
467
468
469 TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager()
470 {
471 public java.security.cert.X509Certificate[] getAcceptedIssuers()
472 {
473 return null;
474 }
475
476 public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
477 {
478 }
479
480 public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
481 {
482 }
483 }};
484
485 HostnameVerifier hostnameVerifier = new HostnameVerifier()
486 {
487 public boolean verify(String urlHostName, SSLSession session)
488 {
489 Log.warn("Warning: URL Host: " + urlHostName + " vs." + session.getPeerHost());
490 return true;
491 }
492 };
493
494
495 try
496 {
497
498 SSLContext sslContext = SSLContext.getInstance("SSL");
499 sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
500 return sslContext;
501 }
502 catch (Exception e)
503 {
504 throw new IOException("issue ignoring certs");
505 }
506 }
507
508
509
510
511
512 public long getIdleTimeout()
513 {
514 return _idleTimeout;
515 }
516
517
518
519
520
521 public void setIdleTimeout(long ms)
522 {
523 _idleTimeout = ms;
524 }
525
526
527 public int getSoTimeout()
528 {
529 return _soTimeout;
530 }
531
532
533 public void setSoTimeout(int so)
534 {
535 _soTimeout = so;
536 }
537
538
539
540
541
542 public long getTimeout()
543 {
544 return _timeout;
545 }
546
547
548
549
550
551 public void setTimeout(long ms)
552 {
553 _timeout = ms;
554 }
555
556
557 public Address getProxy()
558 {
559 return _proxy;
560 }
561
562
563 public void setProxy(Address proxy)
564 {
565 this._proxy = proxy;
566 }
567
568
569 public Authorization getProxyAuthentication()
570 {
571 return _proxyAuthentication;
572 }
573
574
575 public void setProxyAuthentication(Authorization authentication)
576 {
577 _proxyAuthentication = authentication;
578 }
579
580
581 public boolean isProxied()
582 {
583 return this._proxy != null;
584 }
585
586
587 public Set<String> getNoProxy()
588 {
589 return _noProxy;
590 }
591
592
593 public void setNoProxy(Set<String> noProxyAddresses)
594 {
595 _noProxy = noProxyAddresses;
596 }
597
598
599 public int maxRetries()
600 {
601 return _maxRetries;
602 }
603
604
605 public void setMaxRetries(int retries)
606 {
607 _maxRetries = retries;
608 }
609
610 public String getTrustStoreLocation()
611 {
612 return _trustStoreLocation;
613 }
614
615 public void setTrustStoreLocation(String trustStoreLocation)
616 {
617 this._trustStoreLocation = trustStoreLocation;
618 }
619
620 public String getKeyStoreLocation()
621 {
622 return _keyStoreLocation;
623 }
624
625 public void setKeyStoreLocation(String keyStoreLocation)
626 {
627 this._keyStoreLocation = keyStoreLocation;
628 }
629
630 public void setKeyStorePassword(String _keyStorePassword)
631 {
632 this._keyStorePassword = _keyStorePassword;
633 }
634
635 public void setKeyManagerPassword(String _keyManagerPassword)
636 {
637 this._keyManagerPassword = _keyManagerPassword;
638 }
639
640 public void setTrustStorePassword(String _trustStorePassword)
641 {
642 this._trustStorePassword = _trustStorePassword;
643 }
644 }