View Javadoc

1   /*
2    * ====================================================================
3    *
4    *  Licensed to the Apache Software Foundation (ASF) under one or more
5    *  contributor license agreements.  See the NOTICE file distributed with
6    *  this work for additional information regarding copyright ownership.
7    *  The ASF licenses this file to You under the Apache License, Version 2.0
8    *  (the "License"); you may not use this file except in compliance with
9    *  the License.  You may obtain a copy of the License at
10   *
11   *      http://www.apache.org/licenses/LICENSE-2.0
12   *
13   *  Unless required by applicable law or agreed to in writing, software
14   *  distributed under the License is distributed on an "AS IS" BASIS,
15   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   *  See the License for the specific language governing permissions and
17   *  limitations under the License.
18   * ====================================================================
19   *
20   * This software consists of voluntary contributions made by many
21   * individuals on behalf of the Apache Software Foundation.  For more
22   * information on the Apache Software Foundation, please see
23   * <http://www.apache.org/>.
24   *
25   */
26  
27  package org.apache.http.impl.conn;
28  
29  import java.io.IOException;
30  import java.io.InterruptedIOException;
31  import java.net.InetAddress;
32  import java.net.Socket;
33  import java.util.concurrent.TimeUnit;
34  
35  import javax.net.ssl.SSLSession;
36  import javax.net.ssl.SSLSocket;
37  
38  import org.apache.http.HttpConnectionMetrics;
39  import org.apache.http.HttpEntityEnclosingRequest;
40  import org.apache.http.HttpException;
41  import org.apache.http.HttpHost;
42  import org.apache.http.HttpRequest;
43  import org.apache.http.HttpResponse;
44  import org.apache.http.annotation.NotThreadSafe;
45  import org.apache.http.conn.ClientConnectionManager;
46  import org.apache.http.conn.ClientConnectionOperator;
47  import org.apache.http.conn.ManagedClientConnection;
48  import org.apache.http.conn.OperatedClientConnection;
49  import org.apache.http.conn.routing.HttpRoute;
50  import org.apache.http.conn.routing.RouteTracker;
51  import org.apache.http.params.HttpParams;
52  import org.apache.http.protocol.HttpContext;
53  
54  @NotThreadSafe
55  class ManagedClientConnectionImpl implements ManagedClientConnection {
56  
57      private final ClientConnectionManager manager;
58      private final ClientConnectionOperator operator;
59      private volatile HttpPoolEntry poolEntry;
60      private volatile boolean reusable;
61      private volatile long duration;
62  
63      ManagedClientConnectionImpl(
64              final ClientConnectionManager manager,
65              final ClientConnectionOperator operator,
66              final HttpPoolEntry entry) {
67          super();
68          if (manager == null) {
69              throw new IllegalArgumentException("Connection manager may not be null");
70          }
71          if (operator == null) {
72              throw new IllegalArgumentException("Connection operator may not be null");
73          }
74          if (entry == null) {
75              throw new IllegalArgumentException("HTTP pool entry may not be null");
76          }
77          this.manager = manager;
78          this.operator = operator;
79          this.poolEntry = entry;
80          this.reusable = false;
81          this.duration = Long.MAX_VALUE;
82      }
83  
84      HttpPoolEntry getPoolEntry() {
85          return this.poolEntry;
86      }
87  
88      HttpPoolEntry detach() {
89          HttpPoolEntry local = this.poolEntry;
90          this.poolEntry = null;
91          return local;
92      }
93  
94      public ClientConnectionManager getManager() {
95          return this.manager;
96      }
97  
98      private OperatedClientConnection getConnection() {
99          HttpPoolEntry local = this.poolEntry;
100         if (local == null) {
101             return null;
102         }
103         return local.getConnection();
104     }
105 
106     private OperatedClientConnection ensureConnection() {
107         HttpPoolEntry local = this.poolEntry;
108         if (local == null) {
109             throw new ConnectionShutdownException();
110         }
111         return local.getConnection();
112     }
113 
114     private HttpPoolEntry ensurePoolEntry() {
115         HttpPoolEntry local = this.poolEntry;
116         if (local == null) {
117             throw new ConnectionShutdownException();
118         }
119         return local;
120     }
121 
122     public void close() throws IOException {
123         HttpPoolEntry local = this.poolEntry;
124         if (local != null) {
125             OperatedClientConnection conn = local.getConnection();
126             local.getTracker().reset();
127             conn.close();
128         }
129     }
130 
131     public void shutdown() throws IOException {
132         HttpPoolEntry local = this.poolEntry;
133         if (local != null) {
134             OperatedClientConnection conn = local.getConnection();
135             local.getTracker().reset();
136             conn.shutdown();
137         }
138     }
139 
140     public boolean isOpen() {
141         OperatedClientConnection conn = getConnection();
142         if (conn != null) {
143             return conn.isOpen();
144         } else {
145             return false;
146         }
147     }
148 
149     public boolean isStale() {
150         OperatedClientConnection conn = getConnection();
151         if (conn != null) {
152             return conn.isStale();
153         } else {
154             return true;
155         }
156     }
157 
158     public void setSocketTimeout(int timeout) {
159         OperatedClientConnection conn = ensureConnection();
160         conn.setSocketTimeout(timeout);
161     }
162 
163     public int getSocketTimeout() {
164         OperatedClientConnection conn = ensureConnection();
165         return conn.getSocketTimeout();
166     }
167 
168     public HttpConnectionMetrics getMetrics() {
169         OperatedClientConnection conn = ensureConnection();
170         return conn.getMetrics();
171     }
172 
173     public void flush() throws IOException {
174         OperatedClientConnection conn = ensureConnection();
175         conn.flush();
176     }
177 
178     public boolean isResponseAvailable(int timeout) throws IOException {
179         OperatedClientConnection conn = ensureConnection();
180         return conn.isResponseAvailable(timeout);
181     }
182 
183     public void receiveResponseEntity(
184             final HttpResponse response) throws HttpException, IOException {
185         OperatedClientConnection conn = ensureConnection();
186         conn.receiveResponseEntity(response);
187     }
188 
189     public HttpResponse receiveResponseHeader() throws HttpException, IOException {
190         OperatedClientConnection conn = ensureConnection();
191         return conn.receiveResponseHeader();
192     }
193 
194     public void sendRequestEntity(
195             final HttpEntityEnclosingRequest request) throws HttpException, IOException {
196         OperatedClientConnection conn = ensureConnection();
197         conn.sendRequestEntity(request);
198     }
199 
200     public void sendRequestHeader(
201             final HttpRequest request) throws HttpException, IOException {
202         OperatedClientConnection conn = ensureConnection();
203         conn.sendRequestHeader(request);
204     }
205 
206     public InetAddress getLocalAddress() {
207         OperatedClientConnection conn = ensureConnection();
208         return conn.getLocalAddress();
209     }
210 
211     public int getLocalPort() {
212         OperatedClientConnection conn = ensureConnection();
213         return conn.getLocalPort();
214     }
215 
216     public InetAddress getRemoteAddress() {
217         OperatedClientConnection conn = ensureConnection();
218         return conn.getRemoteAddress();
219     }
220 
221     public int getRemotePort() {
222         OperatedClientConnection conn = ensureConnection();
223         return conn.getRemotePort();
224     }
225 
226     public boolean isSecure() {
227         OperatedClientConnection conn = ensureConnection();
228         return conn.isSecure();
229     }
230 
231     public SSLSession getSSLSession() {
232         OperatedClientConnection conn = ensureConnection();
233         SSLSession result = null;
234         Socket sock = conn.getSocket();
235         if (sock instanceof SSLSocket) {
236             result = ((SSLSocket)sock).getSession();
237         }
238         return result;
239     }
240 
241     public Object getAttribute(final String id) {
242         OperatedClientConnection conn = ensureConnection();
243         if (conn instanceof HttpContext) {
244             return ((HttpContext) conn).getAttribute(id);
245         } else {
246             return null;
247         }
248     }
249 
250     public Object removeAttribute(final String id) {
251         OperatedClientConnection conn = ensureConnection();
252         if (conn instanceof HttpContext) {
253             return ((HttpContext) conn).removeAttribute(id);
254         } else {
255             return null;
256         }
257     }
258 
259     public void setAttribute(final String id, final Object obj) {
260         OperatedClientConnection conn = ensureConnection();
261         if (conn instanceof HttpContext) {
262             ((HttpContext) conn).setAttribute(id, obj);
263         }
264     }
265 
266     public HttpRoute getRoute() {
267         HttpPoolEntry local = ensurePoolEntry();
268         return local.getEffectiveRoute();
269     }
270 
271     public void open(
272             final HttpRoute route,
273             final HttpContext context,
274             final HttpParams params) throws IOException {
275         if (route == null) {
276             throw new IllegalArgumentException("Route may not be null");
277         }
278         if (params == null) {
279             throw new IllegalArgumentException("HTTP parameters may not be null");
280         }
281         OperatedClientConnection conn;
282         synchronized (this) {
283             if (this.poolEntry == null) {
284                 throw new ConnectionShutdownException();
285             }
286             RouteTracker tracker = this.poolEntry.getTracker();
287             if (tracker.isConnected()) {
288                 throw new IllegalStateException("Connection already open");
289             }
290             conn = this.poolEntry.getConnection();
291         }
292 
293         HttpHost proxy  = route.getProxyHost();
294         this.operator.openConnection(
295                 conn,
296                 (proxy != null) ? proxy : route.getTargetHost(),
297                 route.getLocalAddress(),
298                 context, params);
299 
300         synchronized (this) {
301             if (this.poolEntry == null) {
302                 throw new InterruptedIOException();
303             }
304             RouteTracker tracker = this.poolEntry.getTracker();
305             if (proxy == null) {
306                 tracker.connectTarget(conn.isSecure());
307             } else {
308                 tracker.connectProxy(proxy, conn.isSecure());
309             }
310         }
311     }
312 
313     public void tunnelTarget(
314             boolean secure, final HttpParams params) throws IOException {
315         if (params == null) {
316             throw new IllegalArgumentException("HTTP parameters may not be null");
317         }
318         HttpHost target;
319         OperatedClientConnection conn;
320         synchronized (this) {
321             if (this.poolEntry == null) {
322                 throw new ConnectionShutdownException();
323             }
324             RouteTracker tracker = this.poolEntry.getTracker();
325             if (!tracker.isConnected()) {
326                 throw new IllegalStateException("Connection not open");
327             }
328             if (tracker.isTunnelled()) {
329                 throw new IllegalStateException("Connection is already tunnelled");
330             }
331             target = tracker.getTargetHost();
332             conn = this.poolEntry.getConnection();
333         }
334 
335         conn.update(null, target, secure, params);
336 
337         synchronized (this) {
338             if (this.poolEntry == null) {
339                 throw new InterruptedIOException();
340             }
341             RouteTracker tracker = this.poolEntry.getTracker();
342             tracker.tunnelTarget(secure);
343         }
344     }
345 
346     public void tunnelProxy(
347             final HttpHost next, boolean secure, final HttpParams params) throws IOException {
348         if (next == null) {
349             throw new IllegalArgumentException("Next proxy amy not be null");
350         }
351         if (params == null) {
352             throw new IllegalArgumentException("HTTP parameters may not be null");
353         }
354         OperatedClientConnection conn;
355         synchronized (this) {
356             if (this.poolEntry == null) {
357                 throw new ConnectionShutdownException();
358             }
359             RouteTracker tracker = this.poolEntry.getTracker();
360             if (!tracker.isConnected()) {
361                 throw new IllegalStateException("Connection not open");
362             }
363             conn = this.poolEntry.getConnection();
364         }
365 
366         conn.update(null, next, secure, params);
367 
368         synchronized (this) {
369             if (this.poolEntry == null) {
370                 throw new InterruptedIOException();
371             }
372             RouteTracker tracker = this.poolEntry.getTracker();
373             tracker.tunnelProxy(next, secure);
374         }
375     }
376 
377     public void layerProtocol(
378             final HttpContext context, final HttpParams params) throws IOException {
379         if (params == null) {
380             throw new IllegalArgumentException("HTTP parameters may not be null");
381         }
382         HttpHost target;
383         OperatedClientConnection conn;
384         synchronized (this) {
385             if (this.poolEntry == null) {
386                 throw new ConnectionShutdownException();
387             }
388             RouteTracker tracker = this.poolEntry.getTracker();
389             if (!tracker.isConnected()) {
390                 throw new IllegalStateException("Connection not open");
391             }
392             if (!tracker.isTunnelled()) {
393                 throw new IllegalStateException("Protocol layering without a tunnel not supported");
394             }
395             if (tracker.isLayered()) {
396                 throw new IllegalStateException("Multiple protocol layering not supported");
397             }
398             target = tracker.getTargetHost();
399             conn = this.poolEntry.getConnection();
400         }
401         this.operator.updateSecureConnection(conn, target, context, params);
402 
403         synchronized (this) {
404             if (this.poolEntry == null) {
405                 throw new InterruptedIOException();
406             }
407             RouteTracker tracker = this.poolEntry.getTracker();
408             tracker.layerProtocol(conn.isSecure());
409         }
410     }
411 
412     public Object getState() {
413         HttpPoolEntry local = ensurePoolEntry();
414         return local.getState();
415     }
416 
417     public void setState(final Object state) {
418         HttpPoolEntry local = ensurePoolEntry();
419         local.setState(state);
420     }
421 
422     public void markReusable() {
423         this.reusable = true;
424     }
425 
426     public void unmarkReusable() {
427         this.reusable = false;
428     }
429 
430     public boolean isMarkedReusable() {
431         return this.reusable;
432     }
433 
434     public void setIdleDuration(long duration, TimeUnit unit) {
435         if(duration > 0) {
436             this.duration = unit.toMillis(duration);
437         } else {
438             this.duration = -1;
439         }
440     }
441 
442     public void releaseConnection() {
443         synchronized (this) {
444             if (this.poolEntry == null) {
445                 return;
446             }
447             this.manager.releaseConnection(this, this.duration, TimeUnit.MILLISECONDS);
448             this.poolEntry = null;
449         }
450     }
451 
452     public void abortConnection() {
453         synchronized (this) {
454             if (this.poolEntry == null) {
455                 return;
456             }
457             this.reusable = false;
458             OperatedClientConnection conn = this.poolEntry.getConnection();
459             try {
460                 conn.shutdown();
461             } catch (IOException ignore) {
462             }
463             this.manager.releaseConnection(this, this.duration, TimeUnit.MILLISECONDS);
464             this.poolEntry = null;
465         }
466     }
467 
468 }