HTTPCLIENT-2328: Blocking i/o connections to check if the opposite TLS endpoint has been closed by the opposite endpoint while writing out request body

This commit is contained in:
Oleg Kalnichevski 2024-06-22 17:13:27 +02:00
parent 10e8a7acbc
commit ee0a102104
6 changed files with 43 additions and 6 deletions

View File

@ -235,8 +235,8 @@ public class DefaultHttpClientConnectionOperator implements HttpClientConnection
if (LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) {
LOG.debug("{} {} upgrading to TLS", ConnPoolSupport.getId(conn), tlsName); LOG.debug("{} {} upgrading to TLS", ConnPoolSupport.getId(conn), tlsName);
} }
final Socket upgradedSocket = tlsSocketStrategy.upgrade(socket, tlsName.getHostName(), tlsName.getPort(), attachment, context); final SSLSocket sslSocket = tlsSocketStrategy.upgrade(socket, tlsName.getHostName(), tlsName.getPort(), attachment, context);
conn.bind(upgradedSocket); conn.bind(sslSocket, socket);
onAfterTlsHandshake(context, endpointHost); onAfterTlsHandshake(context, endpointHost);
if (LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) {
LOG.debug("{} {} upgraded to TLS", ConnPoolSupport.getId(conn), tlsName); LOG.debug("{} {} upgraded to TLS", ConnPoolSupport.getId(conn), tlsName);

View File

@ -184,6 +184,14 @@ final class DefaultManagedHttpClientConnection
socketTimeout = Timeout.ofMilliseconds(socket.getSoTimeout()); socketTimeout = Timeout.ofMilliseconds(socket.getSoTimeout());
} }
@Override
public void bind(final SSLSocket sslSocket, final Socket socket) throws IOException {
super.bind(WIRE_LOG.isDebugEnabled() ?
new LoggingSocketHolder(sslSocket, socket, this.id, WIRE_LOG) :
new SocketHolder(sslSocket, socket));
socketTimeout = Timeout.ofMilliseconds(sslSocket.getSoTimeout());
}
@Override @Override
protected void onResponseReceived(final ClassicHttpResponse response) { protected void onResponseReceived(final ClassicHttpResponse response) {
if (response != null && HEADER_LOG.isDebugEnabled()) { if (response != null && HEADER_LOG.isDebugEnabled()) {

View File

@ -32,6 +32,8 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.Socket; import java.net.Socket;
import javax.net.ssl.SSLSocket;
import org.apache.hc.client5.http.impl.Wire; import org.apache.hc.client5.http.impl.Wire;
import org.apache.hc.core5.http.impl.io.SocketHolder; import org.apache.hc.core5.http.impl.io.SocketHolder;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -45,6 +47,11 @@ class LoggingSocketHolder extends SocketHolder {
this.wire = new Wire(log, id); this.wire = new Wire(log, id);
} }
LoggingSocketHolder(final SSLSocket sslSocket, final Socket baseSocket, final String id, final Logger log) {
super(sslSocket, baseSocket);
this.wire = new Wire(log, id);
}
@Override @Override
protected InputStream getInputStream(final Socket socket) throws IOException { protected InputStream getInputStream(final Socket socket) throws IOException {
return new LoggingInputStream(super.getInputStream(socket), wire); return new LoggingInputStream(super.getInputStream(socket), wire);

View File

@ -31,6 +31,7 @@ import java.io.IOException;
import java.net.Socket; import java.net.Socket;
import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.Internal;
import org.apache.hc.core5.http.io.HttpClientConnection; import org.apache.hc.core5.http.io.HttpClientConnection;
@ -55,6 +56,21 @@ public interface ManagedHttpClientConnection extends HttpClientConnection {
*/ */
void bind(Socket socket) throws IOException; void bind(Socket socket) throws IOException;
/**
* Binds this connection to the SSL given socket and the underlying network
* socket. The connection is considered open if it is bound, the underlying
* network socket is connection to a remote host and the SSL socket is
* fully initialized (TLS handshake has been successfully executed).
*
* @param sslSocket the SSL socket to bind the connection to.
* @param socket the underlying network socket of the SSL socket.
*
* @since 5.4
*/
default void bind(SSLSocket sslSocket, Socket socket) throws IOException {
bind(sslSocket);
}
/** /**
* Returns the underlying socket. * Returns the underlying socket.
*/ */

View File

@ -62,6 +62,7 @@ import org.apache.hc.core5.http.ssl.TlsCiphers;
import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.apache.hc.core5.http2.ssl.ApplicationProtocol; import org.apache.hc.core5.http2.ssl.ApplicationProtocol;
import org.apache.hc.core5.http2.ssl.H2TlsSupport; import org.apache.hc.core5.http2.ssl.H2TlsSupport;
import org.apache.hc.core5.io.Closer;
import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.net.NamedEndpoint;
import org.apache.hc.core5.reactor.ssl.SSLBufferMode; import org.apache.hc.core5.reactor.ssl.SSLBufferMode;
import org.apache.hc.core5.reactor.ssl.TlsDetails; import org.apache.hc.core5.reactor.ssl.TlsDetails;
@ -204,9 +205,14 @@ abstract class AbstractClientTlsStrategy implements TlsStrategy, TlsSocketStrate
socket, socket,
target, target,
port, port,
true); false);
try {
executeHandshake(upgradedSocket, target, attachment); executeHandshake(upgradedSocket, target, attachment);
return upgradedSocket; return upgradedSocket;
} catch (IOException | RuntimeException ex) {
Closer.closeQuietly(upgradedSocket);
throw ex;
}
} }
private void executeHandshake( private void executeHandshake(

View File

@ -147,7 +147,7 @@ public class TestHttpClientConnectionOperator {
Mockito.verify(socket).connect(new InetSocketAddress(ip1, 443), 123); Mockito.verify(socket).connect(new InetSocketAddress(ip1, 443), 123);
Mockito.verify(conn, Mockito.times(2)).bind(socket); Mockito.verify(conn, Mockito.times(2)).bind(socket);
Mockito.verify(tlsSocketStrategy).upgrade(socket, "somehost", -1, tlsConfig, context); Mockito.verify(tlsSocketStrategy).upgrade(socket, "somehost", -1, tlsConfig, context);
Mockito.verify(conn, Mockito.times(1)).bind(upgradedSocket); Mockito.verify(conn, Mockito.times(1)).bind(upgradedSocket, socket);
} }
@Test @Test