1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.http.impl;
29
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.OutputStream;
33 import java.net.InetAddress;
34 import java.net.Socket;
35 import java.net.SocketAddress;
36 import java.net.SocketException;
37 import java.net.SocketTimeoutException;
38 import java.nio.charset.CharsetDecoder;
39 import java.nio.charset.CharsetEncoder;
40
41 import org.apache.http.Header;
42 import org.apache.http.HttpConnection;
43 import org.apache.http.HttpConnectionMetrics;
44 import org.apache.http.HttpEntity;
45 import org.apache.http.HttpException;
46 import org.apache.http.HttpInetConnection;
47 import org.apache.http.HttpMessage;
48 import org.apache.http.annotation.NotThreadSafe;
49 import org.apache.http.config.MessageConstraints;
50 import org.apache.http.entity.BasicHttpEntity;
51 import org.apache.http.entity.ContentLengthStrategy;
52 import org.apache.http.impl.entity.LaxContentLengthStrategy;
53 import org.apache.http.impl.entity.StrictContentLengthStrategy;
54 import org.apache.http.impl.io.ChunkedInputStream;
55 import org.apache.http.impl.io.ChunkedOutputStream;
56 import org.apache.http.impl.io.ContentLengthInputStream;
57 import org.apache.http.impl.io.ContentLengthOutputStream;
58 import org.apache.http.impl.io.HttpTransportMetricsImpl;
59 import org.apache.http.impl.io.IdentityInputStream;
60 import org.apache.http.impl.io.IdentityOutputStream;
61 import org.apache.http.impl.io.SessionInputBufferImpl;
62 import org.apache.http.impl.io.SessionOutputBufferImpl;
63 import org.apache.http.io.SessionInputBuffer;
64 import org.apache.http.io.SessionOutputBuffer;
65 import org.apache.http.protocol.HTTP;
66 import org.apache.http.util.Args;
67 import org.apache.http.util.Asserts;
68 import org.apache.http.util.NetUtils;
69
70
71
72
73
74
75
76 @NotThreadSafe
77 public class BHttpConnectionBase implements HttpConnection, HttpInetConnection {
78
79 private final SessionInputBufferImpl inbuffer;
80 private final SessionOutputBufferImpl outbuffer;
81 private final HttpConnectionMetricsImpl connMetrics;
82 private final ContentLengthStrategy incomingContentStrategy;
83 private final ContentLengthStrategy outgoingContentStrategy;
84
85 private volatile boolean open;
86 private volatile Socket socket;
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104 protected BHttpConnectionBase(
105 final int buffersize,
106 final int fragmentSizeHint,
107 final CharsetDecoder chardecoder,
108 final CharsetEncoder charencoder,
109 final MessageConstraints constraints,
110 final ContentLengthStrategy incomingContentStrategy,
111 final ContentLengthStrategy outgoingContentStrategy) {
112 super();
113 Args.positive(buffersize, "Buffer size");
114 final HttpTransportMetricsImpl inTransportMetrics = new HttpTransportMetricsImpl();
115 final HttpTransportMetricsImpl outTransportMetrics = new HttpTransportMetricsImpl();
116 this.inbuffer = new SessionInputBufferImpl(inTransportMetrics, buffersize, -1,
117 constraints != null ? constraints : MessageConstraints.DEFAULT, chardecoder);
118 this.outbuffer = new SessionOutputBufferImpl(outTransportMetrics, buffersize, fragmentSizeHint,
119 charencoder);
120 this.connMetrics = new HttpConnectionMetricsImpl(inTransportMetrics, outTransportMetrics);
121 this.incomingContentStrategy = incomingContentStrategy != null ? incomingContentStrategy :
122 LaxContentLengthStrategy.INSTANCE;
123 this.outgoingContentStrategy = outgoingContentStrategy != null ? outgoingContentStrategy :
124 StrictContentLengthStrategy.INSTANCE;
125 }
126
127 protected void ensureOpen() throws IOException {
128 Asserts.check(this.open, "Connection is not open");
129 if (!this.inbuffer.isBound()) {
130 this.inbuffer.bind(getSocketInputStream(this.socket));
131 }
132 if (!this.outbuffer.isBound()) {
133 this.outbuffer.bind(getSocketOutputStream(this.socket));
134 }
135 }
136
137 protected InputStream getSocketInputStream(final Socket socket) throws IOException {
138 return socket.getInputStream();
139 }
140
141 protected OutputStream getSocketOutputStream(final Socket socket) throws IOException {
142 return socket.getOutputStream();
143 }
144
145
146
147
148
149
150
151
152
153
154
155 protected void bind(final Socket socket) throws IOException {
156 Args.notNull(socket, "Socket");
157 this.socket = socket;
158 this.open = true;
159 this.inbuffer.bind(null);
160 this.outbuffer.bind(null);
161 }
162
163 protected SessionInputBuffer getSessionInputBuffer() {
164 return this.inbuffer;
165 }
166
167 protected SessionOutputBuffer getSessionOutputBuffer() {
168 return this.outbuffer;
169 }
170
171 protected void doFlush() throws IOException {
172 this.outbuffer.flush();
173 }
174
175 public boolean isOpen() {
176 return this.open;
177 }
178
179 protected Socket getSocket() {
180 return this.socket;
181 }
182
183 protected OutputStream createOutputStream(
184 final long len,
185 final SessionOutputBuffer outbuffer) {
186 if (len == ContentLengthStrategy.CHUNKED) {
187 return new ChunkedOutputStream(2048, outbuffer);
188 } else if (len == ContentLengthStrategy.IDENTITY) {
189 return new IdentityOutputStream(outbuffer);
190 } else {
191 return new ContentLengthOutputStream(outbuffer, len);
192 }
193 }
194
195 protected OutputStream prepareOutput(final HttpMessage message) throws HttpException {
196 final long len = this.outgoingContentStrategy.determineLength(message);
197 return createOutputStream(len, this.outbuffer);
198 }
199
200 protected InputStream createInputStream(
201 final long len,
202 final SessionInputBuffer inbuffer) {
203 if (len == ContentLengthStrategy.CHUNKED) {
204 return new ChunkedInputStream(inbuffer);
205 } else if (len == ContentLengthStrategy.IDENTITY) {
206 return new IdentityInputStream(inbuffer);
207 } else {
208 return new ContentLengthInputStream(inbuffer, len);
209 }
210 }
211
212 protected HttpEntity prepareInput(final HttpMessage message) throws HttpException {
213 final BasicHttpEntity entity = new BasicHttpEntity();
214
215 final long len = this.incomingContentStrategy.determineLength(message);
216 final InputStream instream = createInputStream(len, this.inbuffer);
217 if (len == ContentLengthStrategy.CHUNKED) {
218 entity.setChunked(true);
219 entity.setContentLength(-1);
220 entity.setContent(instream);
221 } else if (len == ContentLengthStrategy.IDENTITY) {
222 entity.setChunked(false);
223 entity.setContentLength(-1);
224 entity.setContent(instream);
225 } else {
226 entity.setChunked(false);
227 entity.setContentLength(len);
228 entity.setContent(instream);
229 }
230
231 final Header contentTypeHeader = message.getFirstHeader(HTTP.CONTENT_TYPE);
232 if (contentTypeHeader != null) {
233 entity.setContentType(contentTypeHeader);
234 }
235 final Header contentEncodingHeader = message.getFirstHeader(HTTP.CONTENT_ENCODING);
236 if (contentEncodingHeader != null) {
237 entity.setContentEncoding(contentEncodingHeader);
238 }
239 return entity;
240 }
241
242 public InetAddress getLocalAddress() {
243 if (this.socket != null) {
244 return this.socket.getLocalAddress();
245 } else {
246 return null;
247 }
248 }
249
250 public int getLocalPort() {
251 if (this.socket != null) {
252 return this.socket.getLocalPort();
253 } else {
254 return -1;
255 }
256 }
257
258 public InetAddress getRemoteAddress() {
259 if (this.socket != null) {
260 return this.socket.getInetAddress();
261 } else {
262 return null;
263 }
264 }
265
266 public int getRemotePort() {
267 if (this.socket != null) {
268 return this.socket.getPort();
269 } else {
270 return -1;
271 }
272 }
273
274 public void setSocketTimeout(final int timeout) {
275 if (this.socket != null) {
276 try {
277 this.socket.setSoTimeout(timeout);
278 } catch (final SocketException ignore) {
279
280
281
282 }
283 }
284 }
285
286 public int getSocketTimeout() {
287 if (this.socket != null) {
288 try {
289 return this.socket.getSoTimeout();
290 } catch (final SocketException ignore) {
291 return -1;
292 }
293 } else {
294 return -1;
295 }
296 }
297
298 public void shutdown() throws IOException {
299 this.open = false;
300 final Socket tmpsocket = this.socket;
301 if (tmpsocket != null) {
302 tmpsocket.close();
303 }
304 }
305
306 public void close() throws IOException {
307 if (!this.open) {
308 return;
309 }
310 this.open = false;
311 final Socket sock = this.socket;
312 try {
313 this.outbuffer.flush();
314 try {
315 try {
316 sock.shutdownOutput();
317 } catch (final IOException ignore) {
318 }
319 try {
320 sock.shutdownInput();
321 } catch (final IOException ignore) {
322 }
323 } catch (final UnsupportedOperationException ignore) {
324
325 }
326 } finally {
327 sock.close();
328 }
329 }
330
331 private int fillInputBuffer(final int timeout) throws IOException {
332 final int oldtimeout = this.socket.getSoTimeout();
333 try {
334 this.socket.setSoTimeout(timeout);
335 return this.inbuffer.fillBuffer();
336 } finally {
337 this.socket.setSoTimeout(oldtimeout);
338 }
339 }
340
341 protected boolean awaitInput(final int timeout) throws IOException {
342 if (this.inbuffer.hasBufferedData()) {
343 return true;
344 }
345 fillInputBuffer(timeout);
346 return this.inbuffer.hasBufferedData();
347 }
348
349 public boolean isStale() {
350 if (!isOpen()) {
351 return true;
352 }
353 try {
354 final int bytesRead = fillInputBuffer(1);
355 return bytesRead < 0;
356 } catch (final SocketTimeoutException ex) {
357 return false;
358 } catch (final IOException ex) {
359 return true;
360 }
361 }
362
363 protected void incrementRequestCount() {
364 this.connMetrics.incrementRequestCount();
365 }
366
367 protected void incrementResponseCount() {
368 this.connMetrics.incrementResponseCount();
369 }
370
371 public HttpConnectionMetrics getMetrics() {
372 return this.connMetrics;
373 }
374
375 @Override
376 public String toString() {
377 if (this.socket != null) {
378 final StringBuilder buffer = new StringBuilder();
379 final SocketAddress remoteAddress = this.socket.getRemoteSocketAddress();
380 final SocketAddress localAddress = this.socket.getLocalSocketAddress();
381 if (remoteAddress != null && localAddress != null) {
382 NetUtils.formatAddress(buffer, localAddress);
383 buffer.append("<->");
384 NetUtils.formatAddress(buffer, remoteAddress);
385 }
386 return buffer.toString();
387 } else {
388 return "[Not bound]";
389 }
390 }
391
392 }