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