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