1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.http.impl.conn;
29
30 import java.io.Closeable;
31 import java.io.IOException;
32 import java.net.InetSocketAddress;
33 import java.util.Date;
34 import java.util.concurrent.TimeUnit;
35
36 import org.apache.commons.logging.Log;
37 import org.apache.commons.logging.LogFactory;
38 import org.apache.http.HttpClientConnection;
39 import org.apache.http.HttpHost;
40 import org.apache.http.annotation.GuardedBy;
41 import org.apache.http.annotation.ThreadSafe;
42 import org.apache.http.config.ConnectionConfig;
43 import org.apache.http.config.Lookup;
44 import org.apache.http.config.Registry;
45 import org.apache.http.config.RegistryBuilder;
46 import org.apache.http.config.SocketConfig;
47 import org.apache.http.conn.ConnectionRequest;
48 import org.apache.http.conn.DnsResolver;
49 import org.apache.http.conn.HttpClientConnectionManager;
50 import org.apache.http.conn.HttpConnectionFactory;
51 import org.apache.http.conn.SchemePortResolver;
52 import org.apache.http.conn.ManagedHttpClientConnection;
53 import org.apache.http.conn.routing.HttpRoute;
54 import org.apache.http.conn.socket.ConnectionSocketFactory;
55 import org.apache.http.conn.socket.PlainSocketFactory;
56 import org.apache.http.conn.ssl.SSLSocketFactory;
57 import org.apache.http.protocol.HttpContext;
58 import org.apache.http.util.Args;
59 import org.apache.http.util.Asserts;
60 import org.apache.http.util.LangUtils;
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78 @ThreadSafe
79 public class BasicHttpClientConnectionManager implements HttpClientConnectionManager, Closeable {
80
81 private final Log log = LogFactory.getLog(getClass());
82
83 private final HttpClientConnectionOperator connectionOperator;
84 private final HttpConnectionFactory<ManagedHttpClientConnection> connFactory;
85
86 @GuardedBy("this")
87 private ManagedHttpClientConnection conn;
88
89 @GuardedBy("this")
90 private HttpRoute route;
91
92 @GuardedBy("this")
93 private Object state;
94
95 @GuardedBy("this")
96 private long updated;
97
98 @GuardedBy("this")
99 private long expiry;
100
101 @GuardedBy("this")
102 private boolean leased;
103
104 @GuardedBy("this")
105 private SocketConfig socketConfig;
106
107 @GuardedBy("this")
108 private ConnectionConfig connConfig;
109
110 @GuardedBy("this")
111 private volatile boolean shutdown;
112
113 private static Registry<ConnectionSocketFactory> getDefaultRegistry() {
114 return RegistryBuilder.<ConnectionSocketFactory>create()
115 .register("https", PlainSocketFactory.getSocketFactory())
116 .register("https", SSLSocketFactory.getSocketFactory())
117 .build();
118 }
119
120 public BasicHttpClientConnectionManager(
121 final Lookup<ConnectionSocketFactory> socketFactoryRegistry,
122 final HttpConnectionFactory<ManagedHttpClientConnection> connFactory,
123 final SchemePortResolver schemePortResolver,
124 final DnsResolver dnsResolver) {
125 super();
126 this.connectionOperator = new HttpClientConnectionOperator(
127 socketFactoryRegistry, schemePortResolver, dnsResolver);
128 this.connFactory = connFactory != null ? connFactory : ManagedHttpClientConnectionFactory.INSTANCE;
129 this.expiry = Long.MAX_VALUE;
130 this.socketConfig = SocketConfig.DEFAULT;
131 this.connConfig = ConnectionConfig.DEFAULT;
132 }
133
134 public BasicHttpClientConnectionManager(
135 final Lookup<ConnectionSocketFactory> socketFactoryRegistry,
136 final HttpConnectionFactory<ManagedHttpClientConnection> connFactory) {
137 this(socketFactoryRegistry, connFactory, null, null);
138 }
139
140 public BasicHttpClientConnectionManager(
141 final Lookup<ConnectionSocketFactory> socketFactoryRegistry) {
142 this(socketFactoryRegistry, null, null, null);
143 }
144
145 public BasicHttpClientConnectionManager() {
146 this(getDefaultRegistry(), null, null, null);
147 }
148
149 @Override
150 protected void finalize() throws Throwable {
151 try {
152 shutdown();
153 } finally {
154 super.finalize();
155 }
156 }
157
158 public void close() {
159 shutdown();
160 }
161
162 HttpRoute getRoute() {
163 return route;
164 }
165
166 Object getState() {
167 return state;
168 }
169
170 public synchronized SocketConfig getSocketConfig() {
171 return socketConfig;
172 }
173
174 public synchronized void setSocketConfig(final SocketConfig socketConfig) {
175 this.socketConfig = socketConfig != null ? socketConfig : SocketConfig.DEFAULT;
176 }
177
178 public synchronized ConnectionConfig getConnectionConfig() {
179 return connConfig;
180 }
181
182 public synchronized void setConnectionConfig(final ConnectionConfig connConfig) {
183 this.connConfig = connConfig != null ? connConfig : ConnectionConfig.DEFAULT;
184 }
185
186 public final ConnectionRequest requestConnection(
187 final HttpRoute route,
188 final Object state) {
189 Args.notNull(route, "Route");
190 return new ConnectionRequest() {
191
192 public boolean cancel() {
193
194 return false;
195 }
196
197 public HttpClientConnection get(final long timeout, final TimeUnit tunit) {
198 return BasicHttpClientConnectionManager.this.getConnection(
199 route, state);
200 }
201
202 };
203 }
204
205 private void closeConnection() {
206 if (this.conn != null) {
207 this.log.debug("Closing connection");
208 try {
209 this.conn.close();
210 } catch (final IOException iox) {
211 if (this.log.isDebugEnabled()) {
212 this.log.debug("I/O exception closing connection", iox);
213 }
214 }
215 this.conn = null;
216 }
217 }
218
219 private void shutdownConnection() {
220 if (this.conn != null) {
221 this.log.debug("Shutting down connection");
222 try {
223 this.conn.shutdown();
224 } catch (final IOException iox) {
225 if (this.log.isDebugEnabled()) {
226 this.log.debug("I/O exception shutting down connection", iox);
227 }
228 }
229 this.conn = null;
230 }
231 }
232
233 private void checkExpiry() {
234 if (this.conn != null && System.currentTimeMillis() >= this.expiry) {
235 if (this.log.isDebugEnabled()) {
236 this.log.debug("Connection expired @ " + new Date(this.expiry));
237 }
238 closeConnection();
239 }
240 }
241
242 synchronized HttpClientConnection getConnection(final HttpRoute route, final Object state) {
243 Asserts.check(!this.shutdown, "Connection manager has been shut down");
244 if (this.log.isDebugEnabled()) {
245 this.log.debug("Get connection for route " + route);
246 }
247 Asserts.check(!this.leased, "Connection is still allocated");
248 if (!LangUtils.equals(this.route, route) || !LangUtils.equals(this.state, state)) {
249 closeConnection();
250 }
251 this.route = route;
252 this.state = state;
253 checkExpiry();
254 if (this.conn == null) {
255 this.conn = this.connFactory.create(this.connConfig);
256 }
257 this.leased = true;
258 return this.conn;
259 }
260
261 public synchronized void releaseConnection(
262 final HttpClientConnection conn,
263 final Object state,
264 final long keepalive, final TimeUnit tunit) {
265 Args.notNull(conn, "Connection");
266 Asserts.check(conn == this.conn, "Connection not obtained from this manager");
267 if (this.log.isDebugEnabled()) {
268 this.log.debug("Releasing connection " + conn);
269 }
270 if (this.shutdown) {
271 shutdownConnection();
272 return;
273 }
274 try {
275 this.updated = System.currentTimeMillis();
276 if (!this.conn.isOpen()) {
277 this.conn = null;
278 this.route = null;
279 this.conn = null;
280 this.expiry = Long.MAX_VALUE;
281 } else {
282 this.state = state;
283 if (this.log.isDebugEnabled()) {
284 String s;
285 if (keepalive > 0) {
286 s = "for " + keepalive + " " + tunit;
287 } else {
288 s = "indefinitely";
289 }
290 this.log.debug("Connection can be kept alive " + s);
291 }
292 if (keepalive > 0) {
293 this.expiry = this.updated + tunit.toMillis(keepalive);
294 } else {
295 this.expiry = Long.MAX_VALUE;
296 }
297 }
298 } finally {
299 this.leased = false;
300 }
301 }
302
303 public void connect(
304 final HttpClientConnection conn,
305 final HttpRoute route,
306 final int connectTimeout,
307 final HttpContext context) throws IOException {
308 Args.notNull(conn, "Connection");
309 Args.notNull(route, "HTTP route");
310 Asserts.check(conn == this.conn, "Connection not obtained from this manager");
311 HttpHost host;
312 if (route.getProxyHost() != null) {
313 host = route.getProxyHost();
314 } else {
315 host = route.getTargetHost();
316 }
317 final InetSocketAddress localAddress = route.getLocalSocketAddress();
318 this.connectionOperator.connect(this.conn, host, localAddress,
319 connectTimeout, this.socketConfig, context);
320 }
321
322 public void upgrade(
323 final HttpClientConnection conn,
324 final HttpRoute route,
325 final HttpContext context) throws IOException {
326 Args.notNull(conn, "Connection");
327 Args.notNull(route, "HTTP route");
328 Asserts.check(conn == this.conn, "Connection not obtained from this manager");
329 this.connectionOperator.upgrade(this.conn, route.getTargetHost(), context);
330 }
331
332 public void routeComplete(
333 final HttpClientConnection conn,
334 final HttpRoute route,
335 final HttpContext context) throws IOException {
336 }
337
338 public synchronized void closeExpiredConnections() {
339 if (this.shutdown) {
340 return;
341 }
342 if (!this.leased) {
343 checkExpiry();
344 }
345 }
346
347 public synchronized void closeIdleConnections(final long idletime, final TimeUnit tunit) {
348 Args.notNull(tunit, "Time unit");
349 if (this.shutdown) {
350 return;
351 }
352 if (!this.leased) {
353 long time = tunit.toMillis(idletime);
354 if (time < 0) {
355 time = 0;
356 }
357 final long deadline = System.currentTimeMillis() - time;
358 if (this.updated <= deadline) {
359 closeConnection();
360 }
361 }
362 }
363
364 public synchronized void shutdown() {
365 if (this.shutdown) {
366 return;
367 }
368 this.shutdown = true;
369 shutdownConnection();
370 }
371
372 }