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