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  package org.apache.http.impl.nio.client;
28  
29  import java.io.IOException;
30  
31  import org.apache.commons.logging.Log;
32  import org.apache.http.ConnectionReuseStrategy;
33  import org.apache.http.HttpException;
34  import org.apache.http.HttpHost;
35  import org.apache.http.HttpRequest;
36  import org.apache.http.HttpResponse;
37  import org.apache.http.client.methods.HttpExecutionAware;
38  import org.apache.http.client.protocol.HttpClientContext;
39  import org.apache.http.concurrent.BasicFuture;
40  import org.apache.http.conn.ConnectionKeepAliveStrategy;
41  import org.apache.http.nio.ContentDecoder;
42  import org.apache.http.nio.ContentEncoder;
43  import org.apache.http.nio.IOControl;
44  import org.apache.http.nio.NHttpClientConnection;
45  import org.apache.http.nio.conn.NHttpClientConnectionManager;
46  import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
47  import org.apache.http.nio.protocol.HttpAsyncResponseConsumer;
48  
49  /**
50   * Default implementation of {@link org.apache.http.nio.protocol.HttpAsyncClientExchangeHandler}.
51   * <p>
52   * Instances of this class are expected to be accessed by one thread at a time only.
53   * The {@link #cancel()} method can be called concurrently by multiple threads.
54   */
55  class DefaultClientExchangeHandlerImpl<T> extends AbstractClientExchangeHandler {
56  
57      private final HttpAsyncRequestProducer requestProducer;
58      private final HttpAsyncResponseConsumer<T> responseConsumer;
59      private final BasicFuture<T> resultFuture;
60      private final InternalClientExec exec;
61      private final InternalState state;
62  
63      public DefaultClientExchangeHandlerImpl(
64              final Log log,
65              final HttpAsyncRequestProducer requestProducer,
66              final HttpAsyncResponseConsumer<T> responseConsumer,
67              final HttpClientContext localContext,
68              final BasicFuture<T> resultFuture,
69              final NHttpClientConnectionManager connmgr,
70              final ConnectionReuseStrategy connReuseStrategy,
71              final ConnectionKeepAliveStrategy keepaliveStrategy,
72              final InternalClientExec exec) {
73          super(log, localContext, connmgr, connReuseStrategy, keepaliveStrategy);
74          this.requestProducer = requestProducer;
75          this.responseConsumer = responseConsumer;
76          this.resultFuture = resultFuture;
77          this.exec = exec;
78          this.state = new InternalState(getId(), requestProducer, responseConsumer, localContext);
79      }
80  
81      @Override
82      void releaseResources() {
83          try {
84              this.requestProducer.close();
85          } catch (final IOException ex) {
86              this.log.debug("I/O error closing request producer", ex);
87          }
88          try {
89              this.responseConsumer.close();
90          } catch (final IOException ex) {
91              this.log.debug("I/O error closing response consumer", ex);
92          }
93      }
94  
95      @Override
96      void executionFailed(final Exception ex) {
97          try {
98              this.requestProducer.failed(ex);
99              this.responseConsumer.failed(ex);
100         } finally {
101             this.resultFuture.failed(ex);
102         }
103     }
104 
105     @Override
106     boolean executionCancelled() {
107         final boolean cancelled = this.responseConsumer.cancel();
108 
109         final T result = this.responseConsumer.getResult();
110         final Exception ex = this.responseConsumer.getException();
111         if (ex != null) {
112             this.resultFuture.failed(ex);
113         } else if (result != null) {
114             this.resultFuture.completed(result);
115         } else {
116             this.resultFuture.cancel();
117         }
118         return cancelled;
119     }
120 
121     @Override
122     public void start() throws HttpException, IOException {
123         final HttpHost target = this.requestProducer.getTarget();
124         final HttpRequest original = this.requestProducer.generateRequest();
125 
126         if (original instanceof HttpExecutionAware) {
127             ((HttpExecutionAware) original).setCancellable(this);
128         }
129         this.exec.prepare(target, original, this.state, this);
130         requestConnection();
131     }
132 
133     @Override
134     public HttpRequest generateRequest() throws IOException, HttpException {
135         return this.exec.generateRequest(this.state, this);
136     }
137 
138     @Override
139     public void produceContent(
140             final ContentEncoder encoder, final IOControl ioControl) throws IOException {
141         this.exec.produceContent(this.state, encoder, ioControl);
142     }
143 
144     @Override
145     public void requestCompleted() {
146         this.exec.requestCompleted(this.state, this);
147     }
148 
149     @Override
150     public void responseReceived(
151             final HttpResponse response) throws IOException, HttpException {
152         this.exec.responseReceived(response, this.state, this);
153     }
154 
155     @Override
156     public void consumeContent(
157             final ContentDecoder decoder, final IOControl ioControl) throws IOException {
158         this.exec.consumeContent(this.state, decoder, ioControl);
159         if (!decoder.isCompleted() && this.responseConsumer.isDone()) {
160             markConnectionNonReusable();
161             try {
162                 markCompleted();
163                 releaseConnection();
164                 this.resultFuture.cancel();
165             } finally {
166                 close();
167             }
168         }
169     }
170 
171     @Override
172     public void responseCompleted() throws IOException, HttpException {
173         this.exec.responseCompleted(this.state, this);
174 
175         if (this.state.getFinalResponse() != null || this.resultFuture.isDone()) {
176             try {
177                 markCompleted();
178                 releaseConnection();
179                 final T result = this.responseConsumer.getResult();
180                 final Exception ex = this.responseConsumer.getException();
181                 if (ex == null) {
182                     this.resultFuture.completed(result);
183                 } else {
184                     this.resultFuture.failed(ex);
185                 }
186             } finally {
187                 close();
188             }
189         } else {
190             NHttpClientConnection localConn = getConnection();
191             if (localConn != null && !localConn.isOpen()) {
192                 releaseConnection();
193                 localConn = null;
194             }
195             if (localConn != null) {
196                 localConn.requestOutput();
197             } else {
198                 requestConnection();
199             }
200         }
201     }
202 
203     @Override
204     public void inputTerminated() {
205         if (!isCompleted()) {
206             requestConnection();
207         } else {
208             close();
209         }
210     }
211 
212     public void abortConnection() {
213         discardConnection();
214     }
215 
216 }