View Javadoc

1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with 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,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.http.impl.execchain;
29  
30  import java.io.IOException;
31  import java.io.InterruptedIOException;
32  import java.net.URI;
33  import java.util.concurrent.ExecutionException;
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.ConnectionReuseStrategy;
39  import org.apache.http.HttpClientConnection;
40  import org.apache.http.HttpEntity;
41  import org.apache.http.HttpException;
42  import org.apache.http.HttpHost;
43  import org.apache.http.HttpRequest;
44  import org.apache.http.HttpRequestInterceptor;
45  import org.apache.http.HttpResponse;
46  import org.apache.http.annotation.Immutable;
47  import org.apache.http.client.config.RequestConfig;
48  import org.apache.http.client.methods.CloseableHttpResponse;
49  import org.apache.http.client.methods.HttpExecutionAware;
50  import org.apache.http.client.methods.HttpRequestWrapper;
51  import org.apache.http.client.methods.HttpUriRequest;
52  import org.apache.http.client.protocol.HttpClientContext;
53  import org.apache.http.client.protocol.RequestClientConnControl;
54  import org.apache.http.conn.ConnectionKeepAliveStrategy;
55  import org.apache.http.conn.ConnectionRequest;
56  import org.apache.http.conn.HttpClientConnectionManager;
57  import org.apache.http.conn.routing.HttpRoute;
58  import org.apache.http.impl.conn.ConnectionShutdownException;
59  import org.apache.http.protocol.HttpProcessor;
60  import org.apache.http.protocol.HttpRequestExecutor;
61  import org.apache.http.protocol.ImmutableHttpProcessor;
62  import org.apache.http.protocol.RequestContent;
63  import org.apache.http.protocol.RequestTargetHost;
64  import org.apache.http.protocol.RequestUserAgent;
65  import org.apache.http.util.Args;
66  import org.apache.http.util.VersionInfo;
67  
68  /**
69   * @since 4.3
70   */
71  @Immutable
72  public class MinimalClientExec implements ClientExecChain {
73  
74      private final Log log = LogFactory.getLog(getClass());
75  
76      private final HttpRequestExecutor requestExecutor;
77      private final HttpClientConnectionManager connManager;
78      private final ConnectionReuseStrategy reuseStrategy;
79      private final ConnectionKeepAliveStrategy keepAliveStrategy;
80      private final HttpProcessor httpProcessor;
81  
82      public MinimalClientExec(
83              final HttpRequestExecutor requestExecutor,
84              final HttpClientConnectionManager connManager,
85              final ConnectionReuseStrategy reuseStrategy,
86              final ConnectionKeepAliveStrategy keepAliveStrategy) {
87          Args.notNull(requestExecutor, "HTTP request executor");
88          Args.notNull(connManager, "Client connection manager");
89          Args.notNull(reuseStrategy, "Connection reuse strategy");
90          Args.notNull(keepAliveStrategy, "Connection keep alive strategy");
91          this.httpProcessor = new ImmutableHttpProcessor(new HttpRequestInterceptor[] {
92                  new RequestContent(),
93                  new RequestTargetHost(),
94                  new RequestClientConnControl(),
95                  new RequestUserAgent(VersionInfo.getUserAgent(
96                          "Apache-HttpClient", "org.apache.http.client", getClass())),
97          } );
98          this.requestExecutor    = requestExecutor;
99          this.connManager        = connManager;
100         this.reuseStrategy      = reuseStrategy;
101         this.keepAliveStrategy  = keepAliveStrategy;
102     }
103 
104     public CloseableHttpResponse execute(
105             final HttpRoute route,
106             final HttpRequestWrapper request,
107             final HttpClientContext context,
108             final HttpExecutionAware execAware) throws IOException, HttpException {
109         Args.notNull(route, "HTTP route");
110         Args.notNull(request, "HTTP request");
111         Args.notNull(context, "HTTP context");
112 
113         final ConnectionRequest connRequest = connManager.requestConnection(route, null);
114         if (execAware != null) {
115             if (execAware.isAborted()) {
116                 connRequest.cancel();
117                 throw new RequestAbortedException("Request aborted");
118             } else {
119                 execAware.setCancellable(connRequest);
120             }
121         }
122 
123         final RequestConfig config = context.getRequestConfig();
124 
125         HttpClientConnection managedConn;
126         try {
127             final int timeout = config.getConnectionRequestTimeout();
128             managedConn = connRequest.get(timeout > 0 ? timeout : 0, TimeUnit.MILLISECONDS);
129         } catch(final InterruptedException interrupted) {
130             Thread.currentThread().interrupt();
131             throw new RequestAbortedException("Request aborted", interrupted);
132         } catch(final ExecutionException ex) {
133             Throwable cause = ex.getCause();
134             if (cause == null) {
135                 cause = ex;
136             }
137             throw new RequestAbortedException("Request execution failed", cause);
138         }
139 
140         final ConnectionHolder releaseTrigger = new ConnectionHolder(log, connManager, managedConn);
141         try {
142             if (execAware != null) {
143                 if (execAware.isAborted()) {
144                     releaseTrigger.close();
145                     throw new RequestAbortedException("Request aborted");
146                 } else {
147                     execAware.setCancellable(releaseTrigger);
148                 }
149             }
150 
151             if (!managedConn.isOpen()) {
152                 final int timeout = config.getConnectTimeout();
153                 this.connManager.connect(
154                     managedConn,
155                     route,
156                     timeout > 0 ? timeout : 0,
157                     context);
158                 this.connManager.routeComplete(managedConn, route, context);
159             } else {
160                 final int timeout = config.getSocketTimeout();
161                 if (timeout >= 0) {
162                     managedConn.setSocketTimeout(timeout);
163                 }
164             }
165 
166             HttpHost target = null;
167             final HttpRequest original = request.getOriginal();
168             if (original instanceof HttpUriRequest) {
169                 final URI uri = ((HttpUriRequest) original).getURI();
170                 if (uri.isAbsolute()) {
171                     target = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
172                 }
173             }
174             if (target == null) {
175                 target = route.getTargetHost();
176             }
177 
178             context.setAttribute(HttpClientContext.HTTP_TARGET_HOST, target);
179             context.setAttribute(HttpClientContext.HTTP_REQUEST, request);
180             context.setAttribute(HttpClientContext.HTTP_CONNECTION, managedConn);
181             context.setAttribute(HttpClientContext.HTTP_ROUTE, route);
182 
183             httpProcessor.process(request, context);
184             final HttpResponse response = requestExecutor.execute(request, managedConn, context);
185             httpProcessor.process(response, context);
186 
187             // The connection is in or can be brought to a re-usable state.
188             if (reuseStrategy.keepAlive(response, context)) {
189                 // Set the idle duration of this connection
190                 final long duration = keepAliveStrategy.getKeepAliveDuration(response, context);
191                 releaseTrigger.setValidFor(duration, TimeUnit.MILLISECONDS);
192                 releaseTrigger.markReusable();
193             } else {
194                 releaseTrigger.markNonReusable();
195             }
196 
197             // check for entity, release connection if possible
198             final HttpEntity entity = response.getEntity();
199             if (entity == null || !entity.isStreaming()) {
200                 // connection not needed and (assumed to be) in re-usable state
201                 releaseTrigger.releaseConnection();
202                 return Proxies.enhanceResponse(response, null);
203             } else {
204                 return Proxies.enhanceResponse(response, releaseTrigger);
205             }
206         } catch (final ConnectionShutdownException ex) {
207             final InterruptedIOException ioex = new InterruptedIOException(
208                     "Connection has been shut down");
209             ioex.initCause(ex);
210             throw ioex;
211         } catch (final HttpException ex) {
212             releaseTrigger.abortConnection();
213             throw ex;
214         } catch (final IOException ex) {
215             releaseTrigger.abortConnection();
216             throw ex;
217         } catch (final RuntimeException ex) {
218             releaseTrigger.abortConnection();
219             throw ex;
220         }
221     }
222 
223 }