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