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