From ef7e8b687d95ece0f90731c8c2fe1a705ae36dae Mon Sep 17 00:00:00 2001 From: Arturo Bernal Date: Thu, 8 May 2025 15:20:19 +0200 Subject: [PATCH] =?UTF-8?q?HTTPCLIENT-2368:=20Allow=20multiple=20TLS=20han?= =?UTF-8?q?dshakes=20in=20InternalDataChannel.=20This=20patch=20ensures=20?= =?UTF-8?q?that=20any=20existing=20SSLIOSession=20is=20cleared=20and=20the?= =?UTF-8?q?=20channel=20reverted=20to=20raw=20I/O=20before=20installing=20?= =?UTF-8?q?a=20new=20one=E2=80=94allowing=20startTls(...)=20to=20be=20invo?= =?UTF-8?q?ked=20twice=20(proxy=20=E2=86=92=20CONNECT=20=E2=86=92=20target?= =?UTF-8?q?)=20on=20the=20same=20connection=20without=20error.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core5/testing/nio/TLSIntegrationTest.java | 48 +++++++++++++++++++ .../hc/core5/reactor/InternalDataChannel.java | 20 ++++++-- 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/TLSIntegrationTest.java b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/TLSIntegrationTest.java index e84581832..e38f4f7c2 100644 --- a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/TLSIntegrationTest.java +++ b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/TLSIntegrationTest.java @@ -451,4 +451,52 @@ public boolean upgrade( } + @Test + void testHttpsProxyWithConnectAndSecondTls() throws Exception { + final TlsStrategy targetTlsStrategy = new BasicServerTlsStrategy( + SSLTestContexts.createServerSSLContext()); + server = createServer(targetTlsStrategy); + server.start(); + final ListenerEndpoint targetListener = server + .listen(new InetSocketAddress("localhost", 0), URIScheme.HTTPS) + .get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); + final InetSocketAddress targetAddress = (InetSocketAddress) targetListener.getAddress(); + + final HttpAsyncServer proxyServer = createServer( + new BasicServerTlsStrategy(SSLTestContexts.createServerSSLContext())); + proxyServer.start(); + final ListenerEndpoint proxyListener = proxyServer + .listen(new InetSocketAddress("localhost", 0), URIScheme.HTTPS) + .get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); + final InetSocketAddress proxyAddress = (InetSocketAddress) proxyListener.getAddress(); + + final TlsStrategy clientTlsStrategy = new BasicClientTlsStrategy( + SSLTestContexts.createClientSSLContext()); + client = createClient(clientTlsStrategy); + client.start(); + + final HttpHost proxy = new HttpHost("https", "localhost", proxyAddress.getPort()); + final HttpHost target = new HttpHost("https", "localhost", targetAddress.getPort()); + + final Future> future = client.execute( + proxy, + new BasicRequestProducer( + Method.POST, + target, + "/test", + new StringAsyncEntityProducer("ping", ContentType.TEXT_PLAIN) + ), + new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), + TIMEOUT, null + ); + + final Message response = future + .get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); + Assertions.assertNotNull(response); + Assertions.assertEquals(200, response.getHead().getCode()); + Assertions.assertEquals("ping", response.getBody()); + + proxyServer.close(CloseMode.IMMEDIATE); + } + } diff --git a/httpcore5/src/main/java/org/apache/hc/core5/reactor/InternalDataChannel.java b/httpcore5/src/main/java/org/apache/hc/core5/reactor/InternalDataChannel.java index d290939b4..f2e244fb1 100644 --- a/httpcore5/src/main/java/org/apache/hc/core5/reactor/InternalDataChannel.java +++ b/httpcore5/src/main/java/org/apache/hc/core5/reactor/InternalDataChannel.java @@ -226,6 +226,19 @@ public void startTls( final SSLSessionVerifier verifier, final Timeout handshakeTimeout, final FutureCallback callback) { + + // If we already performed TLS (e.g. to the HTTPS proxy), clear out the old session + if (tlsSessionRef.get() != null) { + // Drop the previous SSLIOSession so we can start fresh + tlsSessionRef.set(null); + // Revert to raw TCP I/O before installing the new TLS layer + currentSessionRef.set( + ioSessionDecorator != null + ? ioSessionDecorator.decorate(ioSession) + : ioSession + ); + } + final SSLIOSession sslioSession = new SSLIOSession( endpoint != null ? endpoint : initialEndpoint, ioSession, @@ -247,11 +260,8 @@ public void completed(final SSLSession sslSession) { } }); - if (tlsSessionRef.compareAndSet(null, sslioSession)) { - currentSessionRef.set(ioSessionDecorator != null ? ioSessionDecorator.decorate(sslioSession) : sslioSession); - } else { - throw new IllegalStateException("TLS already activated"); - } + tlsSessionRef.set(sslioSession); + currentSessionRef.set(ioSessionDecorator != null ? ioSessionDecorator.decorate(sslioSession) : sslioSession); try { if (sessionListener != null) { sessionListener.startTls(sslioSession);