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 scheduleIdle(Timeout.Task task)
248 {
249 _idleTimeoutQ.schedule(task);
250 }
251
252
253 public void cancel(Timeout.Task task)
254 {
255 task.cancel();
256 }
257
258
259
260
261
262 public boolean getUseDirectBuffers()
263 {
264 return _useDirectBuffers;
265 }
266
267
268 public void setRealmResolver( RealmResolver resolver )
269 {
270 _realmResolver = resolver;
271 }
272
273
274
275
276
277
278
279 public RealmResolver getRealmResolver()
280 {
281 return _realmResolver;
282 }
283
284
285 public boolean hasRealms()
286 {
287 return _realmResolver==null?false:true;
288 }
289
290
291
292
293
294
295
296
297
298
299
300
301
302 public void registerListener( String listenerClass )
303 {
304 if ( _registeredListeners == null )
305 {
306 _registeredListeners = new LinkedList<String>();
307 }
308 _registeredListeners.add( listenerClass );
309 }
310
311 public LinkedList<String> getRegisteredListeners()
312 {
313 return _registeredListeners;
314 }
315
316
317
318
319
320
321
322
323
324
325
326 public void setUseDirectBuffers(boolean direct)
327 {
328 _useDirectBuffers=direct;
329 }
330
331
332
333
334
335 public int getConnectorType()
336 {
337 return _connectorType;
338 }
339
340
341 public void setConnectorType(int connectorType)
342 {
343 this._connectorType=connectorType;
344 }
345
346
347
348
349
350
351 @Override
352 protected Buffer newBuffer(int size)
353 {
354 if (_connectorType!=CONNECTOR_SOCKET)
355 {
356 Buffer buf=null;
357 if (size==getHeaderBufferSize())
358 buf=new IndirectNIOBuffer(size);
359 else if (_useDirectBuffers)
360 buf=new DirectNIOBuffer(size);
361 else
362 buf=new IndirectNIOBuffer(size);
363 return buf;
364 }
365 else
366 {
367 return new ByteArrayBuffer(size);
368 }
369 }
370
371
372 public int getMaxConnectionsPerAddress()
373 {
374 return _maxConnectionsPerAddress;
375 }
376
377
378 public void setMaxConnectionsPerAddress(int maxConnectionsPerAddress)
379 {
380 _maxConnectionsPerAddress=maxConnectionsPerAddress;
381 }
382
383
384 protected void doStart() throws Exception
385 {
386 super.doStart();
387
388 _timeoutQ.setDuration(_timeout);
389 _timeoutQ.setNow();
390 _idleTimeoutQ.setDuration(_idleTimeout);
391 _idleTimeoutQ.setNow();
392
393 if(_threadPool==null)
394 {
395 QueuedThreadPool pool = new QueuedThreadPool();
396 pool.setMaxThreads(16);
397 pool.setDaemon(true);
398 pool.setName("HttpClient");
399 _threadPool=pool;
400 }
401
402 if (_threadPool instanceof LifeCycle)
403 {
404 ((LifeCycle)_threadPool).start();
405 }
406
407
408 if (_connectorType==CONNECTOR_SELECT_CHANNEL)
409 {
410
411 _connector=new SelectConnector(this);
412 }
413 else
414 {
415 _connector=new SocketConnector(this);
416 }
417 _connector.start();
418
419 _threadPool.dispatch(new Runnable()
420 {
421 public void run()
422 {
423 while (isRunning())
424 {
425 _timeoutQ.tick(System.currentTimeMillis());
426 _idleTimeoutQ.tick(_timeoutQ.getNow());
427 try
428 {
429 Thread.sleep(200);
430 }
431 catch (InterruptedException e)
432 {
433 Log.ignore(e);
434 }
435 }
436 }
437 });
438
439 }
440
441
442 long getNow()
443 {
444 return _timeoutQ.getNow();
445 }
446
447
448 protected void doStop() throws Exception
449 {
450 _connector.stop();
451 _connector=null;
452 if (_threadPool instanceof LifeCycle)
453 {
454 ((LifeCycle)_threadPool).stop();
455 }
456 for (HttpDestination destination : _destinations.values())
457 {
458 destination.close();
459 }
460
461 _timeoutQ.cancelAll();
462 _idleTimeoutQ.cancelAll();
463 super.doStop();
464 }
465
466
467 interface Connector extends LifeCycle
468 {
469 public void startConnection(HttpDestination destination) throws IOException;
470
471 }
472
473
474
475
476
477
478
479
480 protected SSLContext getSSLContext() throws IOException
481 {
482 if (_sslContext == null)
483 {
484 if (_keyStoreLocation == null)
485 {
486 _sslContext = getLooseSSLContext();
487 }
488 else
489 {
490 _sslContext = getStrictSSLContext();
491 }
492 }
493 return _sslContext;
494 }
495
496 protected SSLContext getStrictSSLContext() throws IOException
497 {
498
499 try
500 {
501 if (_trustStoreLocation==null)
502 {
503 _trustStoreLocation=_keyStoreLocation;
504 _trustStoreType=_keyStoreType;
505 }
506
507 KeyManager[] keyManagers=null;
508 InputStream keystoreInputStream = null;
509
510 keystoreInputStream= Resource.newResource(_keyStoreLocation).getInputStream();
511 KeyStore keyStore=KeyStore.getInstance(_keyStoreType);
512 keyStore.load(keystoreInputStream,_keyStorePassword==null?null:_keyStorePassword.toString().toCharArray());
513
514 KeyManagerFactory keyManagerFactory=KeyManagerFactory.getInstance(_keyManagerAlgorithm);
515 keyManagerFactory.init(keyStore,_keyManagerPassword==null?null:_keyManagerPassword.toString().toCharArray());
516 keyManagers=keyManagerFactory.getKeyManagers();
517
518 TrustManager[] trustManagers=null;
519 InputStream truststoreInputStream = null;
520
521 truststoreInputStream = Resource.newResource(_trustStoreLocation).getInputStream();
522 KeyStore trustStore=KeyStore.getInstance(_trustStoreType);
523 trustStore.load(truststoreInputStream,_trustStorePassword==null?null:_trustStorePassword.toString().toCharArray());
524
525 TrustManagerFactory trustManagerFactory=TrustManagerFactory.getInstance(_trustManagerAlgorithm);
526 trustManagerFactory.init(trustStore);
527 trustManagers=trustManagerFactory.getTrustManagers();
528
529 SecureRandom secureRandom=_secureRandomAlgorithm==null?null:SecureRandom.getInstance(_secureRandomAlgorithm);
530 SSLContext context=_provider==null?SSLContext.getInstance(_protocol):SSLContext.getInstance(_protocol,_provider);
531 context.init(keyManagers,trustManagers,secureRandom);
532 return context;
533 }
534 catch ( Exception e )
535 {
536 Log.debug(e);
537 throw new IOException( "error generating ssl context for " + _keyStoreLocation + " " + e.getMessage() );
538 }
539 }
540
541 protected SSLContext getLooseSSLContext() throws IOException
542 {
543
544
545
546 TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager()
547 {
548 public java.security.cert.X509Certificate[] getAcceptedIssuers()
549 {
550 return null;
551 }
552
553 public void checkClientTrusted( java.security.cert.X509Certificate[] certs, String authType )
554 {
555 }
556
557 public void checkServerTrusted( java.security.cert.X509Certificate[] certs, String authType )
558 {
559 }
560 } };
561
562 HostnameVerifier hostnameVerifier = new HostnameVerifier()
563 {
564 public boolean verify( String urlHostName, SSLSession session )
565 {
566 Log.warn( "Warning: URL Host: " + urlHostName + " vs." + session.getPeerHost() );
567 return true;
568 }
569 };
570
571
572 try
573 {
574
575 SSLContext sslContext = SSLContext.getInstance( "SSL" );
576 sslContext.init( null, trustAllCerts, new java.security.SecureRandom() );
577 return sslContext;
578 }
579 catch ( Exception e )
580 {
581 Log.debug(e);
582 throw new IOException( "issue ignoring certs" );
583 }
584 }
585
586
587
588
589
590 public long getIdleTimeout()
591 {
592 return _idleTimeout;
593 }
594
595
596
597
598
599 public void setIdleTimeout(long ms)
600 {
601 _idleTimeout=ms;
602 }
603
604
605 public int getSoTimeout()
606 {
607 return _soTimeout;
608 }
609
610
611 public void setSoTimeout(int so)
612 {
613 _soTimeout = so;
614 }
615
616
617
618
619
620 public long getTimeout()
621 {
622 return _timeout;
623 }
624
625
626
627
628
629 public void setTimeout(long ms)
630 {
631 _timeout=ms;
632 }
633
634
635 public Address getProxy()
636 {
637 return _proxy;
638 }
639
640
641 public void setProxy(Address proxy)
642 {
643 this._proxy = proxy;
644 }
645
646
647 public Authorization getProxyAuthentication()
648 {
649 return _proxyAuthentication;
650 }
651
652
653 public void setProxyAuthentication(Authorization authentication)
654 {
655 _proxyAuthentication = authentication;
656 }
657
658
659 public boolean isProxied()
660 {
661 return this._proxy!=null;
662 }
663
664
665 public Set<String> getNoProxy()
666 {
667 return _noProxy;
668 }
669
670
671 public void setNoProxy(Set<String> noProxyAddresses)
672 {
673 _noProxy = noProxyAddresses;
674 }
675
676
677 public int maxRetries()
678 {
679 return _maxRetries;
680 }
681
682
683 public void setMaxRetries( int retries )
684 {
685 _maxRetries = retries;
686 }
687
688
689 public String getTrustStoreLocation()
690 {
691 return _trustStoreLocation;
692 }
693
694
695 public void setTrustStoreLocation(String trustStoreLocation)
696 {
697 this._trustStoreLocation = trustStoreLocation;
698 }
699
700
701 public String getKeyStoreLocation()
702 {
703 return _keyStoreLocation;
704 }
705
706
707 public void setKeyStoreLocation(String keyStoreLocation)
708 {
709 this._keyStoreLocation = keyStoreLocation;
710 }
711
712
713 public void setKeyStorePassword(String _keyStorePassword)
714 {
715 this._keyStorePassword = _keyStorePassword;
716 }
717
718
719 public void setKeyManagerPassword(String _keyManagerPassword)
720 {
721 this._keyManagerPassword = _keyManagerPassword;
722 }
723
724
725 public void setTrustStorePassword(String _trustStorePassword)
726 {
727 this._trustStorePassword = _trustStorePassword;
728 }
729 }