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;
29  
30  import java.io.IOException;
31  import java.net.SocketTimeoutException;
32  
33  import org.apache.http.HttpClientConnection;
34  import org.apache.http.HttpConnectionMetrics;
35  import org.apache.http.HttpEntity;
36  import org.apache.http.HttpEntityEnclosingRequest;
37  import org.apache.http.HttpException;
38  import org.apache.http.HttpRequest;
39  import org.apache.http.HttpResponse;
40  import org.apache.http.HttpResponseFactory;
41  import org.apache.http.annotation.NotThreadSafe;
42  import org.apache.http.entity.ContentLengthStrategy;
43  import org.apache.http.impl.entity.EntityDeserializer;
44  import org.apache.http.impl.entity.EntitySerializer;
45  import org.apache.http.impl.entity.LaxContentLengthStrategy;
46  import org.apache.http.impl.entity.StrictContentLengthStrategy;
47  import org.apache.http.impl.io.DefaultHttpResponseParser;
48  import org.apache.http.impl.io.HttpRequestWriter;
49  import org.apache.http.io.EofSensor;
50  import org.apache.http.io.HttpMessageParser;
51  import org.apache.http.io.HttpMessageWriter;
52  import org.apache.http.io.HttpTransportMetrics;
53  import org.apache.http.io.SessionInputBuffer;
54  import org.apache.http.io.SessionOutputBuffer;
55  import org.apache.http.message.LineFormatter;
56  import org.apache.http.message.LineParser;
57  import org.apache.http.params.HttpParams;
58  
59  /**
60   * Abstract client-side HTTP connection capable of transmitting and receiving
61   * data using arbitrary {@link SessionInputBuffer} and
62   * {@link SessionOutputBuffer} implementations.
63   * <p>
64   * The following parameters can be used to customize the behavior of this
65   * class:
66   * <ul>
67   *  <li>{@link org.apache.http.params.CoreProtocolPNames#STRICT_TRANSFER_ENCODING}</li>
68   *  <li>{@link org.apache.http.params.CoreConnectionPNames#MAX_HEADER_COUNT}</li>
69   *  <li>{@link org.apache.http.params.CoreConnectionPNames#MAX_LINE_LENGTH}</li>
70   * </ul>
71   *
72   * @since 4.0
73   */
74  @NotThreadSafe
75  public abstract class AbstractHttpClientConnection implements HttpClientConnection {
76  
77      private final EntitySerializer entityserializer;
78      private final EntityDeserializer entitydeserializer;
79  
80      private SessionInputBuffer inbuffer = null;
81      private SessionOutputBuffer outbuffer = null;
82      private EofSensor eofSensor = null;
83      private HttpMessageParser<HttpResponse> responseParser = null;
84      private HttpMessageWriter<HttpRequest> requestWriter = null;
85      private HttpConnectionMetricsImpl metrics = null;
86  
87      /**
88       * Creates an instance of this class.
89       * <p>
90       * This constructor will invoke {@link #createEntityDeserializer()}
91       * and {@link #createEntitySerializer()} methods in order to initialize
92       * HTTP entity serializer and deserializer implementations for this
93       * connection.
94       */
95      public AbstractHttpClientConnection() {
96          super();
97          this.entityserializer = createEntitySerializer();
98          this.entitydeserializer = createEntityDeserializer();
99      }
100 
101     /**
102      * Asserts if the connection is open.
103      *
104      * @throws IllegalStateException if the connection is not open.
105      */
106     protected abstract void assertOpen() throws IllegalStateException;
107 
108     /**
109      * Creates an instance of {@link EntityDeserializer} with the
110      * {@link LaxContentLengthStrategy} implementation to be used for
111      * de-serializing entities received over this connection.
112      * <p>
113      * This method can be overridden in a super class in order to create
114      * instances of {@link EntityDeserializer} using a custom
115      * {@link ContentLengthStrategy}.
116      *
117      * @return HTTP entity deserializer
118      */
119     protected EntityDeserializer createEntityDeserializer() {
120         return new EntityDeserializer(new LaxContentLengthStrategy());
121     }
122 
123     /**
124      * Creates an instance of {@link EntitySerializer} with the
125      * {@link StrictContentLengthStrategy} implementation to be used for
126      * serializing HTTP entities sent over this connection.
127      * <p>
128      * This method can be overridden in a super class in order to create
129      * instances of {@link EntitySerializer} using a custom
130      * {@link ContentLengthStrategy}.
131      *
132      * @return HTTP entity serialzier.
133      */
134     protected EntitySerializer createEntitySerializer() {
135         return new EntitySerializer(new StrictContentLengthStrategy());
136     }
137 
138     /**
139      * Creates an instance of {@link DefaultHttpResponseFactory} to be used
140      * for creating {@link HttpResponse} objects received by over this
141      * connection.
142      * <p>
143      * This method can be overridden in a super class in order to provide
144      * a different implementation of the {@link HttpResponseFactory} interface.
145      *
146      * @return HTTP response factory.
147      */
148     protected HttpResponseFactory createHttpResponseFactory() {
149         return new DefaultHttpResponseFactory();
150     }
151 
152     /**
153      * Creates an instance of {@link HttpMessageParser} to be used for parsing
154      * HTTP responses received over this connection.
155      * <p>
156      * This method can be overridden in a super class in order to provide
157      * a different implementation of the {@link HttpMessageParser} interface or
158      * to pass a different implementation of the {@link LineParser} to the
159      * the {@link DefaultHttpResponseParser} constructor.
160      *
161      * @param buffer the session input buffer.
162      * @param responseFactory the HTTP response factory.
163      * @param params HTTP parameters.
164      * @return HTTP message parser.
165      */
166     protected HttpMessageParser<HttpResponse> createResponseParser(
167             final SessionInputBuffer buffer,
168             final HttpResponseFactory responseFactory,
169             final HttpParams params) {
170         return new DefaultHttpResponseParser(buffer, null, responseFactory, params);
171     }
172 
173     /**
174      * Creates an instance of {@link HttpMessageWriter} to be used for
175      * writing out HTTP requests sent over this connection.
176      * <p>
177      * This method can be overridden in a super class in order to provide
178      * a different implementation of the {@link HttpMessageWriter} interface or
179      * to pass a different implementation of {@link LineFormatter} to the
180      * the default implementation {@link HttpRequestWriter}.
181      *
182      * @param buffer the session output buffer
183      * @param params HTTP parameters
184      * @return HTTP message writer
185      */
186     protected HttpMessageWriter<HttpRequest> createRequestWriter(
187             final SessionOutputBuffer buffer,
188             final HttpParams params) {
189         return new HttpRequestWriter(buffer, null, params);
190     }
191 
192     /**
193      * @since 4.1
194      */
195     protected HttpConnectionMetricsImpl createConnectionMetrics(
196             final HttpTransportMetrics inTransportMetric,
197             final HttpTransportMetrics outTransportMetric) {
198         return new HttpConnectionMetricsImpl(inTransportMetric, outTransportMetric);
199     }
200 
201     /**
202      * Initializes this connection object with {@link SessionInputBuffer} and
203      * {@link SessionOutputBuffer} instances to be used for sending and
204      * receiving data. These session buffers can be bound to any arbitrary
205      * physical output medium.
206      * <p>
207      * This method will invoke {@link #createHttpResponseFactory()},
208      * {@link #createRequestWriter(SessionOutputBuffer, HttpParams)}
209      * and {@link #createResponseParser(SessionInputBuffer, HttpResponseFactory, HttpParams)}
210      * methods to initialize HTTP request writer and response parser for this
211      * connection.
212      *
213      * @param inbuffer the session input buffer.
214      * @param outbuffer the session output buffer.
215      * @param params HTTP parameters.
216      */
217     protected void init(
218             final SessionInputBuffer inbuffer,
219             final SessionOutputBuffer outbuffer,
220             final HttpParams params) {
221         if (inbuffer == null) {
222             throw new IllegalArgumentException("Input session buffer may not be null");
223         }
224         if (outbuffer == null) {
225             throw new IllegalArgumentException("Output session buffer may not be null");
226         }
227         this.inbuffer = inbuffer;
228         this.outbuffer = outbuffer;
229         if (inbuffer instanceof EofSensor) {
230             this.eofSensor = (EofSensor) inbuffer;
231         }
232         this.responseParser = createResponseParser(
233                 inbuffer,
234                 createHttpResponseFactory(),
235                 params);
236         this.requestWriter = createRequestWriter(
237                 outbuffer, params);
238         this.metrics = createConnectionMetrics(
239                 inbuffer.getMetrics(),
240                 outbuffer.getMetrics());
241     }
242 
243     public boolean isResponseAvailable(int timeout) throws IOException {
244         assertOpen();
245         try {
246             return this.inbuffer.isDataAvailable(timeout);
247         } catch (SocketTimeoutException ex) {
248             return false;
249         }
250     }
251 
252     public void sendRequestHeader(final HttpRequest request)
253             throws HttpException, IOException {
254         if (request == null) {
255             throw new IllegalArgumentException("HTTP request may not be null");
256         }
257         assertOpen();
258         this.requestWriter.write(request);
259         this.metrics.incrementRequestCount();
260     }
261 
262     public void sendRequestEntity(final HttpEntityEnclosingRequest request)
263             throws HttpException, IOException {
264         if (request == null) {
265             throw new IllegalArgumentException("HTTP request may not be null");
266         }
267         assertOpen();
268         if (request.getEntity() == null) {
269             return;
270         }
271         this.entityserializer.serialize(
272                 this.outbuffer,
273                 request,
274                 request.getEntity());
275     }
276 
277     protected void doFlush() throws IOException {
278         this.outbuffer.flush();
279     }
280 
281     public void flush() throws IOException {
282         assertOpen();
283         doFlush();
284     }
285 
286     public HttpResponse receiveResponseHeader()
287             throws HttpException, IOException {
288         assertOpen();
289         HttpResponse response = this.responseParser.parse();
290         if (response.getStatusLine().getStatusCode() >= 200) {
291             this.metrics.incrementResponseCount();
292         }
293         return response;
294     }
295 
296     public void receiveResponseEntity(final HttpResponse response)
297             throws HttpException, IOException {
298         if (response == null) {
299             throw new IllegalArgumentException("HTTP response may not be null");
300         }
301         assertOpen();
302         HttpEntity entity = this.entitydeserializer.deserialize(this.inbuffer, response);
303         response.setEntity(entity);
304     }
305 
306     protected boolean isEof() {
307         return this.eofSensor != null && this.eofSensor.isEof();
308     }
309 
310     public boolean isStale() {
311         if (!isOpen()) {
312             return true;
313         }
314         if (isEof()) {
315             return true;
316         }
317         try {
318             this.inbuffer.isDataAvailable(1);
319             return isEof();
320         } catch (SocketTimeoutException ex) {
321             return false;
322         } catch (IOException ex) {
323             return true;
324         }
325     }
326 
327     public HttpConnectionMetrics getMetrics() {
328         return this.metrics;
329     }
330 
331 }