1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.mortbay.jetty.client;
15
16
17 import java.io.IOException;
18 import java.lang.reflect.Constructor;
19 import java.net.InetSocketAddress;
20 import java.util.ArrayList;
21 import java.util.LinkedList;
22 import java.util.List;
23 import java.util.concurrent.ArrayBlockingQueue;
24
25 import javax.servlet.http.Cookie;
26
27 import org.mortbay.io.Buffer;
28 import org.mortbay.io.ByteArrayBuffer;
29 import org.mortbay.jetty.HttpHeaders;
30 import org.mortbay.jetty.client.security.Authorization;
31 import org.mortbay.jetty.client.security.SecurityListener;
32 import org.mortbay.jetty.servlet.PathMap;
33 import org.mortbay.log.Log;
34
35
36
37
38
39
40 public class HttpDestination
41 {
42 private ByteArrayBuffer _hostHeader;
43 private InetSocketAddress _address;
44 private LinkedList<HttpConnection> _connections=new LinkedList<HttpConnection>();
45 private ArrayList<HttpConnection> _idle=new ArrayList<HttpConnection>();
46 private HttpClient _client;
47 private boolean _ssl;
48 private int _maxConnections;
49 private int _pendingConnections=0;
50 private ArrayBlockingQueue<Object> _newQueue = new ArrayBlockingQueue<Object>(10,true);
51 private int _newConnection=0;
52 private InetSocketAddress _proxy;
53 private Authorization _proxyAuthentication;
54 private PathMap _authorizations;
55 private List<Cookie> _cookies;
56
57
58
59 public void dump() throws IOException
60 {
61 synchronized (this)
62 {
63 System.err.println(this);
64 System.err.println("connections="+_connections.size());
65 System.err.println("idle="+_idle.size());
66 System.err.println("pending="+_pendingConnections);
67 for (HttpConnection c : _connections)
68 {
69 if (!c.isIdle())
70 c.dump();
71 }
72 }
73 }
74
75
76 private LinkedList<HttpExchange> _queue=new LinkedList<HttpExchange>();
77
78
79 HttpDestination(HttpClient pool, InetSocketAddress address, boolean ssl, int maxConnections)
80 {
81 _client=pool;
82 _address=address;
83 _ssl=ssl;
84 _maxConnections=maxConnections;
85 String host = address.getHostName();
86 if (address.getPort() != (_ssl ? 443 : 80)) {
87 host += ":" + address.getPort();
88 }
89 _hostHeader = new ByteArrayBuffer (host);
90 }
91
92
93 public InetSocketAddress getAddress()
94 {
95 return _address;
96 }
97
98
99 public Buffer getHostHeader()
100 {
101 return _hostHeader;
102 }
103
104
105 public HttpClient getHttpClient()
106 {
107 return _client;
108 }
109
110
111 public boolean isSecure()
112 {
113 return _ssl;
114 }
115
116
117 public void addAuthorization(String pathSpec,Authorization authorization)
118 {
119 synchronized (this)
120 {
121 if (_authorizations==null)
122 _authorizations=new PathMap();
123 _authorizations.put(pathSpec,authorization);
124 }
125
126
127 }
128
129
130 public void addCookie(Cookie cookie)
131 {
132 synchronized (this)
133 {
134 if (_cookies==null)
135 _cookies=new ArrayList<Cookie>();
136 _cookies.add(cookie);
137 }
138
139
140 }
141
142
143 public HttpConnection getConnection() throws IOException
144 {
145 HttpConnection connection = getIdleConnection();
146
147 while (connection==null)
148 {
149 synchronized(this)
150 {
151 _newConnection++;
152 startNewConnection();
153 }
154
155 try
156 {
157 Object o =_newQueue.take();
158 if (o instanceof HttpConnection)
159 connection=(HttpConnection)o;
160 else
161 throw (IOException)o;
162 }
163 catch (InterruptedException e)
164 {
165 Log.ignore(e);
166 }
167 }
168 return connection;
169 }
170
171
172 public HttpConnection getIdleConnection() throws IOException
173 {
174 synchronized (this)
175 {
176 long now = System.currentTimeMillis();
177 long idleTimeout=_client.getIdleTimeout();
178
179
180 while (_idle.size() > 0)
181 {
182 HttpConnection connection = _idle.remove(_idle.size()-1);
183 long last = connection.getLast();
184 if (connection.getEndPoint().isOpen() && (last==0 || ((now-last)<idleTimeout)) )
185 return connection;
186 else
187 {
188 _connections.remove(connection);
189 connection.getEndPoint().close();
190 }
191 }
192
193 return null;
194 }
195 }
196
197
198 protected void startNewConnection()
199 {
200 try
201 {
202 synchronized (this)
203 {
204 _pendingConnections++;
205 }
206 _client._connector.startConnection(this);
207 }
208 catch(Exception e)
209 {
210 onConnectionFailed(e);
211 }
212 }
213
214
215 public void onConnectionFailed(Throwable throwable)
216 {
217 Throwable connect_failure=null;
218
219 synchronized (this)
220 {
221 _pendingConnections--;
222 if (_newConnection>0)
223 {
224 connect_failure=throwable;
225 _newConnection--;
226 }
227 else if (_queue.size()>0)
228 {
229 HttpExchange ex=_queue.removeFirst();
230 ex.getEventListener().onConnectionFailed(throwable);
231 }
232 }
233
234 if(connect_failure!=null)
235 {
236 try
237 {
238 _newQueue.put(connect_failure);
239 }
240 catch (InterruptedException e)
241 {
242 Log.ignore(e);
243 }
244 }
245 }
246
247
248 public void onException(Throwable throwable)
249 {
250 synchronized (this)
251 {
252 _pendingConnections--;
253 if (_queue.size()>0)
254 {
255 HttpExchange ex=_queue.removeFirst();
256 ex.getEventListener().onException(throwable);
257 ex.setStatus(HttpExchange.STATUS_EXCEPTED);
258 }
259 }
260 }
261
262
263 public void onNewConnection(HttpConnection connection) throws IOException
264 {
265 HttpConnection q_connection=null;
266
267 synchronized (this)
268 {
269 _pendingConnections--;
270 _connections.add(connection);
271
272 if (_newConnection>0)
273 {
274 q_connection=connection;
275 _newConnection--;
276 }
277 else if (_queue.size()==0)
278 {
279 _idle.add(connection);
280 }
281 else
282 {
283 HttpExchange ex=_queue.removeFirst();
284 connection.send(ex);
285 }
286 }
287
288 if (q_connection!=null)
289 {
290 try
291 {
292 _newQueue.put(q_connection);
293 }
294 catch (InterruptedException e)
295 {
296 Log.ignore(e);
297 }
298 }
299 }
300
301
302 public void returnConnection(HttpConnection connection, boolean close) throws IOException
303 {
304 if (close)
305 {
306 try
307 {
308 connection.close();
309 }
310 catch(IOException e)
311 {
312 Log.ignore(e);
313 }
314 }
315
316 if (!_client.isStarted())
317 return;
318
319 if (!close && connection.getEndPoint().isOpen())
320 {
321 synchronized (this)
322 {
323 if (_queue.size()==0)
324 {
325 connection.setLast(System.currentTimeMillis());
326 _idle.add(connection);
327 }
328 else
329 {
330 HttpExchange ex=_queue.removeFirst();
331 connection.send(ex);
332 }
333 this.notifyAll();
334 }
335 }
336 else
337 {
338 synchronized (this)
339 {
340 _connections.remove(connection);
341 if (!_queue.isEmpty())
342 startNewConnection();
343 }
344 }
345 }
346
347
348 public void send(HttpExchange ex) throws IOException
349 {
350 LinkedList<String> listeners = _client.getRegisteredListeners();
351
352 if (listeners != null)
353 {
354
355 for (int i = listeners.size(); i > 0; --i)
356 {
357 String listenerClass = listeners.get(i - 1);
358
359 try
360 {
361 Class listener = Class.forName(listenerClass);
362 Constructor constructor = listener.getDeclaredConstructor(HttpDestination.class, HttpExchange.class);
363 HttpEventListener elistener = (HttpEventListener) constructor.newInstance(this, ex);
364 ex.setEventListener(elistener);
365 }
366 catch (Exception e)
367 {
368 e.printStackTrace();
369 throw new IOException("Unable to instantiate registered listener for destination: " + listenerClass );
370 }
371 }
372 }
373
374
375 if ( _client.hasRealms() )
376 {
377 ex.setEventListener( new SecurityListener( this, ex ) );
378 }
379
380 doSend(ex);
381 }
382
383
384 public void resend(HttpExchange ex) throws IOException
385 {
386 ex.getEventListener().onRetry();
387 doSend(ex);
388 }
389
390
391 protected void doSend(HttpExchange ex) throws IOException
392 {
393
394
395 if (_cookies!=null)
396 {
397 StringBuilder buf=null;
398 for (Cookie cookie : _cookies)
399 {
400 if (buf==null)
401 buf=new StringBuilder();
402 else
403 buf.append("; ");
404 buf.append(cookie.getName());
405 buf.append("=");
406 buf.append(cookie.getValue());
407 }
408 if (buf!=null)
409 ex.addRequestHeader(HttpHeaders.COOKIE,buf.toString());
410 }
411
412
413 if (_authorizations!=null)
414 {
415 Authorization auth= (Authorization)_authorizations.match(ex.getURI());
416 if (auth !=null)
417 ((Authorization)auth).setCredentials(ex);
418 }
419
420 synchronized(this)
421 {
422
423
424 HttpConnection connection=null;
425 if (_queue.size()>0 || (connection=getIdleConnection())==null || !connection.send(ex))
426 {
427 _queue.add(ex);
428 if (_connections.size()+_pendingConnections <_maxConnections)
429 {
430 startNewConnection();
431 }
432 }
433 }
434 }
435
436
437 public synchronized String toString()
438 {
439 return "HttpDestination@"+hashCode()+"//"+_address.getHostName()+":"+_address.getPort()+"("+_connections.size()+","+_idle.size()+","+_queue.size()+")";
440 }
441
442
443 public synchronized String toDetailString()
444 {
445 StringBuilder b = new StringBuilder();
446 b.append(toString());
447 b.append('\n');
448 synchronized(this)
449 {
450 for (HttpConnection connection : _connections)
451 {
452 if (connection._exchange!=null)
453 {
454 b.append(connection.toDetailString());
455 if (_idle.contains(connection))
456 b.append(" IDLE");
457 b.append('\n');
458 }
459 }
460 }
461 b.append("--");
462 b.append('\n');
463
464 return b.toString();
465 }
466
467
468 public void setProxy(InetSocketAddress proxy)
469 {
470 _proxy=proxy;
471 }
472
473
474 public InetSocketAddress getProxy()
475 {
476 return _proxy;
477 }
478
479
480 public Authorization getProxyAuthentication()
481 {
482 return _proxyAuthentication;
483 }
484
485
486 public void setProxyAuthentication(Authorization authentication)
487 {
488 _proxyAuthentication = authentication;
489 }
490
491
492 public boolean isProxied()
493 {
494 return _proxy!=null;
495 }
496
497
498 public void close() throws IOException
499 {
500 synchronized (this)
501 {
502 for (HttpConnection connection : _connections)
503 {
504 connection.close();
505 }
506 }
507 }
508
509 }