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.jetty.AbstractBuffers;
42 import org.mortbay.jetty.HttpSchemes;
43 import org.mortbay.jetty.client.security.Authorization;
44 import org.mortbay.jetty.client.security.RealmResolver;
45 import org.mortbay.log.Log;
46 import org.mortbay.resource.Resource;
47 import org.mortbay.thread.QueuedThreadPool;
48 import org.mortbay.thread.ThreadPool;
49 import org.mortbay.thread.Timeout;
50
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 public class HttpClient extends AbstractBuffers
80 {
81 public static final int CONNECTOR_SOCKET=0;
82 public static final int CONNECTOR_SELECT_CHANNEL=2;
83
84 private int _connectorType=CONNECTOR_SELECT_CHANNEL;
85 private boolean _useDirectBuffers=true;
86 private int _maxConnectionsPerAddress=32;
87 private Map<Address, HttpDestination> _destinations = new HashMap<Address, HttpDestination>();
88 ThreadPool _threadPool;
89 Connector _connector;
90 private long _idleTimeout=20000;
91 private long _timeout=320000;
92 private int _soTimeout = 10000;
93 private Timeout _timeoutQ = new Timeout();
94 private Address _proxy;
95 private Authorization _proxyAuthentication;
96 private Set<String> _noProxy;
97 private int _maxRetries = 3;
98 private LinkedList<String> _registeredListeners;
99
100
101 private String _keyStoreLocation;
102 private String _keyStoreType="JKS";
103 private String _keyStorePassword;
104 private String _keyManagerAlgorithm = "SunX509";
105 private String _keyManagerPassword;
106 private String _trustStoreLocation;
107 private String _trustStoreType="JKS";
108 private String _trustStorePassword;
109 private String _trustManagerAlgorithm = "SunX509";
110
111 private SSLContext _sslContext;
112
113 private String _protocol="TLS";
114 private String _provider;
115 private String _secureRandomAlgorithm;
116
117 private RealmResolver _realmResolver;
118
119 public void dump() throws IOException
120 {
121 for (Map.Entry<Address, HttpDestination> entry : _destinations.entrySet())
122 {
123 System.err.println("\n"+entry.getKey()+":");
124 entry.getValue().dump();
125 }
126 }
127
128
129 public void send(HttpExchange exchange) throws IOException
130 {
131 boolean ssl=HttpSchemes.HTTPS_BUFFER.equalsIgnoreCase(exchange.getScheme());
132 exchange.setStatus(HttpExchange.STATUS_WAITING_FOR_CONNECTION);
133 HttpDestination destination=getDestination(exchange.getAddress(),ssl);
134 destination.send(exchange);
135 }
136
137
138
139
140
141 public ThreadPool getThreadPool()
142 {
143 return _threadPool;
144 }
145
146
147
148
149
150 public void setThreadPool(ThreadPool threadPool)
151 {
152 _threadPool=threadPool;
153 }
154
155
156 public HttpDestination getDestination(Address remote, boolean ssl) throws UnknownHostException, IOException
157 {
158 if (remote==null)
159 throw new UnknownHostException("Remote socket address cannot be null.");
160
161 synchronized (_destinations)
162 {
163 HttpDestination destination=_destinations.get(remote);
164 if (destination==null)
165 {
166 destination=new HttpDestination(this,remote,ssl,_maxConnectionsPerAddress);
167 if (_proxy != null && (_noProxy == null || !_noProxy.contains(remote.getHost())))
168 {
169 destination.setProxy(_proxy);
170 if (_proxyAuthentication!=null)
171 destination.setProxyAuthentication(_proxyAuthentication);
172 }
173 _destinations.put(remote,destination);
174 }
175 return destination;
176 }
177 }
178
179
180 public void schedule(Timeout.Task task)
181 {
182 _timeoutQ.schedule(task);
183 }
184
185
186 public void cancel(Timeout.Task task)
187 {
188 task.cancel();
189 }
190
191
192
193
194
195 public boolean getUseDirectBuffers()
196 {
197 return _useDirectBuffers;
198 }
199
200
201 public void setRealmResolver( RealmResolver resolver )
202 {
203 _realmResolver = resolver;
204 }
205
206
207
208
209
210
211
212 public RealmResolver getRealmResolver()
213 {
214 return _realmResolver;
215 }
216
217
218 public boolean hasRealms()
219 {
220 return _realmResolver==null?false:true;
221 }
222
223
224
225
226
227
228
229
230
231
232
233
234
235 public void registerListener( String listenerClass )
236 {
237 if ( _registeredListeners == null )
238 {
239 _registeredListeners = new LinkedList<String>();
240 }
241 _registeredListeners.add( listenerClass );
242 }
243
244 public LinkedList<String> getRegisteredListeners()
245 {
246 return _registeredListeners;
247 }
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 (isRunning())
355 {
356 _timeoutQ.setNow();
357 _timeoutQ.tick();
358 try
359 {
360 Thread.sleep(1000);
361 }
362 catch (InterruptedException e)
363 {
364 Log.ignore(e);
365 }
366 }
367 }
368 });
369
370 }
371
372
373 protected void doStop() throws Exception
374 {
375 _connector.stop();
376 _connector=null;
377 if (_threadPool instanceof LifeCycle)
378 {
379 ((LifeCycle)_threadPool).stop();
380 }
381 for (HttpDestination destination : _destinations.values())
382 {
383 destination.close();
384 }
385
386 _timeoutQ.cancelAll();
387 super.doStop();
388 }
389
390
391 interface Connector extends LifeCycle
392 {
393 public void startConnection(HttpDestination destination) throws IOException;
394
395 }
396
397
398
399
400
401
402
403
404 protected SSLContext getSSLContext() throws IOException
405 {
406 if (_sslContext == null)
407 {
408 if (_keyStoreLocation == null)
409 {
410 _sslContext = getLooseSSLContext();
411 }
412 else
413 {
414 _sslContext = getStrictSSLContext();
415 }
416 }
417 return _sslContext;
418 }
419
420 protected SSLContext getStrictSSLContext() throws IOException
421 {
422
423 try
424 {
425 if (_trustStoreLocation==null)
426 {
427 _trustStoreLocation=_keyStoreLocation;
428 _trustStoreType=_keyStoreType;
429 }
430
431 KeyManager[] keyManagers=null;
432 InputStream keystoreInputStream = null;
433
434 keystoreInputStream= Resource.newResource(_keyStoreLocation).getInputStream();
435 KeyStore keyStore=KeyStore.getInstance(_keyStoreType);
436 keyStore.load(keystoreInputStream,_keyStorePassword==null?null:_keyStorePassword.toString().toCharArray());
437
438 KeyManagerFactory keyManagerFactory=KeyManagerFactory.getInstance(_keyManagerAlgorithm);
439 keyManagerFactory.init(keyStore,_keyManagerPassword==null?null:_keyManagerPassword.toString().toCharArray());
440 keyManagers=keyManagerFactory.getKeyManagers();
441
442 TrustManager[] trustManagers=null;
443 InputStream truststoreInputStream = null;
444
445 truststoreInputStream = Resource.newResource(_trustStoreLocation).getInputStream();
446 KeyStore trustStore=KeyStore.getInstance(_trustStoreType);
447 trustStore.load(truststoreInputStream,_trustStorePassword==null?null:_trustStorePassword.toString().toCharArray());
448
449 TrustManagerFactory trustManagerFactory=TrustManagerFactory.getInstance(_trustManagerAlgorithm);
450 trustManagerFactory.init(trustStore);
451 trustManagers=trustManagerFactory.getTrustManagers();
452
453 SecureRandom secureRandom=_secureRandomAlgorithm==null?null:SecureRandom.getInstance(_secureRandomAlgorithm);
454 SSLContext context=_provider==null?SSLContext.getInstance(_protocol):SSLContext.getInstance(_protocol,_provider);
455 context.init(keyManagers,trustManagers,secureRandom);
456 return context;
457 }
458 catch ( Exception e )
459 {
460 Log.debug(e);
461 throw new IOException( "error generating ssl context for " + _keyStoreLocation + " " + e.getMessage() );
462 }
463 }
464
465 protected SSLContext getLooseSSLContext() throws IOException
466 {
467
468
469
470 TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager()
471 {
472 public java.security.cert.X509Certificate[] getAcceptedIssuers()
473 {
474 return null;
475 }
476
477 public void checkClientTrusted( java.security.cert.X509Certificate[] certs, String authType )
478 {
479 }
480
481 public void checkServerTrusted( java.security.cert.X509Certificate[] certs, String authType )
482 {
483 }
484 } };
485
486 HostnameVerifier hostnameVerifier = new HostnameVerifier()
487 {
488 public boolean verify( String urlHostName, SSLSession session )
489 {
490 Log.warn( "Warning: URL Host: " + urlHostName + " vs." + session.getPeerHost() );
491 return true;
492 }
493 };
494
495
496 try
497 {
498
499 SSLContext sslContext = SSLContext.getInstance( "SSL" );
500 sslContext.init( null, trustAllCerts, new java.security.SecureRandom() );
501 return sslContext;
502 }
503 catch ( Exception e )
504 {
505 Log.debug(e);
506 throw new IOException( "issue ignoring certs" );
507 }
508 }
509
510
511
512
513
514 public long getIdleTimeout()
515 {
516 return _idleTimeout;
517 }
518
519
520
521
522
523 public void setIdleTimeout(long ms)
524 {
525 _idleTimeout=ms;
526 }
527
528
529 public int getSoTimeout()
530 {
531 return _soTimeout;
532 }
533
534
535 public void setSoTimeout(int so)
536 {
537 _soTimeout = so;
538 }
539
540
541
542
543
544 public long getTimeout()
545 {
546 return _timeout;
547 }
548
549
550
551
552
553 public void setTimeout(long ms)
554 {
555 _timeout=ms;
556 }
557
558
559 public Address getProxy()
560 {
561 return _proxy;
562 }
563
564
565 public void setProxy(Address proxy)
566 {
567 this._proxy = proxy;
568 }
569
570
571 public Authorization getProxyAuthentication()
572 {
573 return _proxyAuthentication;
574 }
575
576
577 public void setProxyAuthentication(Authorization authentication)
578 {
579 _proxyAuthentication = authentication;
580 }
581
582
583 public boolean isProxied()
584 {
585 return this._proxy!=null;
586 }
587
588
589 public Set<String> getNoProxy()
590 {
591 return _noProxy;
592 }
593
594
595 public void setNoProxy(Set<String> noProxyAddresses)
596 {
597 _noProxy = noProxyAddresses;
598 }
599
600
601 public int maxRetries()
602 {
603 return _maxRetries;
604 }
605
606
607 public void setMaxRetries( int retries )
608 {
609 _maxRetries = retries;
610 }
611
612
613 public String getTrustStoreLocation()
614 {
615 return _trustStoreLocation;
616 }
617
618
619 public void setTrustStoreLocation(String trustStoreLocation)
620 {
621 this._trustStoreLocation = trustStoreLocation;
622 }
623
624
625 public String getKeyStoreLocation()
626 {
627 return _keyStoreLocation;
628 }
629
630
631 public void setKeyStoreLocation(String keyStoreLocation)
632 {
633 this._keyStoreLocation = keyStoreLocation;
634 }
635
636
637 public void setKeyStorePassword(String _keyStorePassword)
638 {
639 this._keyStorePassword = _keyStorePassword;
640 }
641
642
643 public void setKeyManagerPassword(String _keyManagerPassword)
644 {
645 this._keyManagerPassword = _keyManagerPassword;
646 }
647
648
649 public void setTrustStorePassword(String _trustStorePassword)
650 {
651 this._trustStorePassword = _trustStorePassword;
652 }
653 }