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 }
246 }
247 }
248
249 private int fillInputBuffer(final Timeout timeout) throws IOException {
250 final SocketHolder socketHolder = ensureOpen();
251 final Socket socket = socketHolder.getSocket();
252 final int oldtimeout = socket.getSoTimeout();
253 try {
254 socket.setSoTimeout(timeout.toMillisecondsIntBound());
255 return this.inBuffer.fillBuffer(socketHolder.getInputStream());
256 } finally {
257 socket.setSoTimeout(oldtimeout);
258 }
259 }
260
261 protected boolean awaitInput(final Timeout timeout) throws IOException {
262 if (this.inBuffer.hasBufferedData()) {
263 return true;
264 }
265 fillInputBuffer(timeout);
266 return this.inBuffer.hasBufferedData();
267 }
268
269 @Override
270 public boolean isDataAvailable(final Timeout timeout) throws IOException {
271 ensureOpen();
272 try {
273 return awaitInput(timeout);
274 } catch (final SocketTimeoutException ex) {
275 return false;
276 }
277 }
278
279 @Override
280 public boolean isStale() throws IOException {
281 if (!isOpen()) {
282 return true;
283 }
284 try {
285 final int bytesRead = fillInputBuffer(Timeout.ofMilliseconds(1));
286 return bytesRead < 0;
287 } catch (final SocketTimeoutException ex) {
288 return false;
289 } catch (final SocketException ex) {
290 return true;
291 }
292 }
293
294 @Override
295 public void flush() throws IOException {
296 final SocketHolder socketHolder = ensureOpen();
297 this.outbuffer.flush(socketHolder.getOutputStream());
298 }
299
300 protected void incrementRequestCount() {
301 this.connMetrics.incrementRequestCount();
302 }
303
304 protected void incrementResponseCount() {
305 this.connMetrics.incrementResponseCount();
306 }
307
308 @Override
309 public SSLSession getSSLSession() {
310 final SocketHolder socketHolder = this.socketHolderRef.get();
311 if (socketHolder != null) {
312 final Socket socket = socketHolder.getSocket();
313 return socket instanceof SSLSocket ? ((SSLSocket) socket).getSession() : null;
314 }
315 return null;
316 }
317
318 @Override
319 public EndpointDetails getEndpointDetails() {
320 if (endpointDetails == null) {
321 final SocketHolder socketHolder = this.socketHolderRef.get();
322 if (socketHolder != null) {
323 @SuppressWarnings("resource")
324 final Socket socket = socketHolder.getSocket();
325 Timeout socketTimeout;
326 try {
327 socketTimeout = Timeout.ofMilliseconds(socket.getSoTimeout());
328 } catch (final SocketException e) {
329 socketTimeout = Timeout.DISABLED;
330 }
331 endpointDetails = new BasicEndpointDetails(
332 socket.getRemoteSocketAddress(),
333 socket.getLocalSocketAddress(),
334 this.connMetrics,
335 socketTimeout);
336 }
337 }
338 return endpointDetails;
339 }
340
341 @Override
342 public String toString() {
343 final SocketHolder socketHolder = this.socketHolderRef.get();
344 if (socketHolder != null) {
345 final Socket socket = socketHolder.getSocket();
346 final StringBuilder buffer = new StringBuilder();
347 final SocketAddress remoteAddress = socket.getRemoteSocketAddress();
348 final SocketAddress localAddress = socket.getLocalSocketAddress();
349 if (remoteAddress != null && localAddress != null) {
350 InetAddressUtils.formatAddress(buffer, localAddress);
351 buffer.append("<->");
352 InetAddressUtils.formatAddress(buffer, remoteAddress);
353 }
354 return buffer.toString();
355 }
356 return "[Not bound]";
357 }
358
359 }