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.hc.core5.http.impl.io;
29
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.OutputStream;
33 import java.net.Socket;
34 import java.net.SocketAddress;
35 import java.net.SocketException;
36 import java.net.SocketTimeoutException;
37 import java.nio.charset.CharsetDecoder;
38 import java.nio.charset.CharsetEncoder;
39 import java.util.List;
40 import java.util.concurrent.atomic.AtomicReference;
41
42 import javax.net.ssl.SSLSession;
43 import javax.net.ssl.SSLSocket;
44
45 import org.apache.hc.core5.function.Supplier;
46 import org.apache.hc.core5.http.ConnectionClosedException;
47 import org.apache.hc.core5.http.ContentLengthStrategy;
48 import org.apache.hc.core5.http.EndpointDetails;
49 import org.apache.hc.core5.http.Header;
50 import org.apache.hc.core5.http.HttpEntity;
51 import org.apache.hc.core5.http.HttpHeaders;
52 import org.apache.hc.core5.http.HttpMessage;
53 import org.apache.hc.core5.http.ProtocolVersion;
54 import org.apache.hc.core5.http.config.Http1Config;
55 import org.apache.hc.core5.http.impl.BasicEndpointDetails;
56 import org.apache.hc.core5.http.impl.BasicHttpConnectionMetrics;
57 import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics;
58 import org.apache.hc.core5.http.io.BHttpConnection;
59 import org.apache.hc.core5.http.io.SessionInputBuffer;
60 import org.apache.hc.core5.http.io.SessionOutputBuffer;
61 import org.apache.hc.core5.http.io.entity.EmptyInputStream;
62 import org.apache.hc.core5.io.CloseMode;
63 import org.apache.hc.core5.io.Closer;
64 import org.apache.hc.core5.net.InetAddressUtils;
65 import org.apache.hc.core5.util.Args;
66 import org.apache.hc.core5.util.Timeout;
67
68 class BHttpConnectionBase implements BHttpConnection {
69
70 final Http1Config http1Config;
71 final SessionInputBufferImpl inBuffer;
72 final SessionOutputBufferImpl outbuffer;
73 final BasicHttpConnectionMetrics connMetrics;
74 final AtomicReference<SocketHolder> socketHolderRef;
75
76 private byte[] chunkedRequestBuffer;
77
78 volatile ProtocolVersion version;
79 volatile EndpointDetails endpointDetails;
80
81 BHttpConnectionBase(
82 final Http1Config http1Config,
83 final CharsetDecoder charDecoder,
84 final CharsetEncoder charEncoder) {
85 this.http1Config = http1Config != null ? http1Config : Http1Config.DEFAULT;
86 final BasicHttpTransportMetricstrics.html#BasicHttpTransportMetrics">BasicHttpTransportMetrics inTransportMetrics = new BasicHttpTransportMetrics();
87 final BasicHttpTransportMetricsrics.html#BasicHttpTransportMetrics">BasicHttpTransportMetrics outTransportMetrics = new BasicHttpTransportMetrics();
88 this.inBuffer = new SessionInputBufferImpl(inTransportMetrics,
89 this.http1Config.getBufferSize(), -1,
90 this.http1Config.getMaxLineLength(), charDecoder);
91 this.outbuffer = new SessionOutputBufferImpl(outTransportMetrics,
92 this.http1Config.getBufferSize(),
93 this.http1Config.getChunkSizeHint(), charEncoder);
94 this.connMetrics = new BasicHttpConnectionMetrics(inTransportMetrics, outTransportMetrics);
95 this.socketHolderRef = new AtomicReference<>();
96 }
97
98 protected SocketHolder ensureOpen() throws IOException {
99 final SocketHolder socketHolder = this.socketHolderRef.get();
100 if (socketHolder == null) {
101 throw new ConnectionClosedException();
102 }
103 return socketHolder;
104 }
105
106
107
108
109
110
111
112
113
114
115
116 protected void bind(final Socket socket) throws IOException {
117 Args.notNull(socket, "Socket");
118 bind(new SocketHolder(socket));
119 }
120
121 protected void bind(final SocketHolder socketHolder) throws IOException {
122 Args.notNull(socketHolder, "Socket holder");
123 this.socketHolderRef.set(socketHolder);
124 this.endpointDetails = null;
125 }
126
127 @Override
128 public boolean isOpen() {
129 return this.socketHolderRef.get() != null;
130 }
131
132
133
134
135 @Override
136 public ProtocolVersion getProtocolVersion() {
137 return this.version;
138 }
139
140 protected SocketHolder getSocketHolder() {
141 return this.socketHolderRef.get();
142 }
143
144 protected OutputStream createContentOutputStream(
145 final long len,
146 final SessionOutputBuffer buffer,
147 final OutputStream outputStream,
148 final Supplier<List<? extends Header>> trailers) {
149 if (len >= 0) {
150 return new ContentLengthOutputStream(buffer, outputStream, len);
151 } else if (len == ContentLengthStrategy.CHUNKED) {
152 return new ChunkedOutputStream(buffer, outputStream, getChunkedRequestBuffer(), trailers);
153 } else {
154 return new IdentityOutputStream(buffer, outputStream);
155 }
156 }
157
158 private byte[] getChunkedRequestBuffer() {
159 if (chunkedRequestBuffer == null) {
160 final int chunkSizeHint = this.http1Config.getChunkSizeHint();
161 chunkedRequestBuffer = new byte[chunkSizeHint > 0 ? chunkSizeHint : 8192];
162 }
163 return chunkedRequestBuffer;
164 }
165
166 protected InputStream createContentInputStream(
167 final long len,
168 final SessionInputBuffer buffer,
169 final InputStream inputStream) {
170 if (len > 0) {
171 return new ContentLengthInputStream(buffer, inputStream, len);
172 } else if (len == 0) {
173 return EmptyInputStream.INSTANCE;
174 } else if (len == ContentLengthStrategy.CHUNKED) {
175 return new ChunkedInputStream(buffer, inputStream, this.http1Config);
176 } else {
177 return new IdentityInputStream(buffer, inputStream);
178 }
179 }
180
181 HttpEntity createIncomingEntity(
182 final HttpMessage message,
183 final SessionInputBuffer inBuffer,
184 final InputStream inputStream,
185 final long len) {
186 return new IncomingHttpEntity(
187 createContentInputStream(len, inBuffer, inputStream),
188 len >= 0 ? len : -1, len == ContentLengthStrategy.CHUNKED,
189 message.getFirstHeader(HttpHeaders.CONTENT_TYPE),
190 message.getFirstHeader(HttpHeaders.CONTENT_ENCODING));
191 }
192
193 @Override
194 public SocketAddress getRemoteAddress() {
195 final SocketHolder socketHolder = this.socketHolderRef.get();
196 return socketHolder != null ? socketHolder.getSocket().getRemoteSocketAddress() : null;
197 }
198
199 @Override
200 public SocketAddress getLocalAddress() {
201 final SocketHolder socketHolder = this.socketHolderRef.get();
202 return socketHolder != null ? socketHolder.getSocket().getLocalSocketAddress() : null;
203 }
204
205 @Override
206 public void setSocketTimeout(final Timeout timeout) {
207 final SocketHolder socketHolder = this.socketHolderRef.get();
208 if (socketHolder != null) {
209 try {
210 socketHolder.getSocket().setSoTimeout(Timeout.defaultsToDisabled(timeout).toMillisecondsIntBound());
211 } catch (final SocketException ignore) {
212
213
214
215 }
216 }
217 }
218
219 @Override
220 public Timeout getSocketTimeout() {
221 final SocketHolder socketHolder = this.socketHolderRef.get();
222 if (socketHolder != null) {
223 try {
224 return Timeout.ofMilliseconds(socketHolder.getSocket().getSoTimeout());
225 } catch (final SocketException ignore) {
226 }
227 }
228 return Timeout.DISABLED;
229 }
230
231 @Override
232 public void close(final CloseMode closeMode) {
233 final SocketHolder socketHolder = this.socketHolderRef.getAndSet(null);
234 if (socketHolder != null) {
235 final Socket socket = socketHolder.getSocket();
236 try {
237 if (closeMode == CloseMode.IMMEDIATE) {
238
239 socket.setSoLinger(true, 0);
240 }
241 } catch (final IOException ignore) {
242 } finally {
243 Closer.closeQuietly(socket);
244 }
245 }
246 }
247
248 @Override
249 public void close() throws IOException {
250 final SocketHolder socketHolder = this.socketHolderRef.getAndSet(null);
251 if (socketHolder != null) {
252 try (final Socket socket = socketHolder.getSocket()) {
253 this.inBuffer.clear();
254 this.outbuffer.flush(socketHolder.getOutputStream());
255 }
256 }
257 }
258
259 private int fillInputBuffer(final Timeout timeout) throws IOException {
260 final SocketHolder socketHolder = ensureOpen();
261 final Socket socket = socketHolder.getSocket();
262 final int oldtimeout = socket.getSoTimeout();
263 try {
264 socket.setSoTimeout(timeout.toMillisecondsIntBound());
265 return this.inBuffer.fillBuffer(socketHolder.getInputStream());
266 } finally {
267 socket.setSoTimeout(oldtimeout);
268 }
269 }
270
271 protected boolean awaitInput(final Timeout timeout) throws IOException {
272 if (this.inBuffer.hasBufferedData()) {
273 return true;
274 }
275 fillInputBuffer(timeout);
276 return this.inBuffer.hasBufferedData();
277 }
278
279 @Override
280 public boolean isDataAvailable(final Timeout timeout) throws IOException {
281 ensureOpen();
282 try {
283 return awaitInput(timeout);
284 } catch (final SocketTimeoutException ex) {
285 return false;
286 }
287 }
288
289 @Override
290 public boolean isStale() throws IOException {
291 if (!isOpen()) {
292 return true;
293 }
294 try {
295 final int bytesRead = fillInputBuffer(Timeout.ofMilliseconds(1));
296 return bytesRead < 0;
297 } catch (final SocketTimeoutException ex) {
298 return false;
299 } catch (final SocketException ex) {
300 return true;
301 }
302 }
303
304 @Override
305 public void flush() throws IOException {
306 final SocketHolder socketHolder = ensureOpen();
307 this.outbuffer.flush(socketHolder.getOutputStream());
308 }
309
310 protected void incrementRequestCount() {
311 this.connMetrics.incrementRequestCount();
312 }
313
314 protected void incrementResponseCount() {
315 this.connMetrics.incrementResponseCount();
316 }
317
318 @Override
319 public SSLSession getSSLSession() {
320 final SocketHolder socketHolder = this.socketHolderRef.get();
321 if (socketHolder != null) {
322 final Socket socket = socketHolder.getSocket();
323 return socket instanceof SSLSocket ? ((SSLSocket) socket).getSession() : null;
324 }
325 return null;
326 }
327
328 @Override
329 public EndpointDetails getEndpointDetails() {
330 if (endpointDetails == null) {
331 final SocketHolder socketHolder = this.socketHolderRef.get();
332 if (socketHolder != null) {
333 @SuppressWarnings("resource")
334 final Socket socket = socketHolder.getSocket();
335 Timeout socketTimeout;
336 try {
337 socketTimeout = Timeout.ofMilliseconds(socket.getSoTimeout());
338 } catch (final SocketException e) {
339 socketTimeout = Timeout.DISABLED;
340 }
341 endpointDetails = new BasicEndpointDetails(
342 socket.getRemoteSocketAddress(),
343 socket.getLocalSocketAddress(),
344 this.connMetrics,
345 socketTimeout);
346 }
347 }
348 return endpointDetails;
349 }
350
351 @Override
352 public String toString() {
353 final SocketHolder socketHolder = this.socketHolderRef.get();
354 if (socketHolder != null) {
355 final Socket socket = socketHolder.getSocket();
356 final StringBuilder buffer = new StringBuilder();
357 final SocketAddress remoteAddress = socket.getRemoteSocketAddress();
358 final SocketAddress localAddress = socket.getLocalSocketAddress();
359 if (remoteAddress != null && localAddress != null) {
360 InetAddressUtils.formatAddress(buffer, localAddress);
361 buffer.append("<->");
362 InetAddressUtils.formatAddress(buffer, remoteAddress);
363 }
364 return buffer.toString();
365 }
366 return "[Not bound]";
367 }
368
369 }