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