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     public void start() throws HttpException, IOException {
122         final HttpHost target = this.requestProducer.getTarget();
123         final HttpRequest original = this.requestProducer.generateRequest();
124 
125         if (original instanceof HttpExecutionAware) {
126             ((HttpExecutionAware) original).setCancellable(this);
127         }
128         this.exec.prepare(target, original, this.state, this);
129         requestConnection();
130     }
131 
132     @Override
133     public HttpRequest generateRequest() throws IOException, HttpException {
134         return this.exec.generateRequest(this.state, this);
135     }
136 
137     @Override
138     public void produceContent(
139             final ContentEncoder encoder, final IOControl ioctrl) throws IOException {
140         this.exec.produceContent(this.state, encoder, ioctrl);
141     }
142 
143     @Override
144     public void requestCompleted() {
145         this.exec.requestCompleted(this.state, this);
146     }
147 
148     @Override
149     public void responseReceived(
150             final HttpResponse response) throws IOException, HttpException {
151         this.exec.responseReceived(response, this.state, this);
152     }
153 
154     @Override
155     public void consumeContent(
156             final ContentDecoder decoder, final IOControl ioctrl) throws IOException {
157         this.exec.consumeContent(this.state, decoder, ioctrl);
158         if (!decoder.isCompleted() && this.responseConsumer.isDone()) {
159             markConnectionNonReusable();
160             try {
161                 markCompleted();
162                 releaseConnection();
163                 this.resultFuture.cancel();
164             } finally {
165                 close();
166             }
167         }
168     }
169 
170     @Override
171     public void responseCompleted() throws IOException, HttpException {
172         this.exec.responseCompleted(this.state, this);
173 
174         if (this.state.getFinalResponse() != null || this.resultFuture.isDone()) {
175             try {
176                 markCompleted();
177                 releaseConnection();
178                 final T result = this.responseConsumer.getResult();
179                 final Exception ex = this.responseConsumer.getException();
180                 if (ex == null) {
181                     this.resultFuture.completed(result);
182                 } else {
183                     this.resultFuture.failed(ex);
184                 }
185             } finally {
186                 close();
187             }
188         } else {
189             NHttpClientConnection localConn = getConnection();
190             if (localConn != null && !localConn.isOpen()) {
191                 releaseConnection();
192                 localConn = null;
193             }
194             if (localConn != null) {
195                 localConn.requestOutput();
196             } else {
197                 requestConnection();
198             }
199         }
200     }
201 
202     @Override
203     public void inputTerminated() {
204         if (!isCompleted()) {
205             requestConnection();
206         } else {
207             close();
208         }
209     }
210 
211     public void abortConnection() {
212         discardConnection();
213     }
214 
215 }