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