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